[Author: andreip]
[google-gears.git] / gears / base / common / js_runner_ie.cc
blobda968b52d5cd7cdb526043f17fb3e237e5f53468
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 // In IE, JsRunner is a wrapper and most work happens in
27 // JsRunnerImpl. The split is necessary so we can do two things:
28 // (1) provide a simple NewJsRunner/delete interface (not ref-counting), plus
29 // (2) derive from ATL for the internal implementation (e.g. for ScriptSiteImpl)
31 // JAVASCRIPT ENGINE DETAILS: Internet Explorer uses the JScript engine.
32 // The interface is IActiveScript, a shared scripting engine interface.
33 // Communication from the engine to external objects, and communication
34 // from externally into the engine, is all handled via IDispatch pointers.
36 #include <assert.h>
37 #include <dispex.h>
38 #include <map>
39 #include <set>
40 #include <vector>
41 #include "gears/third_party/scoped_ptr/scoped_ptr.h"
43 #include "gears/base/common/js_runner.h"
45 #include "gears/base/common/common.h" // for DISALLOW_EVIL_CONSTRUCTORS
46 #include "gears/base/common/exception_handler_win32.h"
47 #include "gears/base/common/html_event_monitor.h"
48 #include "gears/base/common/scoped_token.h"
49 #include "gears/base/ie/activex_utils.h"
50 #include "gears/base/ie/atl_headers.h"
51 #include "gears/third_party/AtlActiveScriptSite.h"
54 // Internal base class used to share some code between DocumentJsRunner and
55 // JsRunner. Do not override these methods from JsRunner or DocumentJsRunner.
56 // Either share the code here, or move it to those two classes if it's
57 // different.
58 class JsRunnerBase : public JsRunnerInterface {
59 public:
60 JsRunnerBase() {}
62 JsContextPtr GetContext() {
63 return NULL; // not used in IE
66 JsObject *NewObject(const char16 *optional_global_ctor_name,
67 bool dump_on_error = false) {
68 CComPtr<IDispatch> global_object = GetGlobalObject(dump_on_error);
69 if (!global_object) {
70 LOG(("Could not get global object from script engine."));
71 return NULL;
74 CComQIPtr<IDispatchEx> global_dispex = global_object;
75 if (!global_dispex) {
76 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
77 return NULL;
80 DISPID error_dispid;
81 CComBSTR ctor_name(optional_global_ctor_name ? optional_global_ctor_name :
82 STRING16(L"Object"));
83 HRESULT hr = global_dispex->GetDispID(ctor_name, fdexNameCaseSensitive,
84 &error_dispid);
85 if (FAILED(hr)) {
86 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
87 return NULL;
90 CComVariant result;
91 DISPPARAMS no_args = {NULL, NULL, 0, 0};
92 hr = global_dispex->InvokeEx(
93 error_dispid, LOCALE_USER_DEFAULT,
94 DISPATCH_CONSTRUCT, &no_args, &result,
95 NULL, // receives exception (NULL okay)
96 NULL); // pointer to "this" object (NULL okay)
97 if (FAILED(hr)) {
98 LOG(("Could not invoke object constructor."));
99 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
100 return NULL;
103 scoped_ptr<JsObject> retval(new JsObject);
104 if (!retval->SetObject(result, GetContext())) {
105 LOG(("Could not assign to JsObject."));
106 return NULL;
109 return retval.release();
112 JsArray* NewArray() {
113 scoped_ptr<JsObject> js_object(NewObject(STRING16(L"Array")));
114 if (!js_object.get())
115 return NULL;
117 scoped_ptr<JsArray> js_array(new JsArray());
118 if (!js_array.get())
119 return NULL;
121 if (!js_array->SetArray(js_object->js_object_, js_object->js_context_))
122 return NULL;
124 return js_array.release();
127 bool InvokeCallback(const JsRootedCallback *callback,
128 int argc, JsParamToSend *argv,
129 JsRootedToken **optional_alloc_retval) {
130 assert(callback && (!argc || argv));
131 if (callback->token().vt != VT_DISPATCH) { return false; }
133 // Setup argument array.
134 scoped_array<CComVariant> js_engine_argv(new CComVariant[argc]);
135 for (int i = 0; i < argc; ++i) {
136 int dest = argc - 1 - i; // args are expected in reverse order!!
137 ConvertJsParamToToken(argv[i], NULL, &js_engine_argv[dest]);
140 // Invoke the method.
141 DISPPARAMS invoke_params = {0};
142 invoke_params.cArgs = argc;
143 invoke_params.rgvarg = js_engine_argv.get();
145 VARIANT retval = {0};
146 HRESULT hr = callback->token().pdispVal->Invoke(
147 DISPID_VALUE, IID_NULL, // DISPID_VALUE = default action
148 LOCALE_SYSTEM_DEFAULT, // TODO(cprince): should this be user default?
149 DISPATCH_METHOD, // dispatch/invoke as...
150 &invoke_params, // parameters
151 optional_alloc_retval ? &retval : NULL, // receives result (NULL okay)
152 NULL, // receives exception (NULL okay)
153 NULL); // receives badarg index (NULL okay)
154 if (FAILED(hr)) { return false; }
156 if (optional_alloc_retval) {
157 // Note: A valid VARIANT is returned no matter what the js function
158 // returns. If it returns nothing, or explicitly returns <undefined>, the
159 // variant will contain VT_EMPTY. If it returns <null>, the variant will
160 // contain VT_NULL. Always returning a JsRootedToken should allow us to
161 // coerce these values to other types correctly in the future.
162 *optional_alloc_retval = new JsRootedToken(NULL, retval);
165 return true;
168 // Add the provided handler to the notification list for the specified event.
169 virtual bool AddEventHandler(JsEventType event_type,
170 JsEventHandlerInterface *handler) {
171 assert(event_type >= 0 && event_type < MAX_JSEVENTS);
173 event_handlers_[event_type].insert(handler);
174 return true;
177 // Remove the provided handler from the notification list for the specified
178 // event.
179 virtual bool RemoveEventHandler(JsEventType event_type,
180 JsEventHandlerInterface *handler) {
181 assert(event_type >= 0 && event_type < MAX_JSEVENTS);
183 event_handlers_[event_type].erase(handler);
184 return true;
187 #ifdef DEBUG
188 void ForceGC() {
189 // CollectGarbage is not available through COM, so we call through JS.
190 if (!Eval(STRING16(L"CollectGarbage();"))) {
191 assert(false);
194 #endif
196 protected:
197 // Alert all monitors that an event has occured.
198 void SendEvent(JsEventType event_type) {
199 assert(event_type >= 0 && event_type < MAX_JSEVENTS);
201 // Make a copy of the list of listeners, in case they change during the
202 // alert phase.
203 std::vector<JsEventHandlerInterface *> monitors;
204 monitors.insert(monitors.end(),
205 event_handlers_[event_type].begin(),
206 event_handlers_[event_type].end());
208 std::vector<JsEventHandlerInterface *>::iterator monitor;
209 for (monitor = monitors.begin();
210 monitor != monitors.end();
211 ++monitor) {
212 // Check that the listener hasn't been removed. This can occur if a
213 // listener removes another listener from the list.
214 if (event_handlers_[event_type].find(*monitor) !=
215 event_handlers_[event_type].end()) {
216 (*monitor)->HandleEvent(event_type);
221 // TODO(zork): Remove the argument when we find the error.
222 virtual IDispatch *GetGlobalObject(bool dump_on_error = false) = 0;
224 private:
225 std::set<JsEventHandlerInterface *> event_handlers_[MAX_JSEVENTS];
227 DISALLOW_EVIL_CONSTRUCTORS(JsRunnerBase);
231 // Internal helper COM object that implements IActiveScriptSite so we can host
232 // new ActiveScript engine instances. This class does the majority of the work
233 // of the real JsRunner.
234 class ATL_NO_VTABLE JsRunnerImpl
235 : public CComObjectRootEx<CComMultiThreadModel>,
236 public IDispatchImpl<IDispatch>,
237 public IActiveScriptSiteImpl,
238 public IInternetHostSecurityManager,
239 public IServiceProviderImpl<JsRunnerImpl> {
240 public:
241 BEGIN_COM_MAP(JsRunnerImpl)
242 COM_INTERFACE_ENTRY(IDispatch)
243 COM_INTERFACE_ENTRY(IActiveScriptSite)
244 COM_INTERFACE_ENTRY(IInternetHostSecurityManager)
245 COM_INTERFACE_ENTRY(IServiceProvider)
246 END_COM_MAP()
248 // For IServiceProviderImpl (used to disable ActiveX objects, along with
249 // IInternetHostSecurityManager).
250 BEGIN_SERVICE_MAP(JsRunnerImpl)
251 SERVICE_ENTRY(SID_SInternetHostSecurityManager)
252 END_SERVICE_MAP()
254 JsRunnerImpl() : coinit_succeeded_(false) {}
255 ~JsRunnerImpl();
257 // JsRunnerBase implementation
258 IDispatch *GetGlobalObject(bool dump_on_error = false) {
259 IDispatch *global_object;
260 HRESULT hr = javascript_engine_->GetScriptDispatch(
261 NULL, // get global object
262 &global_object);
263 if (FAILED(hr)) {
264 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
265 return NULL;
267 return global_object;
270 // JsRunner implementation
271 bool AddGlobal(const std::string16 &name, IGeneric *object, gIID iface_id);
272 bool Start(const std::string16 &full_script);
273 bool Stop();
274 bool Eval(const std::string16 &script);
275 void SetErrorHandler(JsErrorHandlerInterface *error_handler) {
276 error_handler_ = error_handler;
279 // IActiveScriptSiteImpl overrides
280 STDMETHOD(LookupNamedItem)(const OLECHAR *name, IUnknown **item);
281 STDMETHOD(HandleScriptError)(EXCEPINFO *ei, ULONG line, LONG pos, BSTR src);
283 // Implement IInternetHostSecurityManager. We use this to prevent the script
284 // engine from creating ActiveX objects, using Java, using scriptlets and
285 // various other questionable activities.
286 STDMETHOD(GetSecurityId)(BYTE *securityId, DWORD *securityIdSize,
287 DWORD_PTR reserved);
288 STDMETHOD(ProcessUrlAction)(DWORD action, BYTE *policy, DWORD policy_size,
289 BYTE *context, DWORD context_size, DWORD flags,
290 DWORD reserved);
291 STDMETHOD(QueryCustomPolicy)(REFGUID guid_key, BYTE **policy,
292 DWORD *policy_size, BYTE *context,
293 DWORD context_size, DWORD reserved);
295 private:
296 CComPtr<IActiveScript> javascript_engine_;
297 JsErrorHandlerInterface *error_handler_;
299 typedef std::map<std::string16, IGeneric *> NameToObjectMap;
300 NameToObjectMap global_name_to_object_;
302 bool coinit_succeeded_;
304 DISALLOW_EVIL_CONSTRUCTORS(JsRunnerImpl);
308 JsRunnerImpl::~JsRunnerImpl() {
309 // decrement all refcounts incremented by AddGlobal()
310 JsRunnerImpl::NameToObjectMap::const_iterator it;
311 const JsRunnerImpl::NameToObjectMap &map = global_name_to_object_;
312 for (it = map.begin(); it != map.end(); ++it) {
313 it->second->Release();
316 if (coinit_succeeded_) {
317 CoUninitialize();
322 bool JsRunnerImpl::AddGlobal(const std::string16 &name,
323 IGeneric *object,
324 gIID iface_id) {
325 if (!object) { return false; }
327 // We AddRef() to make sure the object lives as long as the JS engine.
328 // This gets removed in ~JsRunnerImpl.
329 object->AddRef();
330 global_name_to_object_[name] = object;
332 // Start() will insert any globals added before the JS engine started.
333 // But we must call AddNamedItem() here if the JS engine is already running.
334 if (javascript_engine_) {
335 HRESULT hr = javascript_engine_->AddNamedItem(name.c_str(),
336 SCRIPTITEM_ISVISIBLE);
337 if (FAILED(hr)) { return false; }
340 return true;
344 bool JsRunnerImpl::Start(const std::string16 &full_script) {
345 HRESULT hr;
347 coinit_succeeded_ = SUCCEEDED(CoInitializeEx(NULL,
348 GEARS_COINIT_THREAD_MODEL));
349 if (!coinit_succeeded_) { return false; }
350 // CoUninitialize is handled in dtor
353 // Instantiate a JavaScript engine
356 CLSID id;
357 hr = CLSIDFromProgID(L"JScript", &id);
358 if (FAILED(hr)) { return false; }
360 hr = javascript_engine_.CoCreateInstance(id);
361 if (FAILED(hr)) { return false; }
363 // Set the engine's site (which the engine queries when it encounters
364 // an unknown name).
365 hr = javascript_engine_->SetScriptSite(this);
366 if (FAILED(hr)) { return false; }
369 // Tell the script engine up to use a custom security manager implementation.
370 CComQIPtr<IObjectSafety> javascript_engine_safety;
371 javascript_engine_safety = javascript_engine_;
372 if (!javascript_engine_safety) { return false; }
374 hr = javascript_engine_safety->SetInterfaceSafetyOptions(
375 IID_IDispatch,
376 INTERFACE_USES_SECURITY_MANAGER,
377 INTERFACE_USES_SECURITY_MANAGER);
378 if (FAILED(hr)) { return false; }
382 // Tell the engine about named globals (set earlier via AddGlobal).
385 JsRunnerImpl::NameToObjectMap::const_iterator it;
386 const JsRunnerImpl::NameToObjectMap &map = global_name_to_object_;
387 for (it = map.begin(); it != map.end(); ++it) {
388 const std::string16 &name = it->first;
389 hr = javascript_engine_->AddNamedItem(name.c_str(), SCRIPTITEM_ISVISIBLE);
390 // TODO(cprince): see if need |= SCRIPTITEM_GLOBALMEMBERS
391 if (FAILED(hr)) { return false; }
396 // Add script code to the engine instance
399 CComQIPtr<IActiveScriptParse> javascript_engine_parser;
401 javascript_engine_parser = javascript_engine_;
402 if (!javascript_engine_parser) { return false; }
404 hr = javascript_engine_parser->InitNew();
405 if (FAILED(hr)) { return false; }
406 // why does ParseScriptText also AddRef the object?
407 hr = javascript_engine_parser->ParseScriptText(full_script.c_str(),
408 NULL, NULL, NULL, 0, 0,
409 SCRIPTITEM_ISVISIBLE,
410 NULL, NULL);
411 if (FAILED(hr)) { return false; }
414 // Start the engine running
417 // TODO(cprince): do we need STARTED *and* CONNECTED? (Does it matter?)
418 // CONNECTED "runs the script and blocks until the script is finished"
419 hr = javascript_engine_->SetScriptState(SCRIPTSTATE_STARTED);
420 if (FAILED(hr)) { return false; }
421 hr = javascript_engine_->SetScriptState(SCRIPTSTATE_CONNECTED);
422 if (FAILED(hr)) { return false; }
424 // NOTE: at this point, the JS code has returned, and it should have set
425 // an onmessage handler. (If it didn't, the worker is most likely cut off
426 // from other workers. There are ways to prevent this, but they are poor.)
428 return true; // succeeded
431 bool JsRunnerImpl::Eval(const std::string16 &script) {
432 CComQIPtr<IActiveScriptParse> javascript_engine_parser;
434 // Get the parser interface
435 javascript_engine_parser = javascript_engine_;
436 if (!javascript_engine_parser) { return false; }
438 // Execute the script
439 HRESULT hr = javascript_engine_parser->ParseScriptText(script.c_str(),
440 NULL, NULL, 0, 0, 0,
441 SCRIPTITEM_ISVISIBLE,
442 NULL, NULL);
443 if (FAILED(hr)) { return false; }
444 return true;
447 bool JsRunnerImpl::Stop() {
448 // Check pointer because dtor calls Stop() regardless of whether Start()
449 // happened.
450 if (javascript_engine_) {
451 javascript_engine_->Close();
453 return S_OK;
456 STDMETHODIMP JsRunnerImpl::LookupNamedItem(const OLECHAR *name,
457 IUnknown **item) {
458 IGeneric *found_item = global_name_to_object_[name];
459 if (!found_item) { return TYPE_E_ELEMENTNOTFOUND; }
461 // IActiveScript expects items coming into it to already be AddRef()'d. It
462 // will Release() these references on IActiveScript.Close().
463 // For an example of this, see: http://support.microsoft.com/kb/q221992/.
464 found_item->AddRef();
465 *item = found_item;
466 return S_OK;
470 STDMETHODIMP JsRunnerImpl::HandleScriptError(EXCEPINFO *ei, ULONG line,
471 LONG pos, BSTR src) {
472 if (!error_handler_) { return E_FAIL; }
474 const JsErrorInfo error_info = {
475 line + 1, // Reported lines start at zero.
476 ei->bstrDescription ? static_cast<char16*>(ei->bstrDescription)
477 : STRING16(L"")
480 error_handler_->HandleError(error_info);
481 return S_OK;
485 STDMETHODIMP JsRunnerImpl::ProcessUrlAction(DWORD action, BYTE *policy,
486 DWORD policy_size, BYTE *context,
487 DWORD context_size, DWORD flags,
488 DWORD reserved) {
489 // There are many different values of action that could conceivably be
490 // asked about: http://msdn2.microsoft.com/en-us/library/ms537178.aspx.
491 // Many of them probably don't apply to the scripting host alone, but there
492 // is no documentation on which are used, so we just say no to everything to
493 // be paranoid. We can whitelist things back if we find that necessary.
495 // TODO(aa): Consider whitelisting XMLHttpRequest. IE7 now has a global
496 // XMLHttpRequest object, like Safari and Mozilla. I don't believe this
497 // object "counts" as an ActiveX Control. If so, it seems like whitelisting
498 // the ActiveX version might not matter. In any case, doing this would
499 // require figuring out the parallel thing for Mozilla and figuring out how
500 // to get XMLHttpRequest the context it needs to make decisions about same-
501 // origin.
502 *policy = URLPOLICY_DISALLOW;
504 // MSDN says to return S_FALSE if you were successful, but don't want to
505 // allow the action: http://msdn2.microsoft.com/en-us/library/ms537151.aspx.
506 return S_FALSE;
510 STDMETHODIMP JsRunnerImpl::QueryCustomPolicy(REFGUID guid_key, BYTE **policy,
511 DWORD *policy_size, BYTE *context,
512 DWORD context_size,
513 DWORD reserved) {
514 // This method is only used when ProcessUrlAction allows ActiveXObjects to be
515 // created. Since we always say no, we do not need to implement this method.
516 return E_NOTIMPL;
520 STDMETHODIMP JsRunnerImpl::GetSecurityId(BYTE *security_id,
521 DWORD *security_id_size,
522 DWORD_PTR reserved) {
523 // Again, not used in the configuration we use.
524 return E_NOTIMPL;
528 // A wrapper class to AddRef/Release the internal COM object,
529 // while exposing a simpler new/delete interface to users.
530 class JsRunner : public JsRunnerBase {
531 public:
532 JsRunner() {
533 HRESULT hr = CComObject<JsRunnerImpl>::CreateInstance(&com_obj_);
534 if (com_obj_) {
535 com_obj_->AddRef(); // MSDN says call AddRef after CreateInstance
538 virtual ~JsRunner() {
539 // Alert modules that the engine is unloading.
540 SendEvent(JSEVENT_UNLOAD);
542 if (com_obj_) {
543 com_obj_->Stop();
544 com_obj_->Release();
548 IDispatch *GetGlobalObject(bool dump_on_error = false) {
549 return com_obj_->GetGlobalObject(dump_on_error);
551 bool AddGlobal(const std::string16 &name, IGeneric *object, gIID iface_id) {
552 return com_obj_->AddGlobal(name, object, iface_id);
554 bool Start(const std::string16 &full_script) {
555 return com_obj_->Start(full_script);
557 bool Stop() {
558 return com_obj_->Stop();
560 bool Eval(const std::string16 &script) {
561 return com_obj_->Eval(script);
563 void SetErrorHandler(JsErrorHandlerInterface *error_handler) {
564 return com_obj_->SetErrorHandler(error_handler);
567 private:
568 CComObject<JsRunnerImpl> *com_obj_;
570 DISALLOW_EVIL_CONSTRUCTORS(JsRunner);
574 // This class is a stub that is used to present a uniform interface to
575 // common functionality to both workers and the main thread.
576 class DocumentJsRunner : public JsRunnerBase {
577 public:
578 DocumentJsRunner(IGeneric *site) : site_(site) {
581 virtual ~DocumentJsRunner() {
584 IDispatch *GetGlobalObject(bool dump_on_error = false) {
585 IDispatch *global_object;
586 HRESULT hr = ActiveXUtils::GetScriptDispatch(site_, &global_object,
587 dump_on_error);
588 if (FAILED(hr)) {
589 if (dump_on_error) ExceptionManager::CaptureAndSendMinidump();
590 return NULL;
592 return global_object;
595 bool AddGlobal(const std::string16 &name, IGeneric *object, gIID iface_id) {
596 // TODO(zork): Add this functionality to DocumentJsRunner.
597 return false;
600 bool Start(const std::string16 &full_script) {
601 assert(false); // Should not be called on the DocumentJsRunner.
602 return false;
605 bool Stop() {
606 assert(false); // Should not be called on the DocumentJsRunner.
607 return false;
610 bool Eval(const std::string16 &script) {
611 // There appears to be no way to execute script code directly on WinCE.
612 // - IPIEHTMLWindow2 does not provide execScript.
613 // - We have the script engine's IDispatch pointer but there's no way to get
614 // back to the IActiveScript object to use
615 // IActiveScriptParse::ParseScriptText.
616 // Instead, we pass the script as a parameter to JavaScript's 'eval'
617 // function. We use this approach on both Win32 and WinCE to prevent
618 // branching.
620 // TODO(steveblock): Test the cost of using GetDispatchMemberId vs
621 // execScript using Stopwatch.
623 CComPtr<IDispatch> javascript_engine_dispatch = GetGlobalObject();
624 if (!javascript_engine_dispatch) { return false; }
625 DISPID function_iid;
626 if (FAILED(ActiveXUtils::GetDispatchMemberId(javascript_engine_dispatch,
627 STRING16(L"eval"),
628 &function_iid))) {
629 return false;
631 CComVariant script_variant(script.c_str());
632 DISPPARAMS parameters = {0};
633 parameters.cArgs = 1;
634 parameters.rgvarg = &script_variant;
635 return SUCCEEDED(javascript_engine_dispatch->Invoke(
636 function_iid, // member to invoke
637 IID_NULL, // reserved
638 LOCALE_SYSTEM_DEFAULT, // TODO(cprince): should this be user default?
639 DISPATCH_METHOD, // dispatch/invoke as...
640 &parameters, // parameters
641 NULL, // receives result (NULL okay)
642 NULL, // receives exception (NULL okay)
643 NULL)); // receives badarg index (NULL okay)
646 void SetErrorHandler(JsErrorHandlerInterface *handler) {
647 assert(false); // Should not be called on the DocumentJsRunner.
650 bool AddEventHandler(JsEventType event_type,
651 JsEventHandlerInterface *handler) {
652 if (event_type == JSEVENT_UNLOAD) {
653 // Create an HTML event monitor to send the unload event when the page
654 // goes away.
655 if (unload_monitor_ == NULL) {
656 unload_monitor_.reset(new HtmlEventMonitor(kEventUnload,
657 HandleEventUnload, this));
658 #ifdef WINCE
659 // TODO(steveblock): Investigate whether IPIEHTMLWindow2 will meet our
660 // needs here.
661 return false;
662 #else
663 CComPtr<IHTMLWindow3> event_source;
664 if (FAILED(ActiveXUtils::GetHtmlWindow3(site_, &event_source))) {
665 return false;
668 unload_monitor_->Start(event_source);
669 #endif
673 return JsRunnerBase::AddEventHandler(event_type, handler);
676 private:
677 static void HandleEventUnload(void *user_param) {
678 // Callback for 'onunload'
679 static_cast<DocumentJsRunner*>(user_param)->SendEvent(JSEVENT_UNLOAD);
682 scoped_ptr<HtmlEventMonitor> unload_monitor_; // For 'onunload' notifications
683 CComPtr<IUnknown> site_;
685 DISALLOW_EVIL_CONSTRUCTORS(DocumentJsRunner);
689 JsRunnerInterface* NewJsRunner() {
690 return static_cast<JsRunnerInterface*>(new JsRunner());
693 JsRunnerInterface* NewDocumentJsRunner(IGeneric *base, JsContextPtr context) {
694 return static_cast<JsRunnerInterface*>(new DocumentJsRunner(base));