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 #include "gears/base/common/js_runner.h"
33 #include "npruntime.h"
35 #include "gears/third_party/scoped_ptr/scoped_ptr.h"
37 #include "gears/base/common/common.h" // for DISALLOW_EVIL_CONSTRUCTORS
38 #include "gears/base/common/html_event_monitor.h"
39 #include "gears/base/common/scoped_token.h"
40 #include "gears/base/common/string_utils.h"
41 #include "gears/base/npapi/browser_utils.h"
42 #include "gears/base/npapi/np_utils.h"
43 #include "gears/base/npapi/scoped_npapi_handles.h"
46 // Internal base class used to share some code between DocumentJsRunner and
47 // JsRunner. Do not override these methods from JsRunner or DocumentJsRunner.
48 // Either share the code here, or move it to those two classes if it's
50 class JsRunnerBase
: public JsRunnerInterface
{
52 JsRunnerBase(NPP instance
) : np_instance_(instance
) {
53 NPN_GetValue(np_instance_
, NPNVWindowNPObject
, &global_object_
);
56 NPN_ReleaseObject(global_object_
);
59 JsContextPtr
GetContext() {
63 NPObject
*GetGlobalObject() {
64 return global_object_
;
67 JsObject
*NewObject(const char16
*optional_global_ctor_name
,
68 bool dump_on_fail
= false) {
69 NPObject
*global_object
= GetGlobalObject();
71 LOG(("Could not get global object from script engine."));
75 std::string ctor_name_utf8
;
76 if (optional_global_ctor_name
) {
77 if (!String16ToUTF8(optional_global_ctor_name
, &ctor_name_utf8
)) {
78 LOG(("Could not convert constructor name."));
82 ctor_name_utf8
= "Object";
84 ctor_name_utf8
.append("()");
86 // Evaluate javascript code: 'ConstructorName()'
87 NPString script
= {ctor_name_utf8
.c_str(), ctor_name_utf8
.length()};
88 ScopedNPVariant object
;
89 bool rv
= NPN_Evaluate(GetContext(), global_object
, &script
, &object
);
91 LOG(("Could not invoke object constructor."));
95 scoped_ptr
<JsObject
> retval(new JsObject
);
96 if (!retval
->SetObject(object
, GetContext())) {
97 LOG(("Could not assign to JsObject."));
101 return retval
.release();
104 JsArray
* NewArray() {
110 bool InvokeCallback(const JsRootedCallback
*callback
,
111 int argc
, JsParamToSend
*argv
,
112 JsRootedToken
**optional_alloc_retval
) {
113 assert(callback
&& (!argc
|| argv
));
114 if (!NPVARIANT_IS_OBJECT(callback
->token())) { return false; }
116 // Setup argument array.
117 scoped_array
<ScopedNPVariant
> js_engine_argv(new ScopedNPVariant
[argc
]);
118 for (int i
= 0; i
< argc
; ++i
)
119 ConvertJsParamToToken(argv
[i
], GetContext(), &js_engine_argv
[i
]);
121 // Invoke the method.
123 bool rv
= NPN_InvokeDefault(np_instance_
,
124 NPVARIANT_TO_OBJECT(callback
->token()),
125 js_engine_argv
.get(), argc
, &retval
);
126 if (!rv
) { return false; }
128 if (optional_alloc_retval
) {
129 // Note: A valid NPVariant is returned no matter what the js function
130 // returns. If it returns nothing, or explicitly returns <undefined>, the
131 // variant will contain VOID. If it returns <null>, the variant will
132 // contain NULL. Always returning a JsRootedToken should allow us to
133 // coerce these values to other types correctly in the future.
134 *optional_alloc_retval
= new JsRootedToken(np_instance_
, retval
);
137 NPN_ReleaseVariantValue(&retval
);
142 // Add the provided handler to the notification list for the specified event.
143 virtual bool AddEventHandler(JsEventType event_type
,
144 JsEventHandlerInterface
*handler
) {
145 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
147 event_handlers_
[event_type
].insert(handler
);
151 // Remove the provided handler from the notification list for the specified
153 virtual bool RemoveEventHandler(JsEventType event_type
,
154 JsEventHandlerInterface
*handler
) {
155 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
157 event_handlers_
[event_type
].erase(handler
);
163 // TODO(mpcomplete): figure this out.
164 //Eval(STRING16(L"CollectGarbage();"));
169 // Alert all monitors that an event has occured.
170 void SendEvent(JsEventType event_type
) {
171 assert(event_type
>= 0 && event_type
< MAX_JSEVENTS
);
173 // Make a copy of the list of listeners, in case they change during the
175 std::vector
<JsEventHandlerInterface
*> monitors
;
176 monitors
.insert(monitors
.end(),
177 event_handlers_
[event_type
].begin(),
178 event_handlers_
[event_type
].end());
180 std::vector
<JsEventHandlerInterface
*>::iterator monitor
;
181 for (monitor
= monitors
.begin();
182 monitor
!= monitors
.end();
184 // Check that the listener hasn't been removed. This can occur if a
185 // listener removes another listener from the list.
186 if (event_handlers_
[event_type
].find(*monitor
) !=
187 event_handlers_
[event_type
].end()) {
188 (*monitor
)->HandleEvent(event_type
);
194 NPObject
*global_object_
;
197 std::set
<JsEventHandlerInterface
*> event_handlers_
[MAX_JSEVENTS
];
199 DISALLOW_EVIL_CONSTRUCTORS(JsRunnerBase
);
202 // TODO(mpcomplete): implement me. We need a browser-independent way of
203 // creating a JS engine. Options:
204 // 1. Bundle spidermonkey and just run that.
205 // 2. Leave this class as a browser-specific module.
206 class JsRunner
: public JsRunnerBase
{
208 JsRunner() : JsRunnerBase(NULL
) {
210 virtual ~JsRunner() {
211 // Alert modules that the engine is unloading.
212 SendEvent(JSEVENT_UNLOAD
);
215 bool AddGlobal(const std::string16
&name
, IGeneric
*object
, gIID iface_id
) {
218 bool Start(const std::string16
&full_script
) {
224 bool Eval(const std::string16
&script
) {
227 void SetErrorHandler(JsErrorHandlerInterface
*error_handler
) {
232 DISALLOW_EVIL_CONSTRUCTORS(JsRunner
);
236 // This class is a stub that is used to present a uniform interface to
237 // common functionality to both workers and the main thread.
238 class DocumentJsRunner
: public JsRunnerBase
{
240 DocumentJsRunner(NPP instance
) : JsRunnerBase(instance
) { }
242 virtual ~DocumentJsRunner() {}
244 bool AddGlobal(const std::string16
&name
, IGeneric
*object
, gIID iface_id
) {
245 // TODO(mpcomplete): Add this functionality to DocumentJsRunner.
249 bool Start(const std::string16
&full_script
) {
250 assert(false); // Should not be called on the DocumentJsRunner.
255 assert(false); // Should not be called on the DocumentJsRunner.
259 bool Eval(const std::string16
&script
) {
260 NPObject
*global_object
= GetGlobalObject();
262 std::string script_utf8
;
263 if (!String16ToUTF8(script
.data(), script
.length(), &script_utf8
)) {
264 LOG(("Could not convert script to UTF8."));
268 NPString np_script
= {script_utf8
.data(), script_utf8
.length()};
270 if (!NPN_Evaluate(np_instance_
, global_object
, &np_script
, &retval
))
273 NPN_ReleaseVariantValue(&retval
);
277 void SetErrorHandler(JsErrorHandlerInterface
*handler
) {
278 assert(false); // Should not be called on the DocumentJsRunner.
281 static void HandleEventUnload(void *user_param
) {
282 static_cast<DocumentJsRunner
*>(user_param
)->SendEvent(JSEVENT_UNLOAD
);
285 bool AddEventHandler(JsEventType event_type
,
286 JsEventHandlerInterface
*handler
) {
287 if (event_type
== JSEVENT_UNLOAD
) {
288 // Monitor 'onunload' to send the unload event when the page goes away.
289 if (unload_monitor_
== NULL
) {
290 // Retrieve retrieve the DOM window.
292 if (NPN_GetValue(GetContext(), NPNVWindowNPObject
, &window
)
295 ScopedNPObject
window_scoped(window
);
297 unload_monitor_
.reset(new HtmlEventMonitor(kEventUnload
,
300 unload_monitor_
->Start(GetContext(), window
);
304 return JsRunnerBase::AddEventHandler(event_type
, handler
);
308 scoped_ptr
<HtmlEventMonitor
> unload_monitor_
; // For 'onunload' notifications
309 DISALLOW_EVIL_CONSTRUCTORS(DocumentJsRunner
);
313 JsRunnerInterface
*NewJsRunner() {
314 return static_cast<JsRunnerInterface
*>(new JsRunner());
317 JsRunnerInterface
*NewDocumentJsRunner(IGeneric
*base
, JsContextPtr context
) {
318 return static_cast<JsRunnerInterface
*>(new DocumentJsRunner(context
));