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.
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.
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
58 class JsRunnerBase
: public JsRunnerInterface
{
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
);
70 LOG(("Could not get global object from script engine."));
74 CComQIPtr
<IDispatchEx
> global_dispex
= global_object
;
76 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
81 CComBSTR
ctor_name(optional_global_ctor_name
? optional_global_ctor_name
:
83 HRESULT hr
= global_dispex
->GetDispID(ctor_name
, fdexNameCaseSensitive
,
86 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
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)
98 LOG(("Could not invoke object constructor."));
99 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
103 scoped_ptr
<JsObject
> retval(new JsObject
);
104 if (!retval
->SetObject(result
, GetContext())) {
105 LOG(("Could not assign to JsObject."));
109 return retval
.release();
112 JsArray
* NewArray() {
113 scoped_ptr
<JsObject
> js_object(NewObject(STRING16(L
"Array")));
114 if (!js_object
.get())
117 scoped_ptr
<JsArray
> js_array(new JsArray());
121 if (!js_array
->SetArray(js_object
->js_object_
, js_object
->js_context_
))
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
);
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
);
177 // Remove the provided handler from the notification list for the specified
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
);
189 // CollectGarbage is not available through COM, so we call through JS.
190 if (!Eval(STRING16(L
"CollectGarbage();"))) {
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
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();
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;
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
> {
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
)
248 // For IServiceProviderImpl (used to disable ActiveX objects, along with
249 // IInternetHostSecurityManager).
250 BEGIN_SERVICE_MAP(JsRunnerImpl
)
251 SERVICE_ENTRY(SID_SInternetHostSecurityManager
)
254 JsRunnerImpl() : coinit_succeeded_(false) {}
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
264 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
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
);
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
,
288 STDMETHOD(ProcessUrlAction
)(DWORD action
, BYTE
*policy
, DWORD policy_size
,
289 BYTE
*context
, DWORD context_size
, DWORD flags
,
291 STDMETHOD(QueryCustomPolicy
)(REFGUID guid_key
, BYTE
**policy
,
292 DWORD
*policy_size
, BYTE
*context
,
293 DWORD context_size
, DWORD reserved
);
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_
) {
322 bool JsRunnerImpl::AddGlobal(const std::string16
&name
,
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.
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; }
344 bool JsRunnerImpl::Start(const std::string16
&full_script
) {
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
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
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(
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
,
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(),
441 SCRIPTITEM_ISVISIBLE
,
443 if (FAILED(hr
)) { return false; }
447 bool JsRunnerImpl::Stop() {
448 // Check pointer because dtor calls Stop() regardless of whether Start()
450 if (javascript_engine_
) {
451 javascript_engine_
->Close();
456 STDMETHODIMP
JsRunnerImpl::LookupNamedItem(const OLECHAR
*name
,
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();
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
)
480 error_handler_
->HandleError(error_info
);
485 STDMETHODIMP
JsRunnerImpl::ProcessUrlAction(DWORD action
, BYTE
*policy
,
486 DWORD policy_size
, BYTE
*context
,
487 DWORD context_size
, DWORD flags
,
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-
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.
510 STDMETHODIMP
JsRunnerImpl::QueryCustomPolicy(REFGUID guid_key
, BYTE
**policy
,
511 DWORD
*policy_size
, BYTE
*context
,
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.
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.
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
{
533 HRESULT hr
= CComObject
<JsRunnerImpl
>::CreateInstance(&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
);
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
);
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
);
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
{
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
,
589 if (dump_on_error
) ExceptionManager::CaptureAndSendMinidump();
592 return global_object
;
595 bool AddGlobal(const std::string16
&name
, IGeneric
*object
, gIID iface_id
) {
596 // TODO(zork): Add this functionality to DocumentJsRunner.
600 bool Start(const std::string16
&full_script
) {
601 assert(false); // Should not be called on the DocumentJsRunner.
606 assert(false); // Should not be called on the DocumentJsRunner.
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
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; }
626 if (FAILED(ActiveXUtils::GetDispatchMemberId(javascript_engine_dispatch
,
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 ¶meters
, // 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
655 if (unload_monitor_
== NULL
) {
656 unload_monitor_
.reset(new HtmlEventMonitor(kEventUnload
,
657 HandleEventUnload
, this));
659 // TODO(steveblock): Investigate whether IPIEHTMLWindow2 will meet our
663 CComPtr
<IHTMLWindow3
> event_source
;
664 if (FAILED(ActiveXUtils::GetHtmlWindow3(site_
, &event_source
))) {
668 unload_monitor_
->Start(event_source
);
673 return JsRunnerBase::AddEventHandler(event_type
, handler
);
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
));