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 "Node.h" 35 #include "NodeFilter.h" 36 #include "PlatformString.h" // for WebCore::String 37 #include "ScriptSourceCode.h" // for WebCore::ScriptSourceCode 38 #include "SecurityOrigin.h" // for WebCore::SecurityOrigin 39 #include "V8CustomBinding.h" 40 #include "V8DOMMap.h" 41 #include "V8DOMWrapper.h" 42 #include "V8EventListenerList.h" 43 #include "V8GCController.h" 44 #include "V8Index.h" 45 #include "V8Utilities.h" 46 #include <v8.h> 47 #include <wtf/Assertions.h> 48 #include <wtf/PassRefPtr.h> // so generated bindings don't have to 49 #include <wtf/Vector.h> 50 51 #if defined(ENABLE_DOM_STATS_COUNTERS) && PLATFORM(CHROMIUM) 52 #include "ChromiumBridge.h" 53 #define INC_STATS(name) ChromiumBridge::incrementStatsCounter(name) 54 #else 55 #define INC_STATS(name) 56 #endif 57 58 namespace WebCore { 59 60 class CSSRule; 61 class CSSRuleList; 62 class CSSStyleDeclaration; 63 class CSSValue; 64 class CSSValueList; 65 class ClientRectList; 66 class DOMImplementation; 67 class DOMWindow; 68 class Document; 69 class Element; 70 class Event; 71 class EventListener; 72 class EventTarget; 73 class Frame; 74 class HTMLCollection; 75 class HTMLDocument; 76 class HTMLElement; 77 class HTMLOptionsCollection; 78 class MediaList; 79 class MimeType; 80 class MimeTypeArray; 81 class NamedNodeMap; 82 class Navigator; 83 class Node; 84 class NodeFilter; 85 class NodeList; 86 class Plugin; 87 class PluginArray; 88 class SVGElement; 89 #if ENABLE(SVG) 90 class SVGElementInstance; 91 #endif 92 class Screen; 93 class ScriptExecutionContext; 94 #if ENABLE(DOM_STORAGE) 95 class Storage; 96 class StorageEvent; 97 #endif 98 class String; 99 class StyleSheet; 100 class StyleSheetList; 101 class V8EventListener; 102 class V8ObjectEventListener; 103 104 // FIXME: use standard logging facilities in WebCore. 105 void logInfo(Frame*, const String& message, const String& url); 106 107 // The following Batch structs and methods are used for setting multiple 108 // properties on an ObjectTemplate, used from the generated bindings 109 // initialization (ConfigureXXXTemplate). This greatly reduces the binary 110 // size by moving from code driven setup to data table driven setup. 111 112 // BatchedAttribute translates into calls to SetAccessor() on either the 113 // instance or the prototype ObjectTemplate, based on |onProto|. 114 struct BatchedAttribute { 115 const char* const name; 116 v8::AccessorGetter getter; 117 v8::AccessorSetter setter; 118 V8ClassIndex::V8WrapperType data; 119 v8::AccessControl settings; 120 v8::PropertyAttribute attribute; 121 bool onProto; 122 }; 123 124 void batchConfigureAttributes(v8::Handle<v8::ObjectTemplate>, v8::Handle<v8::ObjectTemplate>, const BatchedAttribute*, size_t attributeCount); 125 126 // BatchedConstant translates into calls to Set() for setting up an object's 127 // constants. It sets the constant on both the FunctionTemplate and the 128 // ObjectTemplate. PropertyAttributes is always ReadOnly. 129 struct BatchedConstant { 130 const char* const name; 131 int value; 132 }; 133 134 void batchConfigureConstants(v8::Handle<v8::FunctionTemplate>, v8::Handle<v8::ObjectTemplate>, const BatchedConstant*, size_t constantCount); 135 136 const int kMaxRecursionDepth = 20; 137 138 // Information about an extension that is registered for use with V8. If 139 // scheme is non-empty, it contains the URL scheme the extension should be 140 // used with. If group is non-zero, the extension will only be loaded into 141 // script contexts that belong to that group. Otherwise, the extension is 142 // used with all schemes and contexts. 143 struct V8ExtensionInfo { 144 String scheme; 145 int group; 146 v8::Extension* extension; 147 }; 148 typedef WTF::Vector<V8ExtensionInfo> V8ExtensionList; 149 150 class V8Proxy { 151 public: 152 // The types of javascript errors that can be thrown. 153 enum ErrorType { 154 RangeError, 155 ReferenceError, 156 SyntaxError, 157 TypeError, 158 GeneralError 159 }; 160 V8Proxy(Frame * frame)161 explicit V8Proxy(Frame* frame) : m_frame(frame), m_inlineCode(false), m_timerCallback(false), m_recursion(0) { } 162 163 ~V8Proxy(); 164 frame()165 Frame* frame() { return m_frame; } 166 167 // Clear page-specific data, but keep the global object identify. 168 void clearForNavigation(); 169 170 // Clear page-specific data before shutting down the proxy object. 171 void clearForClose(); 172 173 // Update document object of the frame. 174 void updateDocument(); 175 176 // Update the security origin of a document 177 // (e.g., after setting docoument.domain). 178 void updateSecurityOrigin(); 179 180 // Destroy the global object. 181 void destroyGlobal(); 182 183 // FIXME: Need comment. User Gesture related. inlineCode()184 bool inlineCode() const { return m_inlineCode; } setInlineCode(bool value)185 void setInlineCode(bool value) { m_inlineCode = value; } 186 timerCallback()187 bool timerCallback() const { return m_timerCallback; } setTimerCallback(bool value)188 void setTimerCallback(bool value) { m_timerCallback = value; } 189 190 // Has the context for this proxy been initialized? 191 bool isContextInitialized(); 192 193 // Disconnects the proxy from its owner frame, 194 // and clears all timeouts on the DOM window. 195 void disconnectFrame(); 196 197 bool isEnabled(); 198 eventListeners()199 V8EventListenerList* eventListeners() { return &m_eventListeners; } objectListeners()200 V8EventListenerList* objectListeners() { return &m_objectListeners; } 201 202 #if ENABLE(SVG) 203 static void setSVGContext(void*, SVGElement*); 204 static SVGElement* svgContext(void*); 205 #endif 206 setEventHandlerLineNumber(int lineNumber)207 void setEventHandlerLineNumber(int lineNumber) { m_handlerLineNumber = lineNumber; } finishedWithEvent(Event *)208 void finishedWithEvent(Event*) { } 209 210 // Evaluate JavaScript in a new isolated world. The script gets its own 211 // global scope, its own prototypes for intrinsic JavaScript objects (String, 212 // Array, and so-on), and its own wrappers for all DOM nodes and DOM 213 // constructors. 214 void evaluateInNewWorld(const Vector<ScriptSourceCode>& sources, int extensionGroup); 215 216 // Evaluate JavaScript in a new context. The script gets its own global scope 217 // and its own prototypes for intrinsic JavaScript objects (String, Array, 218 // and so-on). It shares the wrappers for all DOM nodes and DOM constructors. 219 void evaluateInNewContext(const Vector<ScriptSourceCode>&, int extensionGroup); 220 221 // Evaluate a script file in the current execution environment. 222 // The caller must hold an execution context. 223 // If cannot evalute the script, it returns an error. 224 v8::Local<v8::Value> evaluate(const ScriptSourceCode&, Node*); 225 226 // Run an already compiled script. 227 v8::Local<v8::Value> runScript(v8::Handle<v8::Script>, bool isInlineCode); 228 229 #ifdef ANDROID_INSTRUMENT 230 v8::Local<v8::Value> runScriptInternal(v8::Handle<v8::Script> script, bool inline_code); 231 #endif 232 233 // Call the function with the given receiver and arguments. 234 v8::Local<v8::Value> callFunction(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int argc, v8::Handle<v8::Value> argv[]); 235 236 // Call the function as constructor with the given arguments. 237 v8::Local<v8::Value> newInstance(v8::Handle<v8::Function>, int argc, v8::Handle<v8::Value> argv[]); 238 239 // To create JS Wrapper objects, we create a cache of a 'boiler plate' 240 // object, and then simply Clone that object each time we need a new one. 241 // This is faster than going through the full object creation process. 242 v8::Local<v8::Object> createWrapperFromCache(V8ClassIndex::V8WrapperType); 243 244 // Returns the window object associated with a context. 245 static DOMWindow* retrieveWindow(v8::Handle<v8::Context>); 246 // Returns V8Proxy object of the currently executing context. 247 static V8Proxy* retrieve(); 248 // Returns V8Proxy object associated with a frame. 249 static V8Proxy* retrieve(Frame*); 250 // Returns V8Proxy object associated with a script execution context. 251 static V8Proxy* retrieve(ScriptExecutionContext*); 252 253 // Returns the frame object of the window object associated with 254 // a context. 255 static Frame* retrieveFrame(v8::Handle<v8::Context>); 256 257 258 // The three functions below retrieve WebFrame instances relating the 259 // currently executing JavaScript. Since JavaScript can make function calls 260 // across frames, though, we need to be more precise. 261 // 262 // For example, imagine that a JS function in frame A calls a function in 263 // frame B, which calls native code, which wants to know what the 'active' 264 // frame is. 265 // 266 // The 'entered context' is the context where execution first entered the 267 // script engine; the context that is at the bottom of the JS function stack. 268 // RetrieveFrameForEnteredContext() would return Frame A in our example. 269 // This frame is often referred to as the "dynamic global object." 270 // 271 // The 'current context' is the context the JS engine is currently inside of; 272 // the context that is at the top of the JS function stack. 273 // RetrieveFrameForCurrentContext() would return Frame B in our example. 274 // This frame is often referred to as the "lexical global object." 275 // 276 // Finally, the 'calling context' is the context one below the current 277 // context on the JS function stack. For example, if function f calls 278 // function g, then the calling context will be the context associated with 279 // f. This context is commonly used by DOM security checks because they want 280 // to know who called them. 281 // 282 // If you are unsure which of these functions to use, ask abarth. 283 // 284 // NOTE: These cannot be declared as inline function, because VS complains at 285 // linking time. 286 static Frame* retrieveFrameForEnteredContext(); 287 static Frame* retrieveFrameForCurrentContext(); 288 static Frame* retrieveFrameForCallingContext(); 289 290 // Returns V8 Context of a frame. If none exists, creates 291 // a new context. It is potentially slow and consumes memory. 292 static v8::Local<v8::Context> context(Frame*); 293 static v8::Local<v8::Context> mainWorldContext(Frame*); 294 static v8::Local<v8::Context> currentContext(); 295 296 // If the current context causes out of memory, JavaScript setting 297 // is disabled and it returns true. 298 static bool handleOutOfMemory(); 299 300 // Check if the active execution context can access the target frame. 301 static bool canAccessFrame(Frame*, bool reportError); 302 303 // Check if it is safe to access the given node from the 304 // current security context. 305 static bool checkNodeSecurity(Node*); 306 307 static v8::Handle<v8::Value> checkNewLegal(const v8::Arguments&); 308 309 static v8::Handle<v8::Script> compileScript(v8::Handle<v8::String> code, const String& fileName, int baseLine); 310 311 #ifdef ANDROID_INSTRUMENT 312 static v8::Handle<v8::Script> compileScriptInternal(v8::Handle<v8::String> code, const String& fileName, int baseLine); 313 #endif 314 315 // If the exception code is different from zero, a DOM exception is 316 // schedule to be thrown. 317 static void setDOMException(int exceptionCode); 318 319 // Schedule an error object to be thrown. 320 static v8::Handle<v8::Value> throwError(ErrorType, const char* message); 321 322 // Create an instance of a function descriptor and set to the global object 323 // as a named property. Used by v8_test_shell. 324 static void bindJsObjectToWindow(Frame*, const char* name, int type, v8::Handle<v8::FunctionTemplate>, void*); 325 326 template <int tag, typename T> 327 static v8::Handle<v8::Value> constructDOMObject(const v8::Arguments&); 328 329 // Process any pending JavaScript console messages. 330 static void processConsoleMessages(); 331 332 // Function for retrieving the line number and source name for the top 333 // JavaScript stack frame. 334 static int sourceLineNumber(); 335 static String sourceName(); 336 337 // Returns a local handle of the context. context()338 v8::Local<v8::Context> context() 339 { 340 return v8::Local<v8::Context>::New(m_context); 341 } 342 343 bool setContextDebugId(int id); 344 static int contextDebugId(v8::Handle<v8::Context>); 345 346 static v8::Handle<v8::Value> getHiddenObjectPrototype(v8::Handle<v8::Context>); 347 // WARNING: Call |installHiddenObjectPrototype| only on fresh contexts! 348 static void installHiddenObjectPrototype(v8::Handle<v8::Context>); 349 350 // Registers a v8 extension to be available on webpages. The two forms 351 // offer various restrictions on what types of contexts the extension is 352 // loaded into. If a scheme is provided, only pages whose URL has the given 353 // scheme will match. If extensionGroup is provided, the extension will 354 // only be loaded into scripts run via evaluateInNewWorld with the 355 // matching group. Will only affect v8 contexts initialized after this 356 // call. Takes ownership of the v8::Extension object passed. 357 static void registerExtension(v8::Extension*, const String& schemeRestriction); 358 static void registerExtension(v8::Extension*, int extensionGroup); 359 360 // FIXME: Separate these concerns from V8Proxy? 361 v8::Persistent<v8::Context> createNewContext(v8::Handle<v8::Object> global, int extensionGroup); 362 static bool installDOMWindow(v8::Handle<v8::Context> context, DOMWindow* window); 363 364 void initContextIfNeeded(); 365 void updateDocumentWrapper(v8::Handle<v8::Value> wrapper); 366 367 private: 368 static const char* kContextDebugDataType; 369 static const char* kContextDebugDataValue; 370 371 void disconnectEventListeners(); 372 void setSecurityToken(); 373 void clearDocumentWrapper(); 374 375 // The JavaScript wrapper for the document object is cached on the global 376 // object for fast access. UpdateDocumentWrapperCache sets the wrapper 377 // for the current document on the global object. ClearDocumentWrapperCache 378 // deletes the document wrapper from the global object. 379 void updateDocumentWrapperCache(); 380 void clearDocumentWrapperCache(); 381 382 // Dispose global handles of m_contexts and friends. 383 void disposeContextHandles(); 384 385 static bool canAccessPrivate(DOMWindow*); 386 387 static const char* rangeExceptionName(int exceptionCode); 388 static const char* eventExceptionName(int exceptionCode); 389 static const char* xmlHttpRequestExceptionName(int exceptionCode); 390 static const char* domExceptionName(int exceptionCode); 391 392 #if ENABLE(XPATH) 393 static const char* xpathExceptionName(int exceptionCode); 394 #endif 395 396 #if ENABLE(SVG) 397 static const char* svgExceptionName(int exceptionCode); 398 #endif 399 400 static void createUtilityContext(); 401 402 // Returns a local handle of the utility context. utilityContext()403 static v8::Local<v8::Context> utilityContext() 404 { 405 if (m_utilityContext.IsEmpty()) 406 createUtilityContext(); 407 return v8::Local<v8::Context>::New(m_utilityContext); 408 } 409 410 static void registerExtensionWithV8(v8::Extension*); 411 412 Frame* m_frame; 413 414 v8::Persistent<v8::Context> m_context; 415 // For each possible type of wrapper, we keep a boilerplate object. 416 // The boilerplate is used to create additional wrappers of the same 417 // type. We keep a single persistent handle to an array of the 418 // activated boilerplates. 419 v8::Persistent<v8::Array> m_wrapperBoilerplates; 420 421 v8::Persistent<v8::Object> m_global; 422 v8::Persistent<v8::Value> m_document; 423 424 // Utility context holding JavaScript functions used internally. 425 static v8::Persistent<v8::Context> m_utilityContext; 426 427 int m_handlerLineNumber; 428 429 // A list of event listeners created for this frame, 430 // the list gets cleared when removing all timeouts. 431 V8EventListenerList m_eventListeners; 432 433 // A list of event listeners create for XMLHttpRequest object for this frame, 434 // the list gets cleared when removing all timeouts. 435 V8EventListenerList m_objectListeners; 436 437 // True for <a href="javascript:foo()"> and false for <script>foo()</script>. 438 // Only valid during execution. 439 bool m_inlineCode; 440 441 // True when executing from within a timer callback. Only valid during 442 // execution. 443 bool m_timerCallback; 444 445 // Track the recursion depth to be able to avoid too deep recursion. The V8 446 // engine allows much more recursion than KJS does so we need to guard against 447 // excessive recursion in the binding layer. 448 int m_recursion; 449 450 // List of extensions registered with the context. 451 static V8ExtensionList m_extensions; 452 }; 453 454 template <int tag, typename T> constructDOMObject(const v8::Arguments & args)455 v8::Handle<v8::Value> V8Proxy::constructDOMObject(const v8::Arguments& args) 456 { 457 if (!args.IsConstructCall()) 458 return throwError(V8Proxy::TypeError, "DOM object constructor cannot be called as a function."); 459 460 // Note: it's OK to let this RefPtr go out of scope because we also call 461 // SetDOMWrapper(), which effectively holds a reference to obj. 462 RefPtr<T> obj = T::create(); 463 V8DOMWrapper::setDOMWrapper(args.Holder(), tag, obj.get()); 464 obj->ref(); 465 V8DOMWrapper::setJSWrapperForDOMObject(obj.get(), v8::Persistent<v8::Object>::New(args.Holder())); 466 return args.Holder(); 467 } 468 469 470 // Used by an interceptor callback that it hasn't found anything to 471 // intercept. notHandledByInterceptor()472 inline static v8::Local<v8::Object> notHandledByInterceptor() 473 { 474 return v8::Local<v8::Object>(); 475 } 476 deletionNotHandledByInterceptor()477 inline static v8::Local<v8::Boolean> deletionNotHandledByInterceptor() 478 { 479 return v8::Local<v8::Boolean>(); 480 } 481 inline v8::Handle<v8::Primitive> throwError(const char* message, V8Proxy::ErrorType type = V8Proxy::TypeError) 482 { 483 V8Proxy::throwError(type, message); 484 return v8::Undefined(); 485 } 486 throwError(ExceptionCode ec)487 inline v8::Handle<v8::Primitive> throwError(ExceptionCode ec) 488 { 489 V8Proxy::setDOMException(ec); 490 return v8::Undefined(); 491 } 492 throwError(v8::Local<v8::Value> exception)493 inline v8::Handle<v8::Primitive> throwError(v8::Local<v8::Value> exception) 494 { 495 v8::ThrowException(exception); 496 return v8::Undefined(); 497 } 498 toV8(PassRefPtr<T> object,v8::Local<v8::Object> holder)499 template <class T> inline v8::Handle<v8::Object> toV8(PassRefPtr<T> object, v8::Local<v8::Object> holder) 500 { 501 object->ref(); 502 V8DOMWrapper::setJSWrapperForDOMObject(object.get(), v8::Persistent<v8::Object>::New(holder)); 503 return holder; 504 } 505 506 } 507 508 #endif // V8Proxy_h 509