[Author: andreip]
[google-gears.git] / gears / base / common / js_runner_np.cc
blobfb039942c0ec16fcf17d17587637960bcacf13bf
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 "gears/base/common/js_runner.h"
28 #include <assert.h>
29 #include <set>
30 #include <stdio.h>
32 #include "npapi.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
49 // different.
50 class JsRunnerBase : public JsRunnerInterface {
51 public:
52 JsRunnerBase(NPP instance) : np_instance_(instance) {
53 NPN_GetValue(np_instance_, NPNVWindowNPObject, &global_object_);
55 ~JsRunnerBase() {
56 NPN_ReleaseObject(global_object_);
59 JsContextPtr GetContext() {
60 return np_instance_;
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();
70 if (!global_object) {
71 LOG(("Could not get global object from script engine."));
72 return NULL;
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."));
79 return NULL;
81 } else {
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);
90 if (!rv) {
91 LOG(("Could not invoke object constructor."));
92 return NULL;
95 scoped_ptr<JsObject> retval(new JsObject);
96 if (!retval->SetObject(object, GetContext())) {
97 LOG(("Could not assign to JsObject."));
98 return NULL;
101 return retval.release();
104 JsArray* NewArray() {
105 // TODO: Implement
106 assert(false);
107 return NULL;
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.
122 NPVariant retval;
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);
139 return true;
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);
148 return true;
151 // Remove the provided handler from the notification list for the specified
152 // event.
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);
158 return true;
161 #ifdef DEBUG
162 void ForceGC() {
163 // TODO(mpcomplete): figure this out.
164 //Eval(STRING16(L"CollectGarbage();"));
166 #endif
168 protected:
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
174 // alert phase.
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();
183 ++monitor) {
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);
193 NPP np_instance_;
194 NPObject *global_object_;
196 private:
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 {
207 public:
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) {
216 return false;
218 bool Start(const std::string16 &full_script) {
219 return false;
221 bool Stop() {
222 return false;
224 bool Eval(const std::string16 &script) {
225 return false;
227 void SetErrorHandler(JsErrorHandlerInterface *error_handler) {
230 private:
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 {
239 public:
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.
246 return false;
249 bool Start(const std::string16 &full_script) {
250 assert(false); // Should not be called on the DocumentJsRunner.
251 return false;
254 bool Stop() {
255 assert(false); // Should not be called on the DocumentJsRunner.
256 return false;
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."));
265 return false;
268 NPString np_script = {script_utf8.data(), script_utf8.length()};
269 NPVariant retval;
270 if (!NPN_Evaluate(np_instance_, global_object, &np_script, &retval))
271 return false;
273 NPN_ReleaseVariantValue(&retval);
274 return true;
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.
291 NPObject* window;
292 if (NPN_GetValue(GetContext(), NPNVWindowNPObject, &window)
293 != NPERR_NO_ERROR)
294 return false;
295 ScopedNPObject window_scoped(window);
297 unload_monitor_.reset(new HtmlEventMonitor(kEventUnload,
298 HandleEventUnload,
299 this));
300 unload_monitor_->Start(GetContext(), window);
304 return JsRunnerBase::AddEventHandler(event_type, handler);
307 private:
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));