1 // Copyright 2007, Google Inc.
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
6 // 1. Redistributions of source code must retain the above copyright notice,
7 // this list of conditions and the following disclaimer.
8 // 2. Redistributions in binary form must reproduce the above copyright notice,
9 // this list of conditions and the following disclaimer in the documentation
10 // and/or other materials provided with the distribution.
11 // 3. Neither the name of Google Inc. nor the names of its contributors may be
12 // used to endorse or promote products derived from this software without
13 // specific prior written permission.
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <gecko_sdk/include/nspr.h> // for PR_*
30 #include <gecko_sdk/include/nsCOMPtr.h>
31 #include <gecko_internal/jsapi.h>
32 #include <gecko_internal/nsIJSContextStack.h>
33 #include <gecko_internal/nsIPrincipal.h>
34 #include <gecko_internal/nsIScriptContext.h>
35 #include <gecko_internal/nsIScriptGlobalObject.h>
36 #include <gecko_internal/nsIScriptObjectPrincipal.h>
37 #include <gecko_internal/nsITimer.h>
38 #include <gecko_internal/nsITimerInternal.h>
40 #include "gears/base/common/js_runner.h"
42 #include "ff/genfiles/console.h"
43 #include "ff/genfiles/database.h"
44 #include "ff/genfiles/desktop_ff.h"
45 #include "ff/genfiles/httprequest.h"
48 // The Image and Blgo APIs have not been finalized for official builds
50 #include "ff/genfiles/blob_ff.h"
51 #include "ff/genfiles/image.h"
54 #include "ff/genfiles/localserver.h"
55 #include "ff/genfiles/timer_ff.h"
56 #include "ff/genfiles/workerpool.h"
57 #include "gears/base/common/common.h" // for DISALLOW_EVIL_CONSTRUCTORS
58 #include "gears/base/common/exception_handler_win32.h"
59 #include "gears/base/common/html_event_monitor.h"
60 #include "gears/base/common/js_runner_ff_marshaling.h"
61 #include "gears/base/common/scoped_token.h"
62 #include "gears/base/common/string_utils.h"
63 #include "gears/base/firefox/dom_utils.h"
64 #include "gears/factory/firefox/factory.h"
66 static const int kGarbageCollectionIntervalMsec
= 2000;
68 // Internal base class used to share some code between DocumentJsRunner and
69 // JsRunner. Do not override these methods from JsRunner or DocumentJsRunner.
70 // Either share the code here, or move it to those two classes if it's
72 class JsRunnerBase
: public JsRunnerInterface
{
74 JsRunnerBase() : alloc_js_wrapper_(NULL
), js_engine_context_(NULL
) {}
76 JsContextPtr
GetContext() {
77 return js_engine_context_
;
80 JsContextWrapperPtr
GetContextWrapper() {
81 assert(alloc_js_wrapper_
);
82 return alloc_js_wrapper_
;
85 JsObject
*NewObject(const char16
*optional_global_ctor_name
,
86 bool dump_on_error
= false) {
87 if (!js_engine_context_
) {
88 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
89 LOG(("Could not get JavaScript engine context."));
93 JSObject
*global_object
= JS_GetGlobalObject(js_engine_context_
);
95 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
96 LOG(("Could not get global object from script engine."));
100 std::string ctor_name_utf8
;
101 if (optional_global_ctor_name
) {
102 if (!String16ToUTF8(optional_global_ctor_name
, &ctor_name_utf8
)) {
103 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
104 LOG(("Could not convert constructor name."));
108 ctor_name_utf8
= "Object";
111 jsval val
= INT_TO_JSVAL(0);
112 JSBool result
= JS_GetProperty(js_engine_context_
, global_object
,
113 ctor_name_utf8
.c_str(), &val
);
115 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
116 LOG(("Could not get constructor property from global object."));
120 JSFunction
*ctor
= JS_ValueToFunction(js_engine_context_
, val
);
122 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
123 LOG(("Could not convert constructor property to function."));
127 // NOTE: We are calling the specified function here as a regular function,
128 // not as a constructor. I could not find a way to call a function as a
129 // constructor using JSAPI other than JS_ConstructObject which takes
130 // arguments I don't know how to provide. Ideally, there would be something
131 // like DISPATCH_CONSTRUCT in IE.
133 // This is OK for the built-in constructors that we want to call (such as
134 // "Error", "Object", etc) because those objects are specified to behave as
135 // constructors even without the 'new' keyword.
137 // For more information, see:
138 // * ECMAScript spec section 15.2.1, 15.3.1, 15.4.1, etc.
139 // * DISPATCH_CONSTRUCT:
140 // http://msdn2.microsoft.com/en-us/library/asd22sd4.aspx
141 result
= JS_CallFunction(js_engine_context_
, global_object
, ctor
, 0, NULL
,
144 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
145 LOG(("Could not call constructor function."));
149 if (JSVAL_IS_OBJECT(val
)) {
150 scoped_ptr
<JsObject
> retval(new JsObject
);
152 if (!retval
->SetObject(val
, GetContext())) {
153 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
154 LOG(("Could not assign to JsObject."));
157 return retval
.release();
159 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
160 LOG(("Constructor did not return an object"));
165 JsArray
* NewArray() {
166 JSObject
* array_object
= JS_NewArrayObject(GetContext(), 0, NULL
);
170 scoped_ptr
<JsArray
> js_array(new JsArray());
174 jsval array
= OBJECT_TO_JSVAL(array_object
);
175 if (!js_array
->SetArray(array
, GetContext()))
178 return js_array
.release();
181 virtual bool InvokeCallbackSpecialized(
182 const JsRootedCallback
*callback
, int argc
, jsval
*argv
,
183 JsRootedToken
**optional_alloc_retval
) = 0;
185 bool InvokeCallback(const JsRootedCallback
*callback
,
186 int argc
, JsParamToSend
*argv
,
187 JsRootedToken
**optional_alloc_retval
) {
188 assert(callback
&& (!argc
|| argv
));
190 if (JsTokenIsNullOrUndefined(callback
->token())) { return false; }
192 // Setup argument array.
193 scoped_array
<jsval
> js_engine_argv(new jsval
[argc
]);
194 for (int i
= 0; i
< argc
; ++i
)
195 ConvertJsParamToToken(argv
[i
], callback
->context(), &js_engine_argv
[i
]);
197 // Invoke the method.
198 return InvokeCallbackSpecialized(callback
, argc
, js_engine_argv
.get(),
199 optional_alloc_retval
);
202 // Add the provided handler to the notification list for the specified event.
203 virtual bool AddEventHandler(JsEventType event_type
,
204 JsEventHandlerInterface
*handler
) {
205 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
207 event_handlers_
[event_type
].insert(handler
);
211 // Remove the provided handler from the notification list for the specified
213 virtual bool RemoveEventHandler(JsEventType event_type
,
214 JsEventHandlerInterface
*handler
) {
215 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
217 event_handlers_
[event_type
].erase(handler
);
223 if (js_engine_context_
) {
224 JS_GC(js_engine_context_
);
230 // Alert all monitors that an event has occured.
231 void SendEvent(JsEventType event_type
) {
232 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
234 // Make a copy of the list of listeners, in case they change during the
236 std::vector
<JsEventHandlerInterface
*> monitors
;
237 monitors
.insert(monitors
.end(),
238 event_handlers_
[event_type
].begin(),
239 event_handlers_
[event_type
].end());
241 std::vector
<JsEventHandlerInterface
*>::iterator monitor
;
242 for (monitor
= monitors
.begin();
243 monitor
!= monitors
.end();
245 // Check that the listener hasn't been removed. This can occur if a
246 // listener removes another listener from the list.
247 if (event_handlers_
[event_type
].find(*monitor
) !=
248 event_handlers_
[event_type
].end()) {
249 (*monitor
)->HandleEvent(event_type
);
254 // Not using scoped_ptr even though it is possible because the cleanup code
255 // for JsRunner is tricky and would rather be explicit about the order things
257 JsContextWrapper
*alloc_js_wrapper_
;
258 JSContext
*js_engine_context_
;
261 std::set
<JsEventHandlerInterface
*> event_handlers_
[MAX_JSEVENTS
];
263 DISALLOW_EVIL_CONSTRUCTORS(JsRunnerBase
);
267 class JsRunner
: public JsRunnerBase
{
269 JsRunner() : error_handler_(NULL
), global_obj_(NULL
), js_runtime_(NULL
),
271 // TODO(aa): Consider moving initialization of JsRunners out since there is
272 // no way to detect errors in ctors.
273 if (!InitJavaScriptEngine())
276 // This creates a timer to run the garbage collector on a repeating
277 // interval, which is what Firefox does in
278 // source/dom/src/base/nsJSEnvironment.cpp.
280 gc_timer_
= do_CreateInstance("@mozilla.org/timer;1", &result
);
282 if (NS_SUCCEEDED(result
)) {
283 // Turning off idle causes the callback to be invoked in this thread,
284 // instead of in the Timer idle thread.
285 nsCOMPtr
<nsITimerInternal
> timer_internal(do_QueryInterface(gc_timer_
));
286 timer_internal
->SetIdle(false);
289 gc_timer_
->InitWithFuncCallback(GarbageCollectionCallback
,
291 kGarbageCollectionIntervalMsec
,
292 nsITimer::TYPE_REPEATING_SLACK
);
298 bool AddGlobal(const std::string16
&name
, IGeneric
*object
, gIID iface_id
);
299 bool Start(const std::string16
&full_script
);
301 bool Eval(const std::string16
&full_script
);
302 void SetErrorHandler(JsErrorHandlerInterface
*handler
) {
303 error_handler_
= handler
;
305 bool InvokeCallbackSpecialized(const JsRootedCallback
*callback
,
306 int argc
, jsval
*argv
,
307 JsRootedCallback
**optional_alloc_retval
);
310 bool InitJavaScriptEngine();
311 bool GetProtoFromIID(const nsIID iface_id
, JSObject
**proto
);
313 static void GarbageCollectionCallback(nsITimer
*timer
, void *context
);
314 static void JS_DLL_CALLBACK
JsErrorHandler(JSContext
*cx
, const char *message
,
315 JSErrorReport
*report
);
317 JsErrorHandlerInterface
*error_handler_
;
318 JSObject
*global_obj_
;
319 IIDToProtoMap proto_interfaces_
;
320 std::vector
<IGeneric
*> globals_
;
321 JSRuntime
*js_runtime_
;
322 JSScript
*js_script_
;
323 scoped_ptr
<JsRootedToken
> js_script_root_
;
324 nsCOMPtr
<nsITimer
> gc_timer_
;
326 DISALLOW_EVIL_CONSTRUCTORS(JsRunner
);
329 void JsRunner::GarbageCollectionCallback(nsITimer
*timer
,
331 JSContext
*cx
= reinterpret_cast<JSContext
*>(context
);
335 void JS_DLL_CALLBACK
JsRunner::JsErrorHandler(JSContext
*cx
,
337 JSErrorReport
*report
) {
338 JsRunner
*js_runner
= static_cast<JsRunner
*>(JS_GetContextPrivate(cx
));
339 if (js_runner
&& js_runner
->error_handler_
&& report
) {
340 JsErrorInfo error_info
;
341 error_info
.line
= report
->lineno
+ 1; // Reported lines start at zero.
343 // The error message can either be in the separate *message param or in
344 // *report->ucmessage. For example, running the following JS in a worker
345 // causes the separate message param to get used:
346 // throw new Error("foo")
347 // Other errors cause the report->ucmessage property to get used.
349 // Mozilla also does this, see:
350 // http://lxr.mozilla.org/mozilla1.8.0/source/dom/src/base/nsJSEnvironment.cpp#163
351 if (report
->ucmessage
) {
352 error_info
.message
= reinterpret_cast<const char16
*>(report
->ucmessage
);
353 } else if (message
) {
354 std::string16 message_str
;
355 if (UTF8ToString16(message
, &message_str
)) {
356 error_info
.message
= message_str
;
360 js_runner
->error_handler_
->HandleError(error_info
);
364 JsRunner::~JsRunner() {
365 // Alert modules that the engine is unloading.
366 SendEvent(JSEVENT_UNLOAD
);
369 // Stop garbage collection now.
374 // We need to remove the roots now, because they will be referencing an
375 // invalid context if we wait for the destructor.
376 if (alloc_js_wrapper_
)
377 alloc_js_wrapper_
->CleanupRoots();
379 std::vector
<IGeneric
*>::iterator global
;
380 for (global
= globals_
.begin(); global
!= globals_
.end(); ++global
) {
384 // Reset the scoped_ptr to unroot the script. This needs to be done before
385 // we destroy the context and runtime, so we can't wait for the destructor.
386 js_script_root_
.reset(NULL
);
388 if (js_engine_context_
) {
389 JS_DestroyContext(js_engine_context_
);
392 JS_DestroyRuntime(js_runtime_
);
395 // This has to occur after the context and runtime have been destroyed,
396 // because it maintains data structures that the JS engine requires.
397 // Specifically, any of the JSObjects stored in the JsWrapperData and the
398 // global_boj_ need to exist as long as the object in the JS Engine which
399 // they are linked to.
400 delete alloc_js_wrapper_
;
403 bool JsRunner::GetProtoFromIID(const nsIID iface_id
, JSObject
**proto
) {
404 IIDToProtoMap::iterator proto_interface
=
405 proto_interfaces_
.find(iface_id
);
406 if (proto_interface
!= proto_interfaces_
.end()) {
407 *proto
= proto_interface
->second
;
411 proto_interfaces_
[iface_id
] = NULL
;
412 proto_interface
= proto_interfaces_
.find(iface_id
);
414 // passing NULL for class_id and class_name prevents child workers
415 // from using "new CLASSNAME"
416 if (alloc_js_wrapper_
->DefineClass(&iface_id
,
419 &(proto_interface
->second
))) {
420 *proto
= proto_interface
->second
;
423 proto_interfaces_
.erase(iface_id
);
428 class JS_DestroyContextFunctor
{
430 inline void operator()(JSContext
* x
) const {
431 if (x
!= NULL
) { JS_DestroyContext(x
); }
434 typedef scoped_token
<JSContext
*, JS_DestroyContextFunctor
> scoped_jscontext_ptr
;
436 bool JsRunner::InitJavaScriptEngine() {
440 // To cleanup after failures we use scoped objects to manage everything that
441 // should be destroyed. On success we take ownership to avoid cleanup.
443 // These structs are static because they must live for duration of JS engine.
444 // SpiderMonkey README also suggests using static for one-off objects.
445 static JSClass global_class
= {
446 "Global", 0, // name, flags
447 JS_PropertyStub
, JS_PropertyStub
, // defineProperty, deleteProperty
448 JS_PropertyStub
, JS_PropertyStub
, // getProperty, setProperty
449 JS_EnumerateStub
, JS_ResolveStub
, // enum, resolve
450 JS_ConvertStub
, JS_FinalizeStub
// convert, finalize
455 // Instantiate a JavaScript engine
458 // Create a new runtime. If we instead use xpc/RuntimeService to get a
459 // runtime, strange things break (like eval).
460 const int kRuntimeMaxBytes
= 64 * 1024 * 1024; // mozilla/.../js.c uses 64 MB
461 js_runtime_
= JS_NewRuntime(kRuntimeMaxBytes
);
463 ExceptionManager::CaptureAndSendMinidump();
464 LOG(("Maximum thread count reached."));
468 const int kContextStackChunkSize
= 1024; // Firefox often uses 1024;
469 // also see js/src/readme.html
471 scoped_jscontext_ptr
cx(JS_NewContext(js_runtime_
, kContextStackChunkSize
));
472 if (!cx
.get()) { return false; }
473 // VAROBJFIX is recommended in /mozilla/js/src/jsapi.h
474 JS_SetOptions(cx
.get(), JS_GetOptions(cx
.get()) | JSOPTION_VAROBJFIX
);
476 // JS_SetErrorReporter takes a static callback, so we need
477 // JS_SetContextPrivate to later save the error in a per-worker location
478 JS_SetErrorReporter(cx
.get(), JsErrorHandler
);
479 JS_SetContextPrivate(cx
.get(), static_cast<void*>(this));
481 // must set this here to allow workerPool.forceGC() during child init
482 js_engine_context_
= cx
.get();
485 global_obj_
= JS_NewObject(cx
.get(), &global_class
, 0, 0);
487 if (!global_obj_
) { return false; }
488 js_ok
= JS_InitStandardClasses(cx
.get(), global_obj_
);
489 if (!js_ok
) { return false; }
490 // Note: an alternative is to lazily define the "standard classes" (which
491 // include things like eval). To do that, change JS_InitStandardClasses
492 // to JS_SetGlobalObject, and add handlers for Enumerate and Resolve in
493 // global_class. See /mozilla/js/src/js.c for sample code.
496 // Define classes in the JSContext
499 // first need to create a JsWrapperManager for this thread
500 scoped_ptr
<JsContextWrapper
> js_wrapper(new JsContextWrapper(cx
.get(),
503 js_engine_context_
= cx
.release();
504 alloc_js_wrapper_
= js_wrapper
.release();
507 const nsIID iface_id
;
508 JSObject
*proto_obj
; // gets set by code below
510 // TODO(cprince): Unify the interface lists here and in GearsFactory.
511 // Could share code, or could query GearsFactory.
512 {GEARSFACTORYINTERFACE_IID
, NULL
},
514 {GEARSWORKERPOOLINTERFACE_IID
, NULL
},
516 {GEARSDATABASEINTERFACE_IID
, NULL
},
517 {GEARSRESULTSETINTERFACE_IID
, NULL
},
519 {GEARSDESKTOPINTERFACE_IID
, NULL
},
521 {GEARSLOCALSERVERINTERFACE_IID
, NULL
},
522 {GEARSMANAGEDRESOURCESTOREINTERFACE_IID
, NULL
},
523 {GEARSRESOURCESTOREINTERFACE_IID
, NULL
},
524 // GEARSFILESUBMITTERINTERFACE_IID can never be created in a child worker
526 {GEARSTIMERINTERFACE_IID
, NULL
},
528 {GEARSHTTPREQUESTINTERFACE_IID
, NULL
},
529 #ifdef OFFICIAL_BUILD
530 // The Image and Blog APIs have not been finalized for official builds
533 {GEARSBLOBINTERFACE_IID
, NULL
},
535 {GEARSIMAGEINTERFACE_IID
, NULL
},
538 {GEARSCONSOLEINTERFACE_IID
, NULL
}
540 const int num_classes
= sizeof(classes
) / sizeof(classes
[0]);
542 for (int i
= 0; i
< num_classes
; ++i
) {
543 // passing NULL for class_id and class_name prevents child workers
544 // from using "new CLASSNAME"
545 succeeded
= GetProtoFromIID(classes
[i
].iface_id
, &classes
[i
].proto_obj
);
546 if (!succeeded
) { return false; }
550 // Do it here to trigger potential GC bugs in our code.
551 JS_GC(js_engine_context_
);
554 return true; // succeeded
557 bool JsRunner::AddGlobal(const std::string16
&name
,
560 JSObject
*proto_object
;
561 if (!GetProtoFromIID(iface_id
, &proto_object
)) {
565 if (!alloc_js_wrapper_
->DefineGlobal(proto_object
, object
, name
.c_str())) {
569 globals_
.push_back(object
);
570 NS_ADDREF(globals_
.back());
572 return true; // succeeded
575 bool JsRunner::Start(const std::string16
&full_script
) {
577 // Add script code to the engine instance
580 uintN line_number_start
= 0;
581 js_script_
= JS_CompileUCScript(
582 js_engine_context_
, global_obj_
,
583 reinterpret_cast<const jschar
*>(full_script
.c_str()),
584 full_script
.length(),
585 "script", line_number_start
);
586 if (!js_script_
) { return false; }
588 // we must root any script returned by JS_Compile* (see jsapi.h)
589 JSObject
*compiled_script_obj
= JS_NewScriptObject(js_engine_context_
,
591 if (!compiled_script_obj
) { return false; }
592 js_script_root_
.reset(new JsRootedToken(
594 OBJECT_TO_JSVAL(compiled_script_obj
)));
598 // Start the engine running
602 JSBool js_ok
= JS_ExecuteScript(js_engine_context_
, global_obj_
,
603 js_script_
, &return_string
);
604 if (!js_ok
) { return false; }
609 bool JsRunner::Stop() {
610 // TODO(zork): Implement
614 bool JsRunner::Eval(const std::string16
&script
) {
615 JSObject
*object
= JS_GetGlobalObject(js_engine_context_
);
617 uintN line_number_start
= 0;
619 JSBool js_ok
= JS_EvaluateUCScript(
622 reinterpret_cast<const jschar
*>(script
.c_str()),
624 "script", line_number_start
,
626 if (!js_ok
) { return false; }
630 bool JsRunner::InvokeCallbackSpecialized(
631 const JsRootedCallback
*callback
, int argc
, jsval
*argv
,
632 JsRootedToken
**optional_alloc_retval
) {
634 JSBool result
= JS_CallFunctionValue(
636 JS_GetGlobalObject(callback
->context()),
637 callback
->token(), argc
, argv
, &retval
);
638 if (result
== JS_FALSE
) { return false; }
640 if (optional_alloc_retval
) {
641 // Note: A valid jsval is returned no matter what the javascript function
642 // returns. If the javascript function returns nothing, or explicitly
643 // returns <undefined>, the the jsval will be JSVAL_IS_VOID. If the
644 // javascript function returns <null>, then the jsval will be JSVAL_IS_NULL.
645 // Always returning a JsRootedToken should allow us to coerce these values
646 // to other types correctly in the future.
647 *optional_alloc_retval
= new JsRootedToken(js_engine_context_
, retval
);
653 // Provides the same interface as JsRunner, but for the normal JavaScript engine
654 // that runs in HTML pages.
655 class DocumentJsRunner
: public JsRunnerBase
{
657 DocumentJsRunner(IGeneric
*base
, JsContextPtr context
) {
658 js_engine_context_
= context
;
659 alloc_js_wrapper_
= new JsContextWrapper(context
,
660 JS_GetGlobalObject(context
));
663 ~DocumentJsRunner() {
664 if (alloc_js_wrapper_
)
665 delete alloc_js_wrapper_
;
668 bool AddGlobal(const std::string16
&name
, IGeneric
*object
, gIID iface_id
) {
669 // TODO(zork): Add this functionality to DocumentJsRunner.
672 void SetErrorHandler(JsErrorHandlerInterface
*handler
) {
673 assert(false); // This should not be called on DocumentJsRunner.
675 bool Start(const std::string16
&full_script
) {
676 assert(false); // This should not be called on DocumentJsRunner.
680 assert(false); // This should not be called on DocumentJsRunner.
683 bool Eval(const std::string16
&full_script
);
684 bool InvokeCallbackSpecialized(const JsRootedCallback
*callback
,
685 int argc
, jsval
*argv
,
686 JsRootedToken
**optional_alloc_retval
);
687 bool AddEventHandler(JsEventType event_type
,
688 JsEventHandlerInterface
*handler
);
691 static void HandleEventUnload(void *user_param
); // Callback for 'onunload'
693 scoped_ptr
<HtmlEventMonitor
> unload_monitor_
; // For 'onunload' notifications
694 DISALLOW_EVIL_CONSTRUCTORS(DocumentJsRunner
);
698 bool DocumentJsRunner::Eval(const std::string16
&script
) {
699 JSObject
*object
= JS_GetGlobalObject(js_engine_context_
);
700 if (!object
) { return false; }
702 // To eval the script, we need the JSPrincipals to be acquired through
703 // nsIPrincipal. nsIPrincipal can be queried through the
704 // nsIScriptObjectPrincipal interface on the Script Global Object. In order
705 // to get the Script Global Object, we need to request the private data
706 // associated with the global JSObject on the current context.
707 nsCOMPtr
<nsIScriptGlobalObject
> sgo
;
708 nsISupports
*priv
= reinterpret_cast<nsISupports
*>(JS_GetPrivate(
711 nsCOMPtr
<nsIXPConnectWrappedNative
> wrapped_native
= do_QueryInterface(priv
);
713 if (wrapped_native
) {
714 // The global object is a XPConnect wrapped native, the native in
715 // the wrapper might be the nsIScriptGlobalObject.
716 sgo
= do_QueryWrappedNative(wrapped_native
);
718 sgo
= do_QueryInterface(priv
);
721 JSPrincipals
*jsprin
;
724 nsCOMPtr
<nsIScriptObjectPrincipal
> obj_prin
= do_QueryInterface(sgo
, &nr
);
725 if (NS_FAILED(nr
)) { return false; }
727 nsIPrincipal
*principal
= obj_prin
->GetPrincipal();
728 if (!principal
) { return false; }
730 principal
->GetJSPrincipals(js_engine_context_
, &jsprin
);
732 // Set up the JS stack so that our context is on top. This is needed to
733 // play nicely with plugins that access the context stack, such as Firebug.
734 nsCOMPtr
<nsIJSContextStack
> stack
=
735 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
736 if (!stack
) { return false; }
738 stack
->Push(js_engine_context_
);
740 uintN line_number_start
= 0;
742 JSBool js_ok
= JS_EvaluateUCScriptForPrincipals(
743 js_engine_context_
, object
, jsprin
,
744 reinterpret_cast<const jschar
*>(script
.c_str()),
745 script
.length(), "script", line_number_start
, &rval
);
747 // Restore the context stack.
751 // Decrements ref count on jsprin (Was added in GetJSPrincipals()).
752 (void)JSPRINCIPALS_DROP(js_engine_context_
, jsprin
);
753 if (!js_ok
) { return false; }
757 bool DocumentJsRunner::AddEventHandler(JsEventType event_type
,
758 JsEventHandlerInterface
*handler
) {
759 if (event_type
== JSEVENT_UNLOAD
) {
760 // Monitor 'onunload' to send the unload event when the page goes away.
761 if (unload_monitor_
== NULL
) {
762 unload_monitor_
.reset(new HtmlEventMonitor(kEventUnload
,
765 nsCOMPtr
<nsIDOMEventTarget
> event_source
;
766 if (NS_SUCCEEDED(DOMUtils::GetWindowEventTarget(
767 getter_AddRefs(event_source
))))
769 unload_monitor_
->Start(event_source
);
776 return JsRunnerBase::AddEventHandler(event_type
, handler
);
779 void DocumentJsRunner::HandleEventUnload(void *user_param
) {
780 static_cast<DocumentJsRunner
*>(user_param
)->SendEvent(JSEVENT_UNLOAD
);
783 bool DocumentJsRunner::InvokeCallbackSpecialized(
784 const JsRootedCallback
*callback
,
785 int argc
, jsval
*argv
,
786 JsRootedToken
**optional_alloc_retval
) {
787 // When invoking a callback on the document context, we must go through
788 // nsIScriptContext->CallEventHandler because it sets up certain state that
789 // the browser error handler expects to find if there is an error. Without
790 // this, crashes happen. For more information, see:
791 // http://code.google.com/p/google-gears/issues/detail?id=32
792 nsCOMPtr
<nsIScriptContext
> sc
;
793 sc
= GetScriptContextFromJSContext(callback
->context());
794 if (!sc
) { return false; }
797 nsresult result
= sc
->CallEventHandler(
798 JS_GetGlobalObject(callback
->context()),
799 JSVAL_TO_OBJECT(callback
->token()),
800 argc
, argv
, &retval
);
801 if (NS_FAILED(result
)) { return false; }
803 if (optional_alloc_retval
) {
804 // See note in JsRunner::InvokeCallbackSpecialized about return values of
805 // javascript functions.
806 *optional_alloc_retval
= new JsRootedToken(js_engine_context_
, retval
);
813 JsRunnerInterface
* NewJsRunner() {
814 return static_cast<JsRunnerInterface
*>(new JsRunner());
817 JsRunnerInterface
* NewDocumentJsRunner(IGeneric
*base
, JsContextPtr context
) {
818 return static_cast<JsRunnerInterface
*>(new DocumentJsRunner(base
, context
));