Add Python REPL that works inside the browser
[nativeclient.git] / npapi_plugin / npapi_bridge / npmodule.cc
blob80893ed67ddbd3d388a64c492df4c600f1a10a30
1 /*
2 * Copyright 2008, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #if NACL_WINDOWS
34 #include <windows.h>
35 #include <windowsx.h>
36 #endif // NACL_WINDOWS
38 #if NACL_LINUX && defined(MOZ_X11)
39 #include <X11/Xlib.h>
40 #include <X11/Intrinsic.h>
41 #endif // NACL_LINUX && defined(MOZ_X11)
43 #include "native_client/npapi_plugin/origin.h"
44 #include "native_client/npapi_plugin/npapi_bridge/npmodule.h"
46 #include "native_client/service_runtime/include/sys/audio_video.h"
47 #include "native_client/service_runtime/nacl_config.h"
48 #include "native_client/service_runtime/sel_util.h"
49 #include "native_client/tools/npapi_runtime/nacl_util.h"
51 namespace {
53 // This implementation of same-origin policy does not take document.domain
54 // element into account.
55 std::string GetModuleOrigin(NPP instance) {
56 std::string origin;
57 NPVariant loc_value;
58 NPVariant href_value;
59 NPIdentifier href_id = NPN_GetStringIdentifier("href");
60 NPIdentifier location_id = NPN_GetStringIdentifier("location");
62 VOID_TO_NPVARIANT(loc_value);
63 VOID_TO_NPVARIANT(href_value);
64 do {
65 NPObject* win_obj;
66 if (NPN_GetValue(instance, NPNVWindowNPObject, &win_obj) !=
67 NPERR_NO_ERROR) {
68 break;
70 if (!NPN_GetProperty(instance, win_obj, location_id, &loc_value) ||
71 !NPVARIANT_IS_OBJECT(loc_value)) {
72 break;
74 NPObject* loc_obj = NPVARIANT_TO_OBJECT(loc_value);
75 if (!NPN_GetProperty(instance, loc_obj, href_id, &href_value) ||
76 !NPVARIANT_IS_STRING(href_value)) {
77 break;
79 std::string href(NPVARIANT_TO_STRING(href_value).utf8characters,
80 NPVARIANT_TO_STRING(href_value).utf8length);
81 origin = nacl::UrlToOrigin(href);
82 } while (0);
83 NPN_ReleaseVariantValue(&loc_value);
84 NPN_ReleaseVariantValue(&href_value);
85 return origin;
88 } // namespace
90 namespace nacl {
92 NPModule::NPModule(NPP npp, int argc, char* argn[], char* argv[])
93 : NPBridge(npp),
94 subprocess_(NULL),
95 child_(kInvalidHandle),
96 proxy_(NULL),
97 filename_(NULL),
98 url_(NULL),
99 stream_(kInvalidHandle),
100 #if NACL_WINDOWS
101 monitor_thread_(kInvalidHtpHandle),
102 #else
103 monitor_thread_(0),
104 monitor_thread_started_(false),
105 #endif
106 window_(NULL),
107 bitmap_shm_(kInvalidHtpHandle),
108 bitmap_data_(NULL),
109 bitmap_size_(0) {
110 const char* agent = NPN_UserAgent(npp);
111 set_is_webkit(strstr(agent, "AppleWebKit") ? true : false);
113 origin_ = GetModuleOrigin(npp);
115 // Check that origin is in the list of permitted origins.
116 origin_valid_ = OriginIsInWhitelist(origin_);
118 argc_ = 0;
119 for (int i = 0; i < argc; ++i) {
120 // Researve last three arguments for the handle number for the connection
121 // to the plungin, for the size of NPVariant in the plugin, and for NULL.
122 if (kMaxArg - 4 <= argc_) {
123 break;
125 // Note argv[i] can be a NULL pointer.
126 argv_[argc_++] = strdup(argn[i] ? argn[i] : "");
127 argv_[argc_++] = strdup(argv[i] ? argv[i] : "");
129 // The last two arguments are the handle value for the connection to the
130 // plugin, and for the size of NPVariant in the plugin.
132 // SelLdrLauncher will replace "$CHAN" with the IMC descriptor.
133 argv_[argc_++] = strdup("$CHAN");
134 char variant_size[4];
135 #if NACL_WINDOWS
136 _snprintf(variant_size, sizeof variant_size, "%u", sizeof(NPVariant));
137 #else
138 snprintf(variant_size, sizeof variant_size, "%zu", sizeof(NPVariant));
139 #endif
140 argv_[argc_++] = strdup(variant_size);
141 argv_[argc_] = NULL;
143 #if NACL_WINDOWS
144 original_window_procedure_ = NULL;
145 #endif
147 NPCapability capability = { 0, 0 };
148 proxy_ = new(std::nothrow) NPObjectProxy(this, capability);
151 NPModule::~NPModule() {
152 if (window_ && window_->window) {
153 #if NACL_WINDOWS
154 SubclassWindow(reinterpret_cast<HWND>(window_->window),
155 original_window_procedure_);
156 #endif
158 if (subprocess_) {
159 delete subprocess_;
161 child_ = kInvalidHandle;
162 if (filename_ != NULL) {
163 free(filename_);
165 if (url_ != NULL) {
166 free(url_);
168 for (int i = 0; i < argc_; ++i) {
169 free(const_cast<char *>(argv_[i]));
171 // Note proxy objects are deleted by Navigator.
172 if (proxy_) {
173 proxy_->Detach();
174 NPN_ReleaseObject(proxy_);
176 #if NACL_WINDOWS
177 if (monitor_thread_ != kInvalidHtpHandle) {
178 WaitForSingleObject(monitor_thread_, INFINITE);
179 CloseHandle(monitor_thread_);
181 #else
182 if (monitor_thread_started()) {
183 // TODO: perhaps if we used pthread_detach we wouldn't have to join.
184 pthread_join(monitor_thread_, NULL);
186 #endif
189 int NPModule::Dispatch(RpcHeader* request, int len) {
190 int result;
191 switch (request->type) {
192 case RPC_GET_WINDOW_OBJECT:
193 result = GetValue(request, len, NPNVWindowNPObject);
194 break;
195 case RPC_GET_PLUGIN_ELEMENT_OBJECT:
196 result = GetValue(request, len, NPNVPluginElementNPObject);
197 break;
198 case RPC_SET_STATUS:
199 result = SetStatus(request, len);
200 break;
201 case RPC_INVALIDATE_RECT:
202 result = InvalidateRect(request, len);
203 break;
204 case RPC_FORCE_REDRAW:
205 result = ForceRedraw(request, len);
206 break;
207 case RPC_CREATE_ARRAY:
208 result = CreateArray(request, len);
209 break;
210 case RPC_OPEN_URL:
211 result = OpenURL(request, len);
212 break;
213 default:
214 return NPBridge::Dispatch(request, len);
215 break;
217 return result;
220 int NPModule::GetValue(RpcHeader* request, int len, NPNVariable variable) {
221 IOVec vecv[2];
222 IOVec* vecp = vecv;
223 vecp->base = request;
224 vecp->length = sizeof(RpcHeader);
225 ++vecp;
227 NPObject* object;
228 request->error_code = NPN_GetValue(npp(), variable, &object);
230 RpcStack stack(this);
231 if (request->error_code == NPERR_NO_ERROR) {
232 stack.Push(object);
234 vecp = stack.SetIOVec(vecp);
235 return Respond(request, vecv, vecp - vecv);
238 int NPModule::SetStatus(RpcHeader* request, int len) {
239 RpcArg arg(this, request, len);
240 arg.Step(sizeof(RpcHeader));
242 IOVec vecv[2];
243 IOVec* vecp = vecv;
244 vecp->base = request;
245 vecp->length = sizeof(RpcHeader);
246 ++vecp;
247 const char* status = arg.GetString();
248 if (status) {
249 NPN_Status(npp(), status);
251 return Respond(request, vecv, vecp - vecv);
254 int NPModule::InvalidateRect(RpcHeader* request, int len) {
255 RpcArg arg(this, request, len);
256 arg.Step(sizeof(RpcHeader));
257 const NPRect* nprect = arg.GetRect();
259 IOVec vecv[1];
260 IOVec* vecp = vecv;
261 vecp->base = request;
262 vecp->length = sizeof(RpcHeader);
263 ++vecp;
264 if (window_ && window_->window && nprect) {
265 #if NACL_WINDOWS
266 HWND hwnd = static_cast<HWND>(window_->window);
267 RECT rect;
268 rect.left = nprect->left;
269 rect.top = nprect->top;
270 rect.right = nprect->right;
271 rect.bottom = nprect->bottom;
272 ::InvalidateRect(hwnd, &rect, FALSE);
273 #endif
274 #if NACL_OSX
275 NPN_InvalidateRect(npp(), const_cast<NPRect*>(nprect));
276 #endif
278 return Respond(request, vecv, vecp - vecv);
281 int NPModule::ForceRedraw(RpcHeader* request, int len) {
282 IOVec vecv[1];
283 IOVec* vecp = vecv;
284 vecp->base = request;
285 vecp->length = sizeof(RpcHeader);
286 ++vecp;
287 if (window_ && window_->window) {
288 #if !NACL_OSX
289 Redraw();
290 #else
291 NPN_ForceRedraw(npp());
292 #endif
294 return Respond(request, vecv, vecp - vecv);
297 int NPModule::CreateArray(RpcHeader* request, int len) {
298 IOVec vecv[2];
299 IOVec* vecp = vecv;
300 vecp->base = request;
301 vecp->length = sizeof(RpcHeader);
302 ++vecp;
304 NPObject* window;
305 NPN_GetValue(npp(), NPNVWindowNPObject, &window);
307 NPString script;
308 script.utf8characters = "new Array();";
309 script.utf8length = strlen(script.utf8characters);
310 NPVariant result;
311 RpcStack stack(this);
312 if (NPN_Evaluate(npp(), window, &script, &result) &&
313 NPVARIANT_IS_OBJECT(result)) {
314 stack.Push(NPVARIANT_TO_OBJECT(result));
315 vecp = stack.SetIOVec(vecp);
318 NPN_ReleaseObject(window);
320 return Respond(request, vecv, vecp - vecv);
323 int NPModule::OpenURL(RpcHeader* request, int len) {
324 RpcArg arg(this, request, len);
325 arg.Step(sizeof(RpcHeader));
327 IOVec vecv[1];
328 IOVec* vecp = vecv;
329 vecp->base = request;
330 vecp->length = sizeof(RpcHeader);
331 ++vecp;
333 const char* url = arg.GetString();
334 if (url) {
335 request->error_code = NPN_GetURLNotify(npp(), url, NULL, NULL);
336 } else {
337 request->error_code = NPERR_GENERIC_ERROR;
339 if (request->error_code == NPERR_NO_ERROR) {
340 // NPP_NewStream, NPP_DestroyStream, and NPP_URLNotify will be invoked
341 // later.
342 set_url(url);
344 return Respond(request, vecv, vecp - vecv);
347 NPError NPModule::New() {
348 RpcHeader request;
349 request.type = RPC_NEW;
350 IOVec vecv[2];
351 IOVec* vecp = vecv;
352 vecp->base = &request;
353 vecp->length = sizeof request;
354 ++vecp;
356 NPSize window_size;
357 if (bitmap_data_) {
358 window_size.width = window_->width;
359 window_size.height = window_->height;
360 } else {
361 window_size.width = 0;
362 window_size.height = 0;
364 vecp->base = &window_size;
365 vecp->length = sizeof window_size;
366 ++vecp;
368 clear_handle_count();
369 if (bitmap_shm_ != kInvalidHtpHandle) {
370 add_handle(bitmap_shm_);
373 int length;
374 RpcHeader* reply = Request(&request, vecv, vecp - vecv, &length);
375 if (reply == NULL) {
376 return NPERR_GENERIC_ERROR;
378 RpcArg result(this, reply, length);
379 result.Step(sizeof(RpcHeader));
380 NPError nperr = static_cast<NPError>(reply->error_code);
381 NPCapability* capability = result.GetCapability();
382 if (capability && proxy_) {
383 proxy_->set_capability(*capability);
384 AddProxy(proxy_);
386 return nperr;
390 // NPInstance methods
393 NPError NPModule::Destroy(NPSavedData** save) {
394 NPError nperr;
395 RpcHeader request;
396 request.type = RPC_DESTROY;
397 IOVec vecv[1];
398 IOVec* vecp = vecv;
399 vecp->base = &request;
400 vecp->length = sizeof request;
401 ++vecp;
402 int length;
403 RpcHeader* reply = Request(&request, vecv, vecp - vecv, &length);
404 if (reply == NULL) {
405 nperr = NPERR_INVALID_INSTANCE_ERROR;
406 } else {
407 nperr = static_cast<NPError>(reply->error_code);
409 delete this;
410 return nperr;
413 #if NACL_LINUX && defined(MOZ_X11)
415 void NPModule::EventHandler(Widget widget,
416 NPModule* module,
417 XEvent* xevent,
418 Boolean* b) {
419 Window window = reinterpret_cast<Window>(module->window_->window);
420 NPSetWindowCallbackStruct* wcbs =
421 reinterpret_cast<NPSetWindowCallbackStruct*>(module->window_->ws_info);
422 Display* display = wcbs->display;
423 switch (xevent->type) {
424 case Expose:
425 // Exposure events come in multiples, one per rectangle uncovered.
426 // We just look at one and redraw the whole region.
427 while (XCheckTypedWindowEvent(display, window, Expose, xevent));
428 module->Redraw();
429 break;
430 default:
431 // Other types of events should be handled here.
432 break;
436 #endif // NACL_LINUX && defined(MOZ_X11)
438 NPError NPModule::SetWindow(NPWindow* window) {
439 if (!window_ && window->window && 0 < window->width && 0 < window->height) {
440 // don't allow invalid window sizes
441 if ((window->width < kNaClVideoMinWindowSize) ||
442 (window->width > kNaClVideoMaxWindowSize) ||
443 (window->height < kNaClVideoMinWindowSize) ||
444 (window->height > kNaClVideoMaxWindowSize)) {
445 return NPERR_GENERIC_ERROR;
447 #if NACL_WINDOWS
448 HWND hwnd = static_cast<HWND>(window->window);
449 original_window_procedure_ =
450 SubclassWindow(hwnd, reinterpret_cast<WNDPROC>(WindowProcedure));
451 SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<LONG>(this));
452 #endif // NACL_WINDOWS
453 #if NACL_LINUX && defined(MOZ_X11)
454 Window xwin = reinterpret_cast<Window>(window->window);
455 NPSetWindowCallbackStruct* npsw =
456 reinterpret_cast<NPSetWindowCallbackStruct*>(window->ws_info);
457 Widget widget = XtWindowToWidget(npsw->display, xwin);
458 if (widget) {
459 // Only enable Expose events.
460 long event_mask = ExposureMask;
461 XSelectInput(npsw->display, xwin, event_mask);
462 XtAddEventHandler(widget, event_mask, False,
463 reinterpret_cast<XtEventHandler>(EventHandler),
464 this);
466 #endif // NACL_LINUX && defined(MOZ_X11)
467 // We are using 32-bit ARGB format (4 bytes per pixel).
468 bitmap_size_ = 4 * window->width * window->height;
469 bitmap_size_ = NaClRoundAllocPage(bitmap_size_);
470 // TODO : Check failure.
471 bitmap_shm_ = CreateShmDesc(CreateMemoryObject(bitmap_size_),
472 bitmap_size_);
473 if (bitmap_shm_ == kInvalidHtpHandle) {
474 return NPERR_GENERIC_ERROR;
476 bitmap_data_ = Map(NULL, bitmap_size_,
477 kProtRead | kProtWrite, kMapShared,
478 bitmap_shm_, 0);
479 if (bitmap_data_ == kMapFailed) {
480 Close(bitmap_shm_);
481 bitmap_shm_ = kInvalidHtpHandle;
482 bitmap_data_ = NULL;
483 return NPERR_GENERIC_ERROR;
485 window_ = window;
487 return NPERR_NO_ERROR;
490 NPError NPModule::GetValue(NPPVariable variable, void *value) {
491 switch (variable) {
492 case NPPVpluginNameString:
493 *static_cast<const char**>(value) = "NativeClient NPAPI bridge plug-in";
494 break;
495 case NPPVpluginDescriptionString:
496 *static_cast<const char**>(value) =
497 "A plug-in for NPAPI based NativeClient modules.";
498 break;
499 case NPPVpluginScriptableNPObject:
500 *reinterpret_cast<NPObject**>(value) = GetScriptableInstance();
501 if (!*reinterpret_cast<NPObject**>(value)) {
502 return NPERR_GENERIC_ERROR;
504 break;
505 default:
506 return NPERR_INVALID_PARAM;
508 return NPERR_NO_ERROR;
511 #if !NACL_OSX
513 int16_t NPModule::HandleEvent(void* event) {
514 return 0;
517 #endif // NACL_OSX
519 // Note GetScriptableInstance() is invoked before the NaCl module is started,
520 // but we still need to return a valide NPObject pointer at least on Windows.
521 NPObject* NPModule::GetScriptableInstance() {
522 if (proxy_) {
523 NPN_RetainObject(proxy_);
525 return proxy_;
528 NPError NPModule::NewStream(NPMIMEType type, NPStream* stream, NPBool seekable,
529 uint16_t* stype) {
530 *stype = NP_ASFILEONLY;
531 return NPERR_NO_ERROR;
534 void NPModule::StreamAsFile(NPStream* stream, const char* filename) {
535 if (url()) {
536 if (filename && UrlToOrigin(stream->url) == origin_) {
537 stream_ = OpenFile(filename);
538 if (stream_ != kInvalidHandle) {
539 set_filename(filename);
540 } else {
541 set_filename(NULL);
543 } else {
544 set_filename(NULL);
546 } else if (filename && subprocess_ == NULL) {
547 // This filename is for the file specified by the src attribute.
548 if (origin_valid_) {
549 // check ABI version compatibility
550 NPError np = nacl::CheckExecutableVersion(npp(), filename);
551 if (NPERR_NO_ERROR == np) {
552 Start(filename);
553 // Use NaCl ABI (X86 ELF).
554 set_peer_npvariant_size(12);
555 New();
556 } else {
557 fprintf(stderr, "Load failed: possible ABI version mismatch\n");
559 } else {
560 fprintf(stderr, "Load failed: NaCl module did not come from a whitelisted"
561 " source.\nSee npapi_plugin/origin.cc for the list.");
562 NPObject* window;
563 NPN_GetValue(npp(), NPNVWindowNPObject, &window);
564 NPString script;
565 script.utf8characters = "alert('Load failed: NaCl module did not"
566 " come from a whitelisted source.\\n"
567 "See npapi_plugin/origin.cc for the list.');";
568 script.utf8length = strlen(script.utf8characters);
569 NPVariant result;
570 NPN_Evaluate(npp(), window, &script, &result);
575 NPError NPModule::DestroyStream(NPStream *stream, NPError reason) {
576 return NPERR_NO_ERROR;
579 void NPModule::URLNotify(const char* url, NPReason reason, void* notify_data) {
580 if (this->url() == NULL) {
581 return;
584 if (filename_ == NULL) {
585 reason = NPRES_NETWORK_ERR;
588 HtpHandle handle = kInvalidHtpHandle;
589 if (reason == NPRES_DONE) {
590 handle = CreateIoDesc(stream_);
593 RpcHeader request;
594 request.type = RPC_NOTIFY_URL;
595 request.error_code = reason;
596 IOVec vecv[2];
597 IOVec* vecp = vecv;
598 vecp->base = &request;
599 vecp->length = sizeof request;
600 ++vecp;
601 RpcStack stack(this);
602 vecp = stack.SetIOVec(vecp);
604 clear_handle_count();
605 if (handle != kInvalidHtpHandle) {
606 add_handle(handle);
609 int length;
610 Request(&request, vecv, vecp - vecv, &length);
612 Close(handle);
614 set_url(NULL);
617 } // namespace nacl