[Author: kevinww]
[google-gears.git] / gears / base / common / js_runner_ff.cc
blob68aee709696ecd4885f013de621b008bde02c6b1
1 // Copyright 2007, Google Inc.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
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.
26 #include <assert.h>
27 #include <map>
28 #include <set>
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"
58 #ifdef DEBUG
59 #include "ff/genfiles/test_ff.h"
60 #endif
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
65 // different.
66 class JsRunnerBase : public JsRunnerInterface {
67 public:
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."));
79 return NULL;
82 JSObject *global_object = JS_GetGlobalObject(js_engine_context_);
83 if (!global_object) {
84 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
85 LOG(("Could not get global object from script engine."));
86 return NULL;
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."));
94 return NULL;
96 } else {
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);
103 if (!result) {
104 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
105 LOG(("Could not get constructor property from global object."));
106 return NULL;
109 JSFunction *ctor = JS_ValueToFunction(js_engine_context_, val);
110 if (!ctor) {
111 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
112 LOG(("Could not convert constructor property to function."));
113 return NULL;
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,
131 &val);
132 if (!result) {
133 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
134 LOG(("Could not call constructor function."));
135 return NULL;
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."));
144 return NULL;
146 return retval.release();
147 } else {
148 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
149 LOG(("Constructor did not return an object"));
150 return NULL;
154 JsArray* NewArray() {
155 JSObject* array_object = JS_NewArrayObject(GetContext(), 0, NULL);
156 if (!array_object)
157 return NULL;
159 scoped_ptr<JsArray> js_array(new JsArray());
160 if (!js_array.get())
161 return NULL;
163 jsval array = OBJECT_TO_JSVAL(array_object);
164 if (!js_array->SetArray(array, GetContext()))
165 return NULL;
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);
197 return true;
200 // Remove the provided handler from the notification list for the specified
201 // event.
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);
207 return true;
210 #ifdef DEBUG
211 void ForceGC() {
212 if (js_engine_context_) {
213 JS_GC(js_engine_context_);
216 #endif
218 protected:
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
224 // alert phase.
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();
233 ++monitor) {
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_;
245 private:
246 std::set<JsEventHandlerInterface *> event_handlers_[MAX_JSEVENTS];
248 DISALLOW_EVIL_CONSTRUCTORS(JsRunnerBase);
252 class JsRunner : public JsRunnerBase {
253 public:
254 JsRunner() : alloc_js_wrapper_(NULL),
255 error_handler_(NULL), global_obj_(NULL), js_runtime_(NULL),
256 js_script_(NULL) {
257 InitJavaScriptEngine();
259 ~JsRunner();
261 bool AddGlobal(const std::string16 &name, IGeneric *object, gIID iface_id);
262 bool Start(const std::string16 &full_script);
263 bool Stop();
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);
272 private:
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,
292 const char *message,
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) {
331 NS_RELEASE(*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_);
341 if (js_runtime_) {
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;
358 return true;
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,
367 NULL, // class_id
368 NULL, // class_name
369 &(proto_interface->second))) {
370 *proto = proto_interface->second;
371 return true;
372 } else {
373 proto_interfaces_.erase(iface_id);
374 return false;
378 class JS_DestroyContextFunctor {
379 public:
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() {
387 JSBool js_ok;
388 bool succeeded;
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));
426 #ifdef DEBUG
427 // must set this here to allow workerPool.forceGC() during child init
428 js_engine_context_ = cx.get();
429 #endif
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(),
447 global_obj_));
449 js_engine_context_ = cx.release();
450 alloc_js_wrapper_ = js_wrapper.release();
452 struct {
453 const nsIID iface_id;
454 JSObject *proto_obj; // gets set by code below
455 } classes[] = {
456 // TODO(cprince): Unify the interface lists here and in GearsFactory.
457 // Could share code, or could query GearsFactory.
458 {GEARSFACTORYINTERFACE_IID, NULL},
459 // workerpool
460 {GEARSWORKERPOOLINTERFACE_IID, NULL},
461 // database
462 {GEARSDATABASEINTERFACE_IID, NULL},
463 {GEARSRESULTSETINTERFACE_IID, NULL},
464 // desktop
465 {GEARSDESKTOPINTERFACE_IID, NULL},
466 // localserver
467 {GEARSLOCALSERVERINTERFACE_IID, NULL},
468 {GEARSMANAGEDRESOURCESTOREINTERFACE_IID, NULL},
469 {GEARSRESOURCESTOREINTERFACE_IID, NULL},
470 // GEARSFILESUBMITTERINTERFACE_IID can never be created in a child worker
471 #ifdef DEBUG
472 // test
473 {GEARSTESTINTERFACE_IID, NULL},
474 #endif
475 // blob
476 {GEARSBLOBINTERFACE_IID, NULL},
477 // timer
478 {GEARSTIMERINTERFACE_IID, NULL},
479 // httprequest
480 {GEARSHTTPREQUESTINTERFACE_IID, NULL},
481 // channel
482 {GEARSCHANNELINTERFACE_IID, NULL},
483 // console
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; }
495 #ifdef DEBUG
496 // Do it here to trigger potential GC bugs in our code.
497 JS_GC(js_engine_context_);
498 #endif
500 return true; // succeeded
503 bool JsRunner::AddGlobal(const std::string16 &name,
504 IGeneric *object,
505 gIID iface_id) {
506 JSObject *proto_object;
507 if (!GetProtoFromIID(iface_id, &proto_object)) {
508 return false;
511 if (!alloc_js_wrapper_->DefineGlobal(proto_object, object, name.c_str())) {
512 return false;
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_,
536 js_script_);
537 if (!compiled_script_obj) { return false; }
538 js_script_root_.reset(new JsRootedToken(
539 js_engine_context_,
540 OBJECT_TO_JSVAL(compiled_script_obj)));
544 // Start the engine running
547 jsval return_string;
548 JSBool js_ok = JS_ExecuteScript(js_engine_context_, global_obj_,
549 js_script_, &return_string);
550 if (!js_ok) { return false; }
552 return true;
555 bool JsRunner::Stop() {
556 // TODO(zork): Implement
557 return false;
560 bool JsRunner::Eval(const std::string16 &script) {
561 JSObject *object = JS_GetGlobalObject(js_engine_context_);
563 uintN line_number_start = 0;
564 jsval rval;
565 JSBool js_ok = JS_EvaluateUCScript(
566 js_engine_context_,
567 object,
568 reinterpret_cast<const jschar *>(script.c_str()),
569 script.length(),
570 "script", line_number_start,
571 &rval);
572 if (!js_ok) { return false; }
573 return true;
576 bool JsRunner::InvokeCallbackSpecialized(
577 const JsRootedCallback *callback, int argc, jsval *argv,
578 JsRootedToken **optional_alloc_retval) {
579 jsval retval;
580 JSBool result = JS_CallFunctionValue(
581 callback->context(),
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);
596 return true;
599 // Provides the same interface as JsRunner, but for the normal JavaScript engine
600 // that runs in HTML pages.
601 class DocumentJsRunner : public JsRunnerBase {
602 public:
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.
612 return false;
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.
619 return false;
621 bool Stop() {
622 assert(false); // This should not be called on DocumentJsRunner.
623 return false;
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);
632 private:
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(
651 js_engine_context_,
652 object));
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);
659 } else {
660 sgo = do_QueryInterface(priv);
663 JSPrincipals *jsprin;
664 nsresult nr;
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;
683 jsval rval;
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.
690 JSContext *cx;
691 stack->Pop(&cx);
693 // Decrements ref count on jsprin (Was added in GetJSPrincipals()).
694 (void)JSPRINCIPALS_DROP(js_engine_context_, jsprin);
695 if (!js_ok) { return false; }
696 return true;
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,
705 HandleEventUnload,
706 this));
707 nsCOMPtr<nsIDOMEventTarget> event_source;
708 if (NS_SUCCEEDED(DOMUtils::GetWindowEventTarget(
709 getter_AddRefs(event_source))))
711 unload_monitor_->Start(event_source);
712 } else {
713 return false;
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; }
738 jsval retval;
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);
751 return true;
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));