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 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 139 Frame* frame() { return m_frame; } 140 141 void clearForNavigation(); 142 void clearForClose(); 143 144 // FIXME: Need comment. User Gesture related. 145 bool inlineCode() const { return m_inlineCode; } 146 void setInlineCode(bool value) { m_inlineCode = value; } 147 148 bool timerCallback() const { return m_timerCallback; } 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 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 #ifdef ANDROID_INSTRUMENT 175 v8::Local<v8::Value> runScriptInternal(v8::Handle<v8::Script> script, bool inline_code); 176 #endif 177 178 // Call the function with the given receiver and arguments. 179 v8::Local<v8::Value> callFunction(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int argc, v8::Handle<v8::Value> argv[]); 180 181 // Call the function with the given receiver and arguments. 182 static v8::Local<v8::Value> callFunctionWithoutFrame(v8::Handle<v8::Function>, v8::Handle<v8::Object>, int argc, v8::Handle<v8::Value> argv[]); 183 184 // Call the function as constructor with the given arguments. 185 v8::Local<v8::Value> newInstance(v8::Handle<v8::Function>, int argc, v8::Handle<v8::Value> argv[]); 186 187 // Returns the window object associated with a context. 188 static DOMWindow* retrieveWindow(v8::Handle<v8::Context>); 189 // Returns V8Proxy object of the currently executing context. 190 static V8Proxy* retrieve(); 191 // Returns V8Proxy object associated with a frame. 192 static V8Proxy* retrieve(Frame*); 193 // Returns V8Proxy object associated with a script execution context. 194 static V8Proxy* retrieve(ScriptExecutionContext*); 195 196 // Returns the frame object of the window object associated with 197 // a context. 198 static Frame* retrieveFrame(v8::Handle<v8::Context>); 199 200 201 // The three functions below retrieve WebFrame instances relating the 202 // currently executing JavaScript. Since JavaScript can make function calls 203 // across frames, though, we need to be more precise. 204 // 205 // For example, imagine that a JS function in frame A calls a function in 206 // frame B, which calls native code, which wants to know what the 'active' 207 // frame is. 208 // 209 // The 'entered context' is the context where execution first entered the 210 // script engine; the context that is at the bottom of the JS function stack. 211 // RetrieveFrameForEnteredContext() would return Frame A in our example. 212 // This frame is often referred to as the "dynamic global object." 213 // 214 // The 'current context' is the context the JS engine is currently inside of; 215 // the context that is at the top of the JS function stack. 216 // RetrieveFrameForCurrentContext() would return Frame B in our example. 217 // This frame is often referred to as the "lexical global object." 218 // 219 // Finally, the 'calling context' is the context one below the current 220 // context on the JS function stack. For example, if function f calls 221 // function g, then the calling context will be the context associated with 222 // f. This context is commonly used by DOM security checks because they want 223 // to know who called them. 224 // 225 // If you are unsure which of these functions to use, ask abarth. 226 // 227 // NOTE: These cannot be declared as inline function, because VS complains at 228 // linking time. 229 static Frame* retrieveFrameForEnteredContext(); 230 static Frame* retrieveFrameForCurrentContext(); 231 static Frame* retrieveFrameForCallingContext(); 232 233 // Returns V8 Context of a frame. If none exists, creates 234 // a new context. It is potentially slow and consumes memory. 235 static v8::Local<v8::Context> context(Frame*); 236 static v8::Local<v8::Context> mainWorldContext(Frame*); 237 static v8::Local<v8::Context> currentContext(); 238 239 // If the current context causes out of memory, JavaScript setting 240 // is disabled and it returns true. 241 static bool handleOutOfMemory(); 242 243 static v8::Handle<v8::Value> checkNewLegal(const v8::Arguments&); 244 245 static v8::Handle<v8::Script> compileScript(v8::Handle<v8::String> code, const String& fileName, const TextPosition0& scriptStartPosition, v8::ScriptData* = 0); 246 247 #ifdef ANDROID_INSTRUMENT 248 static v8::Handle<v8::Script> compileScriptInternal(v8::Handle<v8::String> code, const String& fileName, int baseLine, v8::ScriptData* scriptData); 249 #endif 250 251 // If the exception code is different from zero, a DOM exception is 252 // schedule to be thrown. 253 static void setDOMException(int exceptionCode); 254 255 // Schedule an error object to be thrown. 256 static v8::Handle<v8::Value> throwError(ErrorType, const char* message); 257 258 // Helpers for throwing syntax and type errors with predefined messages. 259 static v8::Handle<v8::Value> throwTypeError(); 260 static v8::Handle<v8::Value> throwSyntaxError(); 261 262 template <typename T> 263 static v8::Handle<v8::Value> constructDOMObject(const v8::Arguments&, WrapperTypeInfo*); 264 265 template <typename T> 266 static v8::Handle<v8::Value> constructDOMObjectWithScriptExecutionContext(const v8::Arguments&, WrapperTypeInfo*); 267 268 v8::Local<v8::Context> context(); 269 v8::Local<v8::Context> mainWorldContext(); 270 271 // FIXME: This should eventually take DOMWrapperWorld argument! 272 V8DOMWindowShell* windowShell() const { return m_windowShell.get(); } 273 274 bool setContextDebugId(int id); 275 static int contextDebugId(v8::Handle<v8::Context>); 276 277 // Registers a v8 extension to be available on webpages. Will only 278 // affect v8 contexts initialized after this call. Takes ownership of 279 // the v8::Extension object passed. 280 static void registerExtension(v8::Extension*); 281 282 static void registerExtensionWithV8(v8::Extension*); 283 static bool registeredExtensionWithV8(v8::Extension*); 284 285 static const V8Extensions& extensions() { return m_extensions; } 286 287 // Report an unsafe attempt to access the given frame on the console. 288 static void reportUnsafeAccessTo(Frame* target); 289 290 private: 291 void didLeaveScriptContext(); 292 293 void resetIsolatedWorlds(); 294 295 PassOwnPtr<v8::ScriptData> precompileScript(v8::Handle<v8::String>, CachedScript*); 296 297 // Returns false when we're out of memory in V8. 298 bool setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContext); 299 300 static const char* rangeExceptionName(int exceptionCode); 301 static const char* eventExceptionName(int exceptionCode); 302 static const char* xmlHttpRequestExceptionName(int exceptionCode); 303 static const char* domExceptionName(int exceptionCode); 304 305 #if ENABLE(XPATH) 306 static const char* xpathExceptionName(int exceptionCode); 307 #endif 308 309 #if ENABLE(SVG) 310 static const char* svgExceptionName(int exceptionCode); 311 #endif 312 313 #if ENABLE(DATABASE) 314 static const char* sqlExceptionName(int exceptionCode); 315 #endif 316 317 Frame* m_frame; 318 319 // For the moment, we have one of these. Soon we will have one per DOMWrapperWorld. 320 RefPtr<V8DOMWindowShell> m_windowShell; 321 322 // True for <a href="javascript:foo()"> and false for <script>foo()</script>. 323 // Only valid during execution. 324 bool m_inlineCode; 325 326 // True when executing from within a timer callback. Only valid during 327 // execution. 328 bool m_timerCallback; 329 330 // Track the recursion depth to be able to avoid too deep recursion. The V8 331 // engine allows much more recursion than KJS does so we need to guard against 332 // excessive recursion in the binding layer. 333 int m_recursion; 334 335 // All of the extensions registered with the context. 336 static V8Extensions m_extensions; 337 338 // The isolated worlds we are tracking for this frame. We hold them alive 339 // here so that they can be used again by future calls to 340 // evaluateInIsolatedWorld(). 341 // 342 // Note: although the pointer is raw, the instance is kept alive by a strong 343 // reference to the v8 context it contains, which is not made weak until we 344 // call world->destroy(). 345 // 346 // FIXME: We want to eventually be holding window shells instead of the 347 // IsolatedContext directly. 348 typedef HashMap<int, V8IsolatedContext*> IsolatedWorldMap; 349 IsolatedWorldMap m_isolatedWorlds; 350 }; 351 352 template <typename T> 353 v8::Handle<v8::Value> V8Proxy::constructDOMObject(const v8::Arguments& args, WrapperTypeInfo* type) 354 { 355 if (!args.IsConstructCall()) 356 return throwError(V8Proxy::TypeError, "DOM object constructor cannot be called as a function."); 357 358 // Note: it's OK to let this RefPtr go out of scope because we also call 359 // SetDOMWrapper(), which effectively holds a reference to obj. 360 RefPtr<T> obj = T::create(); 361 V8DOMWrapper::setDOMWrapper(args.Holder(), type, obj.get()); 362 obj->ref(); 363 V8DOMWrapper::setJSWrapperForDOMObject(obj.get(), v8::Persistent<v8::Object>::New(args.Holder())); 364 return args.Holder(); 365 } 366 367 template <typename T> 368 v8::Handle<v8::Value> V8Proxy::constructDOMObjectWithScriptExecutionContext(const v8::Arguments& args, WrapperTypeInfo* type) 369 { 370 if (!args.IsConstructCall()) 371 return throwError(V8Proxy::TypeError, ""); 372 373 ScriptExecutionContext* context = getScriptExecutionContext(); 374 if (!context) 375 return throwError(V8Proxy::ReferenceError, ""); 376 377 // Note: it's OK to let this RefPtr go out of scope because we also call 378 // SetDOMWrapper(), which effectively holds a reference to obj. 379 RefPtr<T> obj = T::create(context); 380 V8DOMWrapper::setDOMWrapper(args.Holder(), type, obj.get()); 381 obj->ref(); 382 V8DOMWrapper::setJSWrapperForDOMObject(obj.get(), v8::Persistent<v8::Object>::New(args.Holder())); 383 return args.Holder(); 384 } 385 386 387 v8::Local<v8::Context> toV8Context(ScriptExecutionContext*, const WorldContextHandle& worldContext); 388 389 // Used by an interceptor callback that it hasn't found anything to 390 // intercept. 391 inline static v8::Local<v8::Object> notHandledByInterceptor() 392 { 393 return v8::Local<v8::Object>(); 394 } 395 396 inline static v8::Local<v8::Boolean> deletionNotHandledByInterceptor() 397 { 398 return v8::Local<v8::Boolean>(); 399 } 400 inline v8::Handle<v8::Primitive> throwError(const char* message, V8Proxy::ErrorType type = V8Proxy::TypeError) 401 { 402 if (!v8::V8::IsExecutionTerminating()) 403 V8Proxy::throwError(type, message); 404 return v8::Undefined(); 405 } 406 407 inline v8::Handle<v8::Primitive> throwError(ExceptionCode ec) 408 { 409 if (!v8::V8::IsExecutionTerminating()) 410 V8Proxy::setDOMException(ec); 411 return v8::Undefined(); 412 } 413 414 inline v8::Handle<v8::Primitive> throwError(v8::Local<v8::Value> exception) 415 { 416 if (!v8::V8::IsExecutionTerminating()) 417 v8::ThrowException(exception); 418 return v8::Undefined(); 419 } 420 421 template <class T> inline v8::Handle<v8::Object> toV8(PassRefPtr<T> object, v8::Local<v8::Object> holder) 422 { 423 object->ref(); 424 V8DOMWrapper::setJSWrapperForDOMObject(object.get(), v8::Persistent<v8::Object>::New(holder)); 425 return holder; 426 } 427 428 } 429 430 #endif // V8Proxy_h 431