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>
38 #include "gears/base/common/js_runner.h"
40 #include "ff/genfiles/blob_ff.h"
41 #include "ff/genfiles/channel.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"
46 #include "ff/genfiles/localserver.h"
47 #include "ff/genfiles/timer_ff.h"
48 #include "ff/genfiles/workerpool.h"
49 #include "gears/base/common/common.h" // for DISALLOW_EVIL_CONSTRUCTORS
50 #include "gears/base/common/exception_handler_win32.h"
51 #include "gears/base/common/html_event_monitor.h"
52 #include "gears/base/common/js_runner_ff_marshaling.h"
53 #include "gears/base/common/scoped_token.h"
54 #include "gears/base/common/string_utils.h"
55 #include "gears/base/firefox/dom_utils.h"
56 #include "gears/factory/firefox/factory.h"
59 #include "ff/genfiles/test_ff.h"
62 // Internal base class used to share some code between DocumentJsRunner and
63 // JsRunner. Do not override these methods from JsRunner or DocumentJsRunner.
64 // Either share the code here, or move it to those two classes if it's
66 class JsRunnerBase
: public JsRunnerInterface
{
68 JsRunnerBase() : js_engine_context_(NULL
) {}
70 JsContextPtr
GetContext() {
71 return js_engine_context_
;
74 JsObject
*NewObject(const char16
*optional_global_ctor_name
,
75 bool dump_on_error
= false) {
76 if (!js_engine_context_
) {
77 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
78 LOG(("Could not get JavaScript engine context."));
82 JSObject
*global_object
= JS_GetGlobalObject(js_engine_context_
);
84 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
85 LOG(("Could not get global object from script engine."));
89 std::string ctor_name_utf8
;
90 if (optional_global_ctor_name
) {
91 if (!String16ToUTF8(optional_global_ctor_name
, &ctor_name_utf8
)) {
92 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
93 LOG(("Could not convert constructor name."));
97 ctor_name_utf8
= "Object";
100 jsval val
= INT_TO_JSVAL(0);
101 JSBool result
= JS_GetProperty(js_engine_context_
, global_object
,
102 ctor_name_utf8
.c_str(), &val
);
104 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
105 LOG(("Could not get constructor property from global object."));
109 JSFunction
*ctor
= JS_ValueToFunction(js_engine_context_
, val
);
111 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
112 LOG(("Could not convert constructor property to function."));
116 // NOTE: We are calling the specified function here as a regular function,
117 // not as a constructor. I could not find a way to call a function as a
118 // constructor using JSAPI other than JS_ConstructObject which takes
119 // arguments I don't know how to provide. Ideally, there would be something
120 // like DISPATCH_CONSTRUCT in IE.
122 // This is OK for the built-in constructors that we want to call (such as
123 // "Error", "Object", etc) because those objects are specified to behave as
124 // constructors even without the 'new' keyword.
126 // For more information, see:
127 // * ECMAScript spec section 15.2.1, 15.3.1, 15.4.1, etc.
128 // * DISPATCH_CONSTRUCT:
129 // http://msdn2.microsoft.com/en-us/library/asd22sd4.aspx
130 result
= JS_CallFunction(js_engine_context_
, global_object
, ctor
, 0, NULL
,
133 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
134 LOG(("Could not call constructor function."));
138 if (JSVAL_IS_OBJECT(val
)) {
139 scoped_ptr
<JsObject
> retval(new JsObject
);
141 if (!retval
->SetObject(val
, GetContext())) {
142 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
143 LOG(("Could not assign to JsObject."));
146 return retval
.release();
148 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
149 LOG(("Constructor did not return an object"));
154 JsArray
* NewArray() {
155 JSObject
* array_object
= JS_NewArrayObject(GetContext(), 0, NULL
);
159 scoped_ptr
<JsArray
> js_array(new JsArray());
163 jsval array
= OBJECT_TO_JSVAL(array_object
);
164 if (!js_array
->SetArray(array
, GetContext()))
167 return js_array
.release();
170 virtual bool InvokeCallbackSpecialized(
171 const JsRootedCallback
*callback
, int argc
, jsval
*argv
,
172 JsRootedToken
**optional_alloc_retval
) = 0;
174 bool InvokeCallback(const JsRootedCallback
*callback
,
175 int argc
, JsParamToSend
*argv
,
176 JsRootedToken
**optional_alloc_retval
) {
177 assert(callback
&& (!argc
|| argv
));
179 if (callback
->IsNullOrUndefined()) { return false; }
181 // Setup argument array.
182 scoped_array
<jsval
> js_engine_argv(new jsval
[argc
]);
183 for (int i
= 0; i
< argc
; ++i
)
184 ConvertJsParamToToken(argv
[i
], callback
->context(), &js_engine_argv
[i
]);
186 // Invoke the method.
187 return InvokeCallbackSpecialized(callback
, argc
, js_engine_argv
.get(),
188 optional_alloc_retval
);
191 // Add the provided handler to the notification list for the specified event.
192 virtual bool AddEventHandler(JsEventType event_type
,
193 JsEventHandlerInterface
*handler
) {
194 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
196 event_handlers_
[event_type
].insert(handler
);
200 // Remove the provided handler from the notification list for the specified
202 virtual bool RemoveEventHandler(JsEventType event_type
,
203 JsEventHandlerInterface
*handler
) {
204 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
206 event_handlers_
[event_type
].erase(handler
);
212 if (js_engine_context_
) {
213 JS_GC(js_engine_context_
);
219 // Alert all monitors that an event has occured.
220 void SendEvent(JsEventType event_type
) {
221 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
223 // Make a copy of the list of listeners, in case they change during the
225 std::vector
<JsEventHandlerInterface
*> monitors
;
226 monitors
.insert(monitors
.end(),
227 event_handlers_
[event_type
].begin(),
228 event_handlers_
[event_type
].end());
230 std::vector
<JsEventHandlerInterface
*>::iterator monitor
;
231 for (monitor
= monitors
.begin();
232 monitor
!= monitors
.end();
234 // Check that the listener hasn't been removed. This can occur if a
235 // listener removes another listener from the list.
236 if (event_handlers_
[event_type
].find(*monitor
) !=
237 event_handlers_
[event_type
].end()) {
238 (*monitor
)->HandleEvent(event_type
);
243 JSContext
*js_engine_context_
;
246 std::set
<JsEventHandlerInterface
*> event_handlers_
[MAX_JSEVENTS
];
248 DISALLOW_EVIL_CONSTRUCTORS(JsRunnerBase
);
252 class JsRunner
: public JsRunnerBase
{
254 JsRunner() : alloc_js_wrapper_(NULL
),
255 error_handler_(NULL
), global_obj_(NULL
), js_runtime_(NULL
),
257 InitJavaScriptEngine();
261 bool AddGlobal(const std::string16
&name
, IGeneric
*object
, gIID iface_id
);
262 bool Start(const std::string16
&full_script
);
264 bool Eval(const std::string16
&full_script
);
265 void SetErrorHandler(JsErrorHandlerInterface
*handler
) {
266 error_handler_
= handler
;
268 bool InvokeCallbackSpecialized(const JsRootedCallback
*callback
,
269 int argc
, jsval
*argv
,
270 JsRootedCallback
**optional_alloc_retval
);
273 bool InitJavaScriptEngine();
275 bool GetProtoFromIID(const nsIID iface_id
, JSObject
**proto
);
276 static void JS_DLL_CALLBACK
JsErrorHandler(JSContext
*cx
, const char *message
,
277 JSErrorReport
*report
);
279 JsContextWrapper
*alloc_js_wrapper_
; // (created in thread)
280 JsErrorHandlerInterface
*error_handler_
;
281 JSObject
*global_obj_
;
282 IIDToProtoMap proto_interfaces_
;
283 std::vector
<IGeneric
*> globals_
;
284 JSRuntime
*js_runtime_
;
285 JSScript
*js_script_
;
286 scoped_ptr
<JsRootedToken
> js_script_root_
;
288 DISALLOW_EVIL_CONSTRUCTORS(JsRunner
);
291 void JS_DLL_CALLBACK
JsRunner::JsErrorHandler(JSContext
*cx
,
293 JSErrorReport
*report
) {
294 JsRunner
*js_runner
= static_cast<JsRunner
*>(JS_GetContextPrivate(cx
));
295 if (js_runner
&& js_runner
->error_handler_
&& report
) {
296 JsErrorInfo error_info
;
297 error_info
.line
= report
->lineno
+ 1; // Reported lines start at zero.
299 // The error message can either be in the separate *message param or in
300 // *report->ucmessage. For example, running the following JS in a worker
301 // causes the separate message param to get used:
302 // throw new Error("foo")
303 // Other errors cause the report->ucmessage property to get used.
305 // Mozilla also does this, see:
306 // http://lxr.mozilla.org/mozilla1.8.0/source/dom/src/base/nsJSEnvironment.cpp#163
307 if (report
->ucmessage
) {
308 error_info
.message
= reinterpret_cast<const char16
*>(report
->ucmessage
);
309 } else if (message
) {
310 std::string16 message_str
;
311 if (UTF8ToString16(message
, &message_str
)) {
312 error_info
.message
= message_str
;
316 js_runner
->error_handler_
->HandleError(error_info
);
320 JsRunner::~JsRunner() {
321 // Alert modules that the engine is unloading.
322 SendEvent(JSEVENT_UNLOAD
);
324 // We need to remove the roots now, because they will be referencing an
325 // invalid context if we wait for the destructor.
326 if (alloc_js_wrapper_
)
327 alloc_js_wrapper_
->CleanupRoots();
329 std::vector
<IGeneric
*>::iterator global
;
330 for (global
= globals_
.begin(); global
!= globals_
.end(); ++global
) {
334 // Reset the scoped_ptr to unroot the script. This needs to be done before
335 // we destroy the context and runtime, so we can't wait for the destructor.
336 js_script_root_
.reset(NULL
);
338 if (js_engine_context_
) {
339 JS_DestroyContext(js_engine_context_
);
342 JS_DestroyRuntime(js_runtime_
);
345 // This has to occur after the context and runtime have been destroyed,
346 // because it maintains data structures that the JS engine requires.
347 // Specifically, any of the JSObjects stored in the JsWrapperData and the
348 // global_boj_ need to exist as long as the object in the JS Engine which
349 // they are linked to.
350 delete alloc_js_wrapper_
;
353 bool JsRunner::GetProtoFromIID(const nsIID iface_id
, JSObject
**proto
) {
354 IIDToProtoMap::iterator proto_interface
=
355 proto_interfaces_
.find(iface_id
);
356 if (proto_interface
!= proto_interfaces_
.end()) {
357 *proto
= proto_interface
->second
;
361 proto_interfaces_
[iface_id
] = NULL
;
362 proto_interface
= proto_interfaces_
.find(iface_id
);
364 // passing NULL for class_id and class_name prevents child workers
365 // from using "new CLASSNAME"
366 if (alloc_js_wrapper_
->DefineClass(&iface_id
,
369 &(proto_interface
->second
))) {
370 *proto
= proto_interface
->second
;
373 proto_interfaces_
.erase(iface_id
);
378 class JS_DestroyContextFunctor
{
380 inline void operator()(JSContext
* x
) const {
381 if (x
!= NULL
) { JS_DestroyContext(x
); }
384 typedef scoped_token
<JSContext
*, JS_DestroyContextFunctor
> scoped_jscontext_ptr
;
386 bool JsRunner::InitJavaScriptEngine() {
390 // To cleanup after failures we use scoped objects to manage everything that
391 // should be destroyed. On success we take ownership to avoid cleanup.
393 // These structs are static because they must live for duration of JS engine.
394 // SpiderMonkey README also suggests using static for one-off objects.
395 static JSClass global_class
= {
396 "Global", 0, // name, flags
397 JS_PropertyStub
, JS_PropertyStub
, // defineProperty, deleteProperty
398 JS_PropertyStub
, JS_PropertyStub
, // getProperty, setProperty
399 JS_EnumerateStub
, JS_ResolveStub
, // enum, resolve
400 JS_ConvertStub
, JS_FinalizeStub
// convert, finalize
405 // Instantiate a JavaScript engine
408 // Create a new runtime. If we instead use xpc/RuntimeService to get a
409 // runtime, strange things break (like eval).
410 const int kRuntimeMaxBytes
= 64 * 1024 * 1024; // mozilla/.../js.c uses 64 MB
411 js_runtime_
= JS_NewRuntime(kRuntimeMaxBytes
);
412 if (!js_runtime_
) { return false; }
414 const int kContextStackChunkSize
= 1024; // Firefox often uses 1024;
415 // also see js/src/readme.html
417 scoped_jscontext_ptr
cx(JS_NewContext(js_runtime_
, kContextStackChunkSize
));
418 if (!cx
.get()) { return false; }
419 // VAROBJFIX is recommended in /mozilla/js/src/jsapi.h
420 JS_SetOptions(cx
.get(), JS_GetOptions(cx
.get()) | JSOPTION_VAROBJFIX
);
422 // JS_SetErrorReporter takes a static callback, so we need
423 // JS_SetContextPrivate to later save the error in a per-worker location
424 JS_SetErrorReporter(cx
.get(), JsErrorHandler
);
425 JS_SetContextPrivate(cx
.get(), static_cast<void*>(this));
427 // must set this here to allow workerPool.forceGC() during child init
428 js_engine_context_
= cx
.get();
431 global_obj_
= JS_NewObject(cx
.get(), &global_class
, 0, 0);
433 if (!global_obj_
) { return false; }
434 js_ok
= JS_InitStandardClasses(cx
.get(), global_obj_
);
435 if (!js_ok
) { return false; }
436 // Note: an alternative is to lazily define the "standard classes" (which
437 // include things like eval). To do that, change JS_InitStandardClasses
438 // to JS_SetGlobalObject, and add handlers for Enumerate and Resolve in
439 // global_class. See /mozilla/js/src/js.c for sample code.
442 // Define classes in the JSContext
445 // first need to create a JsWrapperManager for this thread
446 scoped_ptr
<JsContextWrapper
> js_wrapper(new JsContextWrapper(cx
.get(),
449 js_engine_context_
= cx
.release();
450 alloc_js_wrapper_
= js_wrapper
.release();
453 const nsIID iface_id
;
454 JSObject
*proto_obj
; // gets set by code below
456 // TODO(cprince): Unify the interface lists here and in GearsFactory.
457 // Could share code, or could query GearsFactory.
458 {GEARSFACTORYINTERFACE_IID
, NULL
},
460 {GEARSWORKERPOOLINTERFACE_IID
, NULL
},
462 {GEARSDATABASEINTERFACE_IID
, NULL
},
463 {GEARSRESULTSETINTERFACE_IID
, NULL
},
465 {GEARSDESKTOPINTERFACE_IID
, NULL
},
467 {GEARSLOCALSERVERINTERFACE_IID
, NULL
},
468 {GEARSMANAGEDRESOURCESTOREINTERFACE_IID
, NULL
},
469 {GEARSRESOURCESTOREINTERFACE_IID
, NULL
},
470 // GEARSFILESUBMITTERINTERFACE_IID can never be created in a child worker
473 {GEARSTESTINTERFACE_IID
, NULL
},
476 {GEARSBLOBINTERFACE_IID
, NULL
},
478 {GEARSTIMERINTERFACE_IID
, NULL
},
480 {GEARSHTTPREQUESTINTERFACE_IID
, NULL
},
482 {GEARSCHANNELINTERFACE_IID
, NULL
},
484 {GEARSCONSOLEINTERFACE_IID
, NULL
}
486 const int num_classes
= sizeof(classes
) / sizeof(classes
[0]);
488 for (int i
= 0; i
< num_classes
; ++i
) {
489 // passing NULL for class_id and class_name prevents child workers
490 // from using "new CLASSNAME"
491 succeeded
= GetProtoFromIID(classes
[i
].iface_id
, &classes
[i
].proto_obj
);
492 if (!succeeded
) { return false; }
496 // Do it here to trigger potential GC bugs in our code.
497 JS_GC(js_engine_context_
);
500 return true; // succeeded
503 bool JsRunner::AddGlobal(const std::string16
&name
,
506 JSObject
*proto_object
;
507 if (!GetProtoFromIID(iface_id
, &proto_object
)) {
511 if (!alloc_js_wrapper_
->DefineGlobal(proto_object
, object
, name
.c_str())) {
515 globals_
.push_back(object
);
516 NS_ADDREF(globals_
.back());
518 return true; // succeeded
521 bool JsRunner::Start(const std::string16
&full_script
) {
523 // Add script code to the engine instance
526 uintN line_number_start
= 0;
527 js_script_
= JS_CompileUCScript(
528 js_engine_context_
, global_obj_
,
529 reinterpret_cast<const jschar
*>(full_script
.c_str()),
530 full_script
.length(),
531 "script", line_number_start
);
532 if (!js_script_
) { return false; }
534 // we must root any script returned by JS_Compile* (see jsapi.h)
535 JSObject
*compiled_script_obj
= JS_NewScriptObject(js_engine_context_
,
537 if (!compiled_script_obj
) { return false; }
538 js_script_root_
.reset(new JsRootedToken(
540 OBJECT_TO_JSVAL(compiled_script_obj
)));
544 // Start the engine running
548 JSBool js_ok
= JS_ExecuteScript(js_engine_context_
, global_obj_
,
549 js_script_
, &return_string
);
550 if (!js_ok
) { return false; }
555 bool JsRunner::Stop() {
556 // TODO(zork): Implement
560 bool JsRunner::Eval(const std::string16
&script
) {
561 JSObject
*object
= JS_GetGlobalObject(js_engine_context_
);
563 uintN line_number_start
= 0;
565 JSBool js_ok
= JS_EvaluateUCScript(
568 reinterpret_cast<const jschar
*>(script
.c_str()),
570 "script", line_number_start
,
572 if (!js_ok
) { return false; }
576 bool JsRunner::InvokeCallbackSpecialized(
577 const JsRootedCallback
*callback
, int argc
, jsval
*argv
,
578 JsRootedToken
**optional_alloc_retval
) {
580 JSBool result
= JS_CallFunctionValue(
582 JS_GetGlobalObject(callback
->context()),
583 callback
->token(), argc
, argv
, &retval
);
584 if (result
== JS_FALSE
) { return false; }
586 if (optional_alloc_retval
) {
587 // Note: A valid jsval is returned no matter what the javascript function
588 // returns. If the javascript function returns nothing, or explicitly
589 // returns <undefined>, the the jsval will be JSVAL_IS_VOID. If the
590 // javascript function returns <null>, then the jsval will be JSVAL_IS_NULL.
591 // Always returning a JsRootedToken should allow us to coerce these values
592 // to other types correctly in the future.
593 *optional_alloc_retval
= new JsRootedToken(js_engine_context_
, retval
);
599 // Provides the same interface as JsRunner, but for the normal JavaScript engine
600 // that runs in HTML pages.
601 class DocumentJsRunner
: public JsRunnerBase
{
603 DocumentJsRunner(IGeneric
*base
, JsContextPtr context
) {
604 js_engine_context_
= context
;
607 ~DocumentJsRunner() {
610 bool AddGlobal(const std::string16
&name
, IGeneric
*object
, gIID iface_id
) {
611 // TODO(zork): Add this functionality to DocumentJsRunner.
614 void SetErrorHandler(JsErrorHandlerInterface
*handler
) {
615 assert(false); // This should not be called on DocumentJsRunner.
617 bool Start(const std::string16
&full_script
) {
618 assert(false); // This should not be called on DocumentJsRunner.
622 assert(false); // This should not be called on DocumentJsRunner.
625 bool Eval(const std::string16
&full_script
);
626 bool InvokeCallbackSpecialized(const JsRootedCallback
*callback
,
627 int argc
, jsval
*argv
,
628 JsRootedToken
**optional_alloc_retval
);
629 bool AddEventHandler(JsEventType event_type
,
630 JsEventHandlerInterface
*handler
);
633 static void HandleEventUnload(void *user_param
); // Callback for 'onunload'
635 scoped_ptr
<HtmlEventMonitor
> unload_monitor_
; // For 'onunload' notifications
636 DISALLOW_EVIL_CONSTRUCTORS(DocumentJsRunner
);
640 bool DocumentJsRunner::Eval(const std::string16
&script
) {
641 JSObject
*object
= JS_GetGlobalObject(js_engine_context_
);
642 if (!object
) { return false; }
644 // To eval the script, we need the JSPrincipals to be acquired through
645 // nsIPrincipal. nsIPrincipal can be queried through the
646 // nsIScriptObjectPrincipal interface on the Script Global Object. In order
647 // to get the Script Global Object, we need to request the private data
648 // associated with the global JSObject on the current context.
649 nsCOMPtr
<nsIScriptGlobalObject
> sgo
;
650 nsISupports
*priv
= reinterpret_cast<nsISupports
*>(JS_GetPrivate(
653 nsCOMPtr
<nsIXPConnectWrappedNative
> wrapped_native
= do_QueryInterface(priv
);
655 if (wrapped_native
) {
656 // The global object is a XPConnect wrapped native, the native in
657 // the wrapper might be the nsIScriptGlobalObject.
658 sgo
= do_QueryWrappedNative(wrapped_native
);
660 sgo
= do_QueryInterface(priv
);
663 JSPrincipals
*jsprin
;
666 nsCOMPtr
<nsIScriptObjectPrincipal
> obj_prin
= do_QueryInterface(sgo
, &nr
);
667 if (NS_FAILED(nr
)) { return false; }
669 nsIPrincipal
*principal
= obj_prin
->GetPrincipal();
670 if (!principal
) { return false; }
672 principal
->GetJSPrincipals(js_engine_context_
, &jsprin
);
674 // Set up the JS stack so that our context is on top. This is needed to
675 // play nicely with plugins that access the context stack, such as Firebug.
676 nsCOMPtr
<nsIJSContextStack
> stack
=
677 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
678 if (!stack
) { return false; }
680 stack
->Push(js_engine_context_
);
682 uintN line_number_start
= 0;
684 JSBool js_ok
= JS_EvaluateUCScriptForPrincipals(
685 js_engine_context_
, object
, jsprin
,
686 reinterpret_cast<const jschar
*>(script
.c_str()),
687 script
.length(), "script", line_number_start
, &rval
);
689 // Restore the context stack.
693 // Decrements ref count on jsprin (Was added in GetJSPrincipals()).
694 (void)JSPRINCIPALS_DROP(js_engine_context_
, jsprin
);
695 if (!js_ok
) { return false; }
699 bool DocumentJsRunner::AddEventHandler(JsEventType event_type
,
700 JsEventHandlerInterface
*handler
) {
701 if (event_type
== JSEVENT_UNLOAD
) {
702 // Monitor 'onunload' to send the unload event when the page goes away.
703 if (unload_monitor_
== NULL
) {
704 unload_monitor_
.reset(new HtmlEventMonitor(kEventUnload
,
707 nsCOMPtr
<nsIDOMEventTarget
> event_source
;
708 if (NS_SUCCEEDED(DOMUtils::GetWindowEventTarget(
709 getter_AddRefs(event_source
))))
711 unload_monitor_
->Start(event_source
);
718 return JsRunnerBase::AddEventHandler(event_type
, handler
);
721 void DocumentJsRunner::HandleEventUnload(void *user_param
) {
722 static_cast<DocumentJsRunner
*>(user_param
)->SendEvent(JSEVENT_UNLOAD
);
725 bool DocumentJsRunner::InvokeCallbackSpecialized(
726 const JsRootedCallback
*callback
,
727 int argc
, jsval
*argv
,
728 JsRootedToken
**optional_alloc_retval
) {
729 // When invoking a callback on the document context, we must go through
730 // nsIScriptContext->CallEventHandler because it sets up certain state that
731 // the browser error handler expects to find if there is an error. Without
732 // this, crashes happen. For more information, see:
733 // http://code.google.com/p/google-gears/issues/detail?id=32
734 nsCOMPtr
<nsIScriptContext
> sc
;
735 sc
= GetScriptContextFromJSContext(callback
->context());
736 if (!sc
) { return false; }
739 nsresult result
= sc
->CallEventHandler(
740 JS_GetGlobalObject(callback
->context()),
741 JSVAL_TO_OBJECT(callback
->token()),
742 argc
, argv
, &retval
);
743 if (NS_FAILED(result
)) { return false; }
745 if (optional_alloc_retval
) {
746 // See note in JsRunner::InvokeCallbackSpecialized about return values of
747 // javascript functions.
748 *optional_alloc_retval
= new JsRootedToken(js_engine_context_
, retval
);
755 JsRunnerInterface
* NewJsRunner() {
756 return static_cast<JsRunnerInterface
*>(new JsRunner());
759 JsRunnerInterface
* NewDocumentJsRunner(IGeneric
*base
, JsContextPtr context
) {
760 return static_cast<JsRunnerInterface
*>(new DocumentJsRunner(base
, context
));