1 /* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #ifndef V8Proxy_h 32 #define V8Proxy_h 33 34 #include "PlatformBridge.h" 35 #include "ScriptSourceCode.h" // for WebCore::ScriptSourceCode 36 #include "SecurityOrigin.h" // for WebCore::SecurityOrigin 37 #include "SharedPersistent.h" 38 #include "V8AbstractEventListener.h" 39 #include "V8DOMWindowShell.h" 40 #include "V8DOMWrapper.h" 41 #include "V8GCController.h" 42 #include "V8Index.h" 43 #include <v8.h> 44 #include <wtf/PassRefPtr.h> // so generated bindings don't have to 45 #include <wtf/Vector.h> 46 47 #if defined(ENABLE_DOM_STATS_COUNTERS) && PLATFORM(CHROMIUM) 48 #define INC_STATS(name) PlatformBridge::incrementStatsCounter(name) 49 #else 50 #define INC_STATS(name) 51 #endif 52 53 namespace WebCore { 54 55 class DOMWindow; 56 class Frame; 57 class Node; 58 class SVGElement; 59 class ScriptExecutionContext; 60 class String; 61 class V8EventListener; 62 class V8IsolatedContext; 63 class WorldContextHandle; 64 65 // FIXME: use standard logging facilities in WebCore. 66 void logInfo(Frame*, const String& message, const String& url); 67 68 // The following Batch structs and methods are used for setting multiple 69 // properties on an ObjectTemplate, used from the generated bindings 70 // initialization (ConfigureXXXTemplate). This greatly reduces the binary 71 // size by moving from code driven setup to data table driven setup. 72 73 // BatchedAttribute translates into calls to SetAccessor() on either the 74 // instance or the prototype ObjectTemplate, based on |onProto|. 75 struct BatchedAttribute { 76 const char* const name; 77 v8::AccessorGetter getter; 78 v8::AccessorSetter setter; 79 V8ClassIndex::V8WrapperType data; 80 v8::AccessControl settings; 81 v8::PropertyAttribute attribute; 82 bool onProto; 83 }; 84 85 void batchConfigureAttributes(v8::Handle<v8::ObjectTemplate>, v8::Handle<v8::ObjectTemplate>, const BatchedAttribute*, size_t attributeCount); 86 configureAttribute(v8::Handle<v8::ObjectTemplate> instance,v8::Handle<v8::ObjectTemplate> proto,const BatchedAttribute & attribute)87 inline void configureAttribute(v8::Handle<v8::ObjectTemplate> instance, v8::Handle<v8::ObjectTemplate> proto, const BatchedAttribute& attribute) 88 { 89 (attribute.onProto ? proto : instance)->SetAccessor(v8::String::New(attribute.name), 90 attribute.getter, 91 attribute.setter, 92 attribute.data == V8ClassIndex::INVALID_CLASS_INDEX ? v8::Handle<v8::Value>() : v8::Integer::New(V8ClassIndex::ToInt(attribute.data)), 93 attribute.settings, 94 attribute.attribute); 95 } 96 97 // BatchedConstant translates into calls to Set() for setting up an object's 98 // constants. It sets the constant on both the FunctionTemplate and the 99 // ObjectTemplate. PropertyAttributes is always ReadOnly. 100 struct BatchedConstant { 101 const char* const name; 102 int value; 103 }; 104 105 void batchConfigureConstants(v8::Handle<v8::FunctionTemplate>, v8::Handle<v8::ObjectTemplate>, const BatchedConstant*, size_t constantCount); 106 107 struct BatchedCallback { 108 const char* const name; 109 v8::InvocationCallback callback; 110 }; 111 112 void batchConfigureCallbacks(v8::Handle<v8::ObjectTemplate>, 113 v8::Handle<v8::Signature>, 114 v8::PropertyAttribute, 115 const BatchedCallback*, 116 size_t callbackCount); 117 118 const int kMaxRecursionDepth = 20; 119 120 // Information about an extension that is registered for use with V8. If 121 // scheme is non-empty, it contains the URL scheme the extension should be 122 // used with. If group is non-zero, the extension will only be loaded into 123 // script contexts that belong to that group. Otherwise, the extension is 124 // used with all schemes and contexts. 125 struct V8ExtensionInfo { 126 String scheme; 127 int group; 128 v8::Extension* extension; 129 }; 130 typedef WTF::Vector<V8ExtensionInfo> V8Extensions; 131 132 class V8Proxy { 133 public: 134 // The types of javascript errors that can be thrown. 135 enum ErrorType { 136 RangeError, 137 ReferenceError, 138 SyntaxError, 139 TypeError, 140 GeneralError 141 }; 142 143 // When to report errors. 144 enum DelayReporting { 145 ReportLater, 146 ReportNow 147 }; 148 149 explicit V8Proxy(Frame*); 150 151 ~V8Proxy(); 152 frame()153 Frame* frame() { return m_frame; } 154 155 void clearForNavigation(); 156 void clearForClose(); 157 158 // FIXME: Need comment. User Gesture related. inlineCode()159 bool inlineCode() const { return m_inlineCode; } setInlineCode(bool value)160 void setInlineCode(bool value) { m_inlineCode = value; } 161 timerCallback()162 bool timerCallback() const { return m_timerCallback; } setTimerCallback(bool value)163 void setTimerCallback(bool value) { m_timerCallback = value; } 164 165 // Disconnects the proxy from its owner frame, 166 // and clears all timeouts on the DOM window. 167 void disconnectFrame(); 168 169 #if ENABLE(SVG) 170 static void setSVGContext(void*, SVGElement*); 171 static SVGElement* svgContext(void*); 172 173 // These helper functions are required in case we are given a PassRefPtr 174 // to a (possibly) newly created object and must prevent its reference 175 // count from dropping to zero as would happen in code like 176 // 177 // V8Proxy::setSVGContext(imp->getNewlyCreatedObject().get(), context); 178 // foo(imp->getNewlyCreatedObject().get()); 179 // 180 // In the above two lines each time getNewlyCreatedObject() is called it 181 // creates a new object because we don't ref() it. (So our attemts to 182 // associate a context with it fail.) Such code should be rewritten to 183 // 184 // foo(V8Proxy::withSVGContext(imp->getNewlyCreatedObject(), context).get()); 185 // 186 // where PassRefPtr::~PassRefPtr() is invoked only after foo() is 187 // called. 188 template <typename T> withSVGContext(PassRefPtr<T> object,SVGElement * context)189 static PassRefPtr<T> withSVGContext(PassRefPtr<T> object, SVGElement* context) 190 { 191 setSVGContext(object.get(), context); 192 return object; 193 } 194 195 template <typename T> withSVGContext(T * object,SVGElement * context)196 static T* withSVGContext(T* object, SVGElement* context) 197 { 198 setSVGContext(object, context); 199 return object; 200 } 201 #endif 202 setEventHandlerLineNumber(int lineNumber)203 void setEventHandlerLineNumber(int lineNumber) { m_handlerLineNumber = lineNumber; } finishedWithEvent(Event *)204 void finishedWithEvent(Event*) { } 205 206 // Evaluate JavaScript in a new isolated world. The script gets its own 207 // global scope, its own prototypes for intrinsic JavaScript objects (String, 208 // Array, and so-on), and its own wrappers for all DOM nodes and DOM 209 // constructors. 210 void evaluateInIsolatedWorld(int worldId, const Vector<ScriptSourceCode>& sources, int extensionGroup); 211 212 // Evaluate a script file in the current execution environment. 213 // The caller must hold an execution context. 214 // If cannot evalute the script, it returns an error. 215 v8::Local<v8::Value> evaluate(const ScriptSourceCode&, Node*); 216 217 // Run an already compiled script. 218 v8::Local<v8::Value> runScript(v8::Handle<v8::Script>, bool isInlineCode); 219 220 #ifdef ANDROID_INSTRUMENT 221 v8::Local<v8::Value> runScriptInternal(v8::Handle<v8::Script> script, bool inline_code); 222 #endif 223 224 // Call the function with the given receiver and arguments. 225 v8::Local<v8::Value> callFunction(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int argc, v8::Handle<v8::Value> argv[]); 226 227 // Call the function as constructor with the given arguments. 228 v8::Local<v8::Value> newInstance(v8::Handle<v8::Function>, int argc, v8::Handle<v8::Value> argv[]); 229 230 // Returns the window object associated with a context. 231 static DOMWindow* retrieveWindow(v8::Handle<v8::Context>); 232 // Returns V8Proxy object of the currently executing context. 233 static V8Proxy* retrieve(); 234 // Returns V8Proxy object associated with a frame. 235 static V8Proxy* retrieve(Frame*); 236 // Returns V8Proxy object associated with a script execution context. 237 static V8Proxy* retrieve(ScriptExecutionContext*); 238 239 // Returns the frame object of the window object associated with 240 // a context. 241 static Frame* retrieveFrame(v8::Handle<v8::Context>); 242 243 244 // The three functions below retrieve WebFrame instances relating the 245 // currently executing JavaScript. Since JavaScript can make function calls 246 // across frames, though, we need to be more precise. 247 // 248 // For example, imagine that a JS function in frame A calls a function in 249 // frame B, which calls native code, which wants to know what the 'active' 250 // frame is. 251 // 252 // The 'entered context' is the context where execution first entered the 253 // script engine; the context that is at the bottom of the JS function stack. 254 // RetrieveFrameForEnteredContext() would return Frame A in our example. 255 // This frame is often referred to as the "dynamic global object." 256 // 257 // The 'current context' is the context the JS engine is currently inside of; 258 // the context that is at the top of the JS function stack. 259 // RetrieveFrameForCurrentContext() would return Frame B in our example. 260 // This frame is often referred to as the "lexical global object." 261 // 262 // Finally, the 'calling context' is the context one below the current 263 // context on the JS function stack. For example, if function f calls 264 // function g, then the calling context will be the context associated with 265 // f. This context is commonly used by DOM security checks because they want 266 // to know who called them. 267 // 268 // If you are unsure which of these functions to use, ask abarth. 269 // 270 // NOTE: These cannot be declared as inline function, because VS complains at 271 // linking time. 272 static Frame* retrieveFrameForEnteredContext(); 273 static Frame* retrieveFrameForCurrentContext(); 274 static Frame* retrieveFrameForCallingContext(); 275 276 // Returns V8 Context of a frame. If none exists, creates 277 // a new context. It is potentially slow and consumes memory. 278 static v8::Local<v8::Context> context(Frame*); 279 static v8::Local<v8::Context> mainWorldContext(Frame*); 280 static v8::Local<v8::Context> currentContext(); 281 282 // If the current context causes out of memory, JavaScript setting 283 // is disabled and it returns true. 284 static bool handleOutOfMemory(); 285 286 static v8::Handle<v8::Value> checkNewLegal(const v8::Arguments&); 287 288 static v8::Handle<v8::Script> compileScript(v8::Handle<v8::String> code, const String& fileName, int baseLine); 289 290 #ifdef ANDROID_INSTRUMENT 291 static v8::Handle<v8::Script> compileScriptInternal(v8::Handle<v8::String> code, const String& fileName, int baseLine); 292 #endif 293 294 // If the exception code is different from zero, a DOM exception is 295 // schedule to be thrown. 296 static void setDOMException(int exceptionCode); 297 298 // Schedule an error object to be thrown. 299 static v8::Handle<v8::Value> throwError(ErrorType, const char* message); 300 301 // Create an instance of a function descriptor and set to the global object 302 // as a named property. Used by v8_test_shell. 303 static void bindJsObjectToWindow(Frame*, const char* name, int type, v8::Handle<v8::FunctionTemplate>, void*); 304 305 template <int tag, typename T> 306 static v8::Handle<v8::Value> constructDOMObject(const v8::Arguments&); 307 308 // Process any pending JavaScript console messages. 309 static void processConsoleMessages(); 310 311 // Function for retrieving the line number and source name for the top 312 // JavaScript stack frame. 313 // 314 // It will return true if the line number was successfully retrieved and written 315 // into the |result| parameter, otherwise the function will return false. It may 316 // fail due to a stck overflow in the underlying JavaScript implentation, handling 317 // of such exception is up to the caller. 318 static bool sourceLineNumber(int& result); 319 static bool sourceName(String& result); 320 321 v8::Local<v8::Context> context(); 322 v8::Local<v8::Context> mainWorldContext(); 323 324 // FIXME: This should eventually take DOMWrapperWorld argument! windowShell()325 V8DOMWindowShell* windowShell() const { return m_windowShell.get(); } 326 327 bool setContextDebugId(int id); 328 static int contextDebugId(v8::Handle<v8::Context>); 329 330 // Registers a v8 extension to be available on webpages. The two forms 331 // offer various restrictions on what types of contexts the extension is 332 // loaded into. If a scheme is provided, only pages whose URL has the given 333 // scheme will match. If extensionGroup is provided, the extension will 334 // only be loaded into scripts run via evaluateInNewWorld with the 335 // matching group. Will only affect v8 contexts initialized after this 336 // call. Takes ownership of the v8::Extension object passed. 337 static void registerExtension(v8::Extension*, const String& schemeRestriction); 338 static void registerExtension(v8::Extension*, int extensionGroup); 339 340 static void registerExtensionWithV8(v8::Extension*); 341 static bool registeredExtensionWithV8(v8::Extension*); 342 extensions()343 static const V8Extensions& extensions() { return m_extensions; } 344 345 // Report an unsafe attempt to access the given frame on the console. 346 static void reportUnsafeAccessTo(Frame* target, DelayReporting delay); 347 348 private: 349 // If m_recursionCount is 0, let LocalStorage know so we can release 350 // the storage mutex. 351 void releaseStorageMutex(); 352 353 void resetIsolatedWorlds(); 354 355 // Returns false when we're out of memory in V8. 356 bool setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContext); 357 358 static const char* rangeExceptionName(int exceptionCode); 359 static const char* eventExceptionName(int exceptionCode); 360 static const char* xmlHttpRequestExceptionName(int exceptionCode); 361 static const char* domExceptionName(int exceptionCode); 362 363 #if ENABLE(XPATH) 364 static const char* xpathExceptionName(int exceptionCode); 365 #endif 366 367 #if ENABLE(SVG) 368 static const char* svgExceptionName(int exceptionCode); 369 #endif 370 371 static void createUtilityContext(); 372 373 // Returns a local handle of the utility context. utilityContext()374 static v8::Local<v8::Context> utilityContext() 375 { 376 if (m_utilityContext.IsEmpty()) 377 createUtilityContext(); 378 return v8::Local<v8::Context>::New(m_utilityContext); 379 } 380 381 Frame* m_frame; 382 383 // For the moment, we have one of these. Soon we will have one per DOMWrapperWorld. 384 RefPtr<V8DOMWindowShell> m_windowShell; 385 386 // Utility context holding JavaScript functions used internally. 387 static v8::Persistent<v8::Context> m_utilityContext; 388 389 int m_handlerLineNumber; 390 391 // True for <a href="javascript:foo()"> and false for <script>foo()</script>. 392 // Only valid during execution. 393 bool m_inlineCode; 394 395 // True when executing from within a timer callback. Only valid during 396 // execution. 397 bool m_timerCallback; 398 399 // Track the recursion depth to be able to avoid too deep recursion. The V8 400 // engine allows much more recursion than KJS does so we need to guard against 401 // excessive recursion in the binding layer. 402 int m_recursion; 403 404 // All of the extensions registered with the context. 405 static V8Extensions m_extensions; 406 407 // The isolated worlds we are tracking for this frame. We hold them alive 408 // here so that they can be used again by future calls to 409 // evaluateInIsolatedWorld(). 410 // 411 // Note: although the pointer is raw, the instance is kept alive by a strong 412 // reference to the v8 context it contains, which is not made weak until we 413 // call world->destroy(). 414 // 415 // FIXME: We want to eventually be holding window shells instead of the 416 // IsolatedContext directly. 417 typedef HashMap<int, V8IsolatedContext*> IsolatedWorldMap; 418 IsolatedWorldMap m_isolatedWorlds; 419 }; 420 421 template <int tag, typename T> constructDOMObject(const v8::Arguments & args)422 v8::Handle<v8::Value> V8Proxy::constructDOMObject(const v8::Arguments& args) 423 { 424 if (!args.IsConstructCall()) 425 return throwError(V8Proxy::TypeError, "DOM object constructor cannot be called as a function."); 426 427 // Note: it's OK to let this RefPtr go out of scope because we also call 428 // SetDOMWrapper(), which effectively holds a reference to obj. 429 RefPtr<T> obj = T::create(); 430 V8DOMWrapper::setDOMWrapper(args.Holder(), tag, obj.get()); 431 obj->ref(); 432 V8DOMWrapper::setJSWrapperForDOMObject(obj.get(), v8::Persistent<v8::Object>::New(args.Holder())); 433 return args.Holder(); 434 } 435 436 437 v8::Local<v8::Context> toV8Context(ScriptExecutionContext*, const WorldContextHandle& worldContext); 438 439 // Used by an interceptor callback that it hasn't found anything to 440 // intercept. notHandledByInterceptor()441 inline static v8::Local<v8::Object> notHandledByInterceptor() 442 { 443 return v8::Local<v8::Object>(); 444 } 445 deletionNotHandledByInterceptor()446 inline static v8::Local<v8::Boolean> deletionNotHandledByInterceptor() 447 { 448 return v8::Local<v8::Boolean>(); 449 } 450 inline v8::Handle<v8::Primitive> throwError(const char* message, V8Proxy::ErrorType type = V8Proxy::TypeError) 451 { 452 V8Proxy::throwError(type, message); 453 return v8::Undefined(); 454 } 455 throwError(ExceptionCode ec)456 inline v8::Handle<v8::Primitive> throwError(ExceptionCode ec) 457 { 458 V8Proxy::setDOMException(ec); 459 return v8::Undefined(); 460 } 461 throwError(v8::Local<v8::Value> exception)462 inline v8::Handle<v8::Primitive> throwError(v8::Local<v8::Value> exception) 463 { 464 v8::ThrowException(exception); 465 return v8::Undefined(); 466 } 467 toV8(PassRefPtr<T> object,v8::Local<v8::Object> holder)468 template <class T> inline v8::Handle<v8::Object> toV8(PassRefPtr<T> object, v8::Local<v8::Object> holder) 469 { 470 object->ref(); 471 V8DOMWrapper::setJSWrapperForDOMObject(object.get(), v8::Persistent<v8::Object>::New(holder)); 472 return holder; 473 } 474 475 } 476 477 #endif // V8Proxy_h 478