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 "V8Utilities.h" 43 #include "WrapperTypeInfo.h" 44 #include <v8.h> 45 #include <wtf/Forward.h> 46 #include <wtf/PassRefPtr.h> // so generated bindings don't have to 47 #include <wtf/Vector.h> 48 49 #if defined(ENABLE_DOM_STATS_COUNTERS) && PLATFORM(CHROMIUM) 50 #define INC_STATS(name) PlatformBridge::incrementStatsCounter(name) 51 #else 52 #define INC_STATS(name) 53 #endif 54 55 namespace WebCore { 56 57 class CachedScript; 58 class DOMWindow; 59 class Frame; 60 class Node; 61 class ScriptExecutionContext; 62 class V8EventListener; 63 class V8IsolatedContext; 64 class WorldContextHandle; 65 66 // FIXME: use standard logging facilities in WebCore. 67 void logInfo(Frame*, const String& message, const String& url); 68 69 // The following Batch structs and methods are used for setting multiple 70 // properties on an ObjectTemplate, used from the generated bindings 71 // initialization (ConfigureXXXTemplate). This greatly reduces the binary 72 // size by moving from code driven setup to data table driven setup. 73 74 // BatchedAttribute translates into calls to SetAccessor() on either the 75 // instance or the prototype ObjectTemplate, based on |onProto|. 76 struct BatchedAttribute { 77 const char* const name; 78 v8::AccessorGetter getter; 79 v8::AccessorSetter setter; 80 WrapperTypeInfo* data; 81 v8::AccessControl settings; 82 v8::PropertyAttribute attribute; 83 bool onProto; 84 }; 85 86 void batchConfigureAttributes(v8::Handle<v8::ObjectTemplate>, v8::Handle<v8::ObjectTemplate>, const BatchedAttribute*, size_t attributeCount); 87 configureAttribute(v8::Handle<v8::ObjectTemplate> instance,v8::Handle<v8::ObjectTemplate> proto,const BatchedAttribute & attribute)88 inline void configureAttribute(v8::Handle<v8::ObjectTemplate> instance, v8::Handle<v8::ObjectTemplate> proto, const BatchedAttribute& attribute) 89 { 90 (attribute.onProto ? proto : instance)->SetAccessor(v8::String::New(attribute.name), 91 attribute.getter, 92 attribute.setter, 93 v8::External::Wrap(attribute.data), 94 attribute.settings, 95 attribute.attribute); 96 } 97 98 // BatchedConstant translates into calls to Set() for setting up an object's 99 // constants. It sets the constant on both the FunctionTemplate and the 100 // ObjectTemplate. PropertyAttributes is always ReadOnly. 101 struct BatchedConstant { 102 const char* const name; 103 int value; 104 }; 105 106 void batchConfigureConstants(v8::Handle<v8::FunctionTemplate>, v8::Handle<v8::ObjectTemplate>, const BatchedConstant*, size_t constantCount); 107 108 struct BatchedCallback { 109 const char* const name; 110 v8::InvocationCallback callback; 111 }; 112 113 void batchConfigureCallbacks(v8::Handle<v8::ObjectTemplate>, 114 v8::Handle<v8::Signature>, 115 v8::PropertyAttribute, 116 const BatchedCallback*, 117 size_t callbackCount); 118 119 const int kMaxRecursionDepth = 22; 120 121 // The list of extensions that are registered for use with V8. 122 typedef WTF::Vector<v8::Extension*> V8Extensions; 123 124 class V8Proxy { 125 public: 126 // The types of javascript errors that can be thrown. 127 enum ErrorType { 128 RangeError, 129 ReferenceError, 130 SyntaxError, 131 TypeError, 132 GeneralError 133 }; 134 135 explicit V8Proxy(Frame*); 136 137 ~V8Proxy(); 138 frame()139 Frame* frame() { return m_frame; } 140 141 void clearForNavigation(); 142 void clearForClose(); 143 144 // FIXME: Need comment. User Gesture related. inlineCode()145 bool inlineCode() const { return m_inlineCode; } setInlineCode(bool value)146 void setInlineCode(bool value) { m_inlineCode = value; } 147 timerCallback()148 bool timerCallback() const { return m_timerCallback; } setTimerCallback(bool value)149 void setTimerCallback(bool value) { m_timerCallback = value; } 150 151 // Disconnects the proxy from its owner frame, 152 // and clears all timeouts on the DOM window. 153 void disconnectFrame(); 154 finishedWithEvent(Event *)155 void finishedWithEvent(Event*) { } 156 157 // Evaluate JavaScript in a new isolated world. The script gets its own 158 // global scope, its own prototypes for intrinsic JavaScript objects (String, 159 // Array, and so-on), and its own wrappers for all DOM nodes and DOM 160 // constructors. 161 void evaluateInIsolatedWorld(int worldId, const Vector<ScriptSourceCode>& sources, int extensionGroup); 162 163 // Returns true if the proxy is currently executing a script in V8. 164 bool executingScript() const; 165 166 // Evaluate a script file in the current execution environment. 167 // The caller must hold an execution context. 168 // If cannot evalute the script, it returns an error. 169 v8::Local<v8::Value> evaluate(const ScriptSourceCode&, Node*); 170 171 // Run an already compiled script. 172 v8::Local<v8::Value> runScript(v8::Handle<v8::Script>, bool isInlineCode); 173 174 // Call the function with the given receiver and arguments. 175 v8::Local<v8::Value> callFunction(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int argc, v8::Handle<v8::Value> argv[]); 176 177 // Call the function with the given receiver and arguments. 178 static v8::Local<v8::Value> callFunctionWithoutFrame(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int argc, v8::Handle<v8::Value> argv[]); 179 180 // Call the function as constructor with the given arguments. 181 v8::Local<v8::Value> newInstance(v8::Handle<v8::Function>, int argc, v8::Handle<v8::Value> argv[]); 182 183 // Returns the window object associated with a context. 184 static DOMWindow* retrieveWindow(v8::Handle<v8::Context>); 185 // Returns V8Proxy object of the currently executing context. 186 static V8Proxy* retrieve(); 187 // Returns V8Proxy object associated with a frame. 188 static V8Proxy* retrieve(Frame*); 189 // Returns V8Proxy object associated with a script execution context. 190 static V8Proxy* retrieve(ScriptExecutionContext*); 191 192 // Returns the frame object of the window object associated with 193 // a context. 194 static Frame* retrieveFrame(v8::Handle<v8::Context>); 195 196 197 // The three functions below retrieve WebFrame instances relating the 198 // currently executing JavaScript. Since JavaScript can make function calls 199 // across frames, though, we need to be more precise. 200 // 201 // For example, imagine that a JS function in frame A calls a function in 202 // frame B, which calls native code, which wants to know what the 'active' 203 // frame is. 204 // 205 // The 'entered context' is the context where execution first entered the 206 // script engine; the context that is at the bottom of the JS function stack. 207 // RetrieveFrameForEnteredContext() would return Frame A in our example. 208 // This frame is often referred to as the "dynamic global object." 209 // 210 // The 'current context' is the context the JS engine is currently inside of; 211 // the context that is at the top of the JS function stack. 212 // RetrieveFrameForCurrentContext() would return Frame B in our example. 213 // This frame is often referred to as the "lexical global object." 214 // 215 // Finally, the 'calling context' is the context one below the current 216 // context on the JS function stack. For example, if function f calls 217 // function g, then the calling context will be the context associated with 218 // f. This context is commonly used by DOM security checks because they want 219 // to know who called them. 220 // 221 // If you are unsure which of these functions to use, ask abarth. 222 // 223 // NOTE: These cannot be declared as inline function, because VS complains at 224 // linking time. 225 static Frame* retrieveFrameForEnteredContext(); 226 static Frame* retrieveFrameForCurrentContext(); 227 static Frame* retrieveFrameForCallingContext(); 228 229 // Returns V8 Context of a frame. If none exists, creates 230 // a new context. It is potentially slow and consumes memory. 231 static v8::Local<v8::Context> context(Frame*); 232 static v8::Local<v8::Context> mainWorldContext(Frame*); 233 static v8::Local<v8::Context> currentContext(); 234 235 // If the current context causes out of memory, JavaScript setting 236 // is disabled and it returns true. 237 static bool handleOutOfMemory(); 238 239 static v8::Handle<v8::Value> checkNewLegal(const v8::Arguments&); 240 241 static v8::Handle<v8::Script> compileScript(v8::Handle<v8::String> code, const String& fileName, const TextPosition0& scriptStartPosition, v8::ScriptData* = 0); 242 243 // If the exception code is different from zero, a DOM exception is 244 // schedule to be thrown. 245 static void setDOMException(int exceptionCode); 246 247 // Schedule an error object to be thrown. 248 static v8::Handle<v8::Value> throwError(ErrorType, const char* message); 249 250 // Helpers for throwing syntax and type errors with predefined messages. 251 static v8::Handle<v8::Value> throwTypeError(); 252 static v8::Handle<v8::Value> throwSyntaxError(); 253 254 template <typename T> 255 static v8::Handle<v8::Value> constructDOMObject(const v8::Arguments&, WrapperTypeInfo*); 256 257 template <typename T> 258 static v8::Handle<v8::Value> constructDOMObjectWithScriptExecutionContext(const v8::Arguments&, WrapperTypeInfo*); 259 260 v8::Local<v8::Context> context(); 261 v8::Local<v8::Context> mainWorldContext(); 262 263 // FIXME: This should eventually take DOMWrapperWorld argument! windowShell()264 V8DOMWindowShell* windowShell() const { return m_windowShell.get(); } 265 266 bool setContextDebugId(int id); 267 static int contextDebugId(v8::Handle<v8::Context>); 268 269 // Registers a v8 extension to be available on webpages. Will only 270 // affect v8 contexts initialized after this call. Takes ownership of 271 // the v8::Extension object passed. 272 static void registerExtension(v8::Extension*); 273 274 static void registerExtensionWithV8(v8::Extension*); 275 static bool registeredExtensionWithV8(v8::Extension*); 276 extensions()277 static const V8Extensions& extensions() { return m_extensions; } 278 279 // Report an unsafe attempt to access the given frame on the console. 280 static void reportUnsafeAccessTo(Frame* target); 281 282 private: 283 void didLeaveScriptContext(); 284 285 void resetIsolatedWorlds(); 286 287 PassOwnPtr<v8::ScriptData> precompileScript(v8::Handle<v8::String>, CachedScript*); 288 289 // Returns false when we're out of memory in V8. 290 bool setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContext); 291 292 static const char* rangeExceptionName(int exceptionCode); 293 static const char* eventExceptionName(int exceptionCode); 294 static const char* xmlHttpRequestExceptionName(int exceptionCode); 295 static const char* domExceptionName(int exceptionCode); 296 297 #if ENABLE(XPATH) 298 static const char* xpathExceptionName(int exceptionCode); 299 #endif 300 301 #if ENABLE(SVG) 302 static const char* svgExceptionName(int exceptionCode); 303 #endif 304 305 #if ENABLE(DATABASE) 306 static const char* sqlExceptionName(int exceptionCode); 307 #endif 308 309 Frame* m_frame; 310 311 // For the moment, we have one of these. Soon we will have one per DOMWrapperWorld. 312 RefPtr<V8DOMWindowShell> m_windowShell; 313 314 // True for <a href="javascript:foo()"> and false for <script>foo()</script>. 315 // Only valid during execution. 316 bool m_inlineCode; 317 318 // True when executing from within a timer callback. Only valid during 319 // execution. 320 bool m_timerCallback; 321 322 // Track the recursion depth to be able to avoid too deep recursion. The V8 323 // engine allows much more recursion than KJS does so we need to guard against 324 // excessive recursion in the binding layer. 325 int m_recursion; 326 327 // All of the extensions registered with the context. 328 static V8Extensions m_extensions; 329 330 // The isolated worlds we are tracking for this frame. We hold them alive 331 // here so that they can be used again by future calls to 332 // evaluateInIsolatedWorld(). 333 // 334 // Note: although the pointer is raw, the instance is kept alive by a strong 335 // reference to the v8 context it contains, which is not made weak until we 336 // call world->destroy(). 337 // 338 // FIXME: We want to eventually be holding window shells instead of the 339 // IsolatedContext directly. 340 typedef HashMap<int, V8IsolatedContext*> IsolatedWorldMap; 341 IsolatedWorldMap m_isolatedWorlds; 342 }; 343 344 template <typename T> constructDOMObject(const v8::Arguments & args,WrapperTypeInfo * type)345 v8::Handle<v8::Value> V8Proxy::constructDOMObject(const v8::Arguments& args, WrapperTypeInfo* type) 346 { 347 if (!args.IsConstructCall()) 348 return throwError(V8Proxy::TypeError, "DOM object constructor cannot be called as a function."); 349 350 // Note: it's OK to let this RefPtr go out of scope because we also call 351 // SetDOMWrapper(), which effectively holds a reference to obj. 352 RefPtr<T> obj = T::create(); 353 V8DOMWrapper::setDOMWrapper(args.Holder(), type, obj.get()); 354 obj->ref(); 355 V8DOMWrapper::setJSWrapperForDOMObject(obj.get(), v8::Persistent<v8::Object>::New(args.Holder())); 356 return args.Holder(); 357 } 358 359 template <typename T> constructDOMObjectWithScriptExecutionContext(const v8::Arguments & args,WrapperTypeInfo * type)360 v8::Handle<v8::Value> V8Proxy::constructDOMObjectWithScriptExecutionContext(const v8::Arguments& args, WrapperTypeInfo* type) 361 { 362 if (!args.IsConstructCall()) 363 return throwError(V8Proxy::TypeError, ""); 364 365 ScriptExecutionContext* context = getScriptExecutionContext(); 366 if (!context) 367 return throwError(V8Proxy::ReferenceError, ""); 368 369 // Note: it's OK to let this RefPtr go out of scope because we also call 370 // SetDOMWrapper(), which effectively holds a reference to obj. 371 RefPtr<T> obj = T::create(context); 372 V8DOMWrapper::setDOMWrapper(args.Holder(), type, obj.get()); 373 obj->ref(); 374 V8DOMWrapper::setJSWrapperForDOMObject(obj.get(), v8::Persistent<v8::Object>::New(args.Holder())); 375 return args.Holder(); 376 } 377 378 379 v8::Local<v8::Context> toV8Context(ScriptExecutionContext*, const WorldContextHandle& worldContext); 380 381 // Used by an interceptor callback that it hasn't found anything to 382 // intercept. notHandledByInterceptor()383 inline static v8::Local<v8::Object> notHandledByInterceptor() 384 { 385 return v8::Local<v8::Object>(); 386 } 387 deletionNotHandledByInterceptor()388 inline static v8::Local<v8::Boolean> deletionNotHandledByInterceptor() 389 { 390 return v8::Local<v8::Boolean>(); 391 } 392 inline v8::Handle<v8::Primitive> throwError(const char* message, V8Proxy::ErrorType type = V8Proxy::TypeError) 393 { 394 if (!v8::V8::IsExecutionTerminating()) 395 V8Proxy::throwError(type, message); 396 return v8::Undefined(); 397 } 398 throwError(ExceptionCode ec)399 inline v8::Handle<v8::Primitive> throwError(ExceptionCode ec) 400 { 401 if (!v8::V8::IsExecutionTerminating()) 402 V8Proxy::setDOMException(ec); 403 return v8::Undefined(); 404 } 405 throwError(v8::Local<v8::Value> exception)406 inline v8::Handle<v8::Primitive> throwError(v8::Local<v8::Value> exception) 407 { 408 if (!v8::V8::IsExecutionTerminating()) 409 v8::ThrowException(exception); 410 return v8::Undefined(); 411 } 412 toV8(PassRefPtr<T> object,v8::Local<v8::Object> holder)413 template <class T> inline v8::Handle<v8::Object> toV8(PassRefPtr<T> object, v8::Local<v8::Object> holder) 414 { 415 object->ref(); 416 V8DOMWrapper::setJSWrapperForDOMObject(object.get(), v8::Persistent<v8::Object>::New(holder)); 417 return holder; 418 } 419 420 } 421 422 #endif // V8Proxy_h 423