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