2 * Copyright 2008, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
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.
36 #endif // NACL_WINDOWS
38 #if NACL_LINUX && defined(MOZ_X11)
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"
53 // This implementation of same-origin policy does not take document.domain
54 // element into account.
55 std::string
GetModuleOrigin(NPP instance
) {
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
);
66 if (NPN_GetValue(instance
, NPNVWindowNPObject
, &win_obj
) !=
70 if (!NPN_GetProperty(instance
, win_obj
, location_id
, &loc_value
) ||
71 !NPVARIANT_IS_OBJECT(loc_value
)) {
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
)) {
79 std::string
href(NPVARIANT_TO_STRING(href_value
).utf8characters
,
80 NPVARIANT_TO_STRING(href_value
).utf8length
);
81 origin
= nacl::UrlToOrigin(href
);
83 NPN_ReleaseVariantValue(&loc_value
);
84 NPN_ReleaseVariantValue(&href_value
);
92 NPModule::NPModule(NPP npp
, int argc
, char* argn
[], char* argv
[])
95 child_(kInvalidHandle
),
99 stream_(kInvalidHandle
),
101 monitor_thread_(kInvalidHtpHandle
),
104 monitor_thread_started_(false),
107 bitmap_shm_(kInvalidHtpHandle
),
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_
);
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_
) {
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];
136 _snprintf(variant_size
, sizeof variant_size
, "%u", sizeof(NPVariant
));
138 snprintf(variant_size
, sizeof variant_size
, "%zu", sizeof(NPVariant
));
140 argv_
[argc_
++] = strdup(variant_size
);
144 original_window_procedure_
= NULL
;
147 NPCapability capability
= { 0, 0 };
148 proxy_
= new(std::nothrow
) NPObjectProxy(this, capability
);
151 NPModule::~NPModule() {
152 if (window_
&& window_
->window
) {
154 SubclassWindow(reinterpret_cast<HWND
>(window_
->window
),
155 original_window_procedure_
);
161 child_
= kInvalidHandle
;
162 if (filename_
!= NULL
) {
168 for (int i
= 0; i
< argc_
; ++i
) {
169 free(const_cast<char *>(argv_
[i
]));
171 // Note proxy objects are deleted by Navigator.
174 NPN_ReleaseObject(proxy_
);
177 if (monitor_thread_
!= kInvalidHtpHandle
) {
178 WaitForSingleObject(monitor_thread_
, INFINITE
);
179 CloseHandle(monitor_thread_
);
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
);
189 int NPModule::Dispatch(RpcHeader
* request
, int len
) {
191 switch (request
->type
) {
192 case RPC_GET_WINDOW_OBJECT
:
193 result
= GetValue(request
, len
, NPNVWindowNPObject
);
195 case RPC_GET_PLUGIN_ELEMENT_OBJECT
:
196 result
= GetValue(request
, len
, NPNVPluginElementNPObject
);
199 result
= SetStatus(request
, len
);
201 case RPC_INVALIDATE_RECT
:
202 result
= InvalidateRect(request
, len
);
204 case RPC_FORCE_REDRAW
:
205 result
= ForceRedraw(request
, len
);
207 case RPC_CREATE_ARRAY
:
208 result
= CreateArray(request
, len
);
211 result
= OpenURL(request
, len
);
214 return NPBridge::Dispatch(request
, len
);
220 int NPModule::GetValue(RpcHeader
* request
, int len
, NPNVariable variable
) {
223 vecp
->base
= request
;
224 vecp
->length
= sizeof(RpcHeader
);
228 request
->error_code
= NPN_GetValue(npp(), variable
, &object
);
230 RpcStack
stack(this);
231 if (request
->error_code
== NPERR_NO_ERROR
) {
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
));
244 vecp
->base
= request
;
245 vecp
->length
= sizeof(RpcHeader
);
247 const char* status
= arg
.GetString();
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();
261 vecp
->base
= request
;
262 vecp
->length
= sizeof(RpcHeader
);
264 if (window_
&& window_
->window
&& nprect
) {
266 HWND hwnd
= static_cast<HWND
>(window_
->window
);
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
);
275 NPN_InvalidateRect(npp(), const_cast<NPRect
*>(nprect
));
278 return Respond(request
, vecv
, vecp
- vecv
);
281 int NPModule::ForceRedraw(RpcHeader
* request
, int len
) {
284 vecp
->base
= request
;
285 vecp
->length
= sizeof(RpcHeader
);
287 if (window_
&& window_
->window
) {
291 NPN_ForceRedraw(npp());
294 return Respond(request
, vecv
, vecp
- vecv
);
297 int NPModule::CreateArray(RpcHeader
* request
, int len
) {
300 vecp
->base
= request
;
301 vecp
->length
= sizeof(RpcHeader
);
305 NPN_GetValue(npp(), NPNVWindowNPObject
, &window
);
308 script
.utf8characters
= "new Array();";
309 script
.utf8length
= strlen(script
.utf8characters
);
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
));
329 vecp
->base
= request
;
330 vecp
->length
= sizeof(RpcHeader
);
333 const char* url
= arg
.GetString();
335 request
->error_code
= NPN_GetURLNotify(npp(), url
, NULL
, NULL
);
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
344 return Respond(request
, vecv
, vecp
- vecv
);
347 NPError
NPModule::New() {
349 request
.type
= RPC_NEW
;
352 vecp
->base
= &request
;
353 vecp
->length
= sizeof request
;
358 window_size
.width
= window_
->width
;
359 window_size
.height
= window_
->height
;
361 window_size
.width
= 0;
362 window_size
.height
= 0;
364 vecp
->base
= &window_size
;
365 vecp
->length
= sizeof window_size
;
368 clear_handle_count();
369 if (bitmap_shm_
!= kInvalidHtpHandle
) {
370 add_handle(bitmap_shm_
);
374 RpcHeader
* reply
= Request(&request
, vecv
, vecp
- vecv
, &length
);
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
);
390 // NPInstance methods
393 NPError
NPModule::Destroy(NPSavedData
** save
) {
396 request
.type
= RPC_DESTROY
;
399 vecp
->base
= &request
;
400 vecp
->length
= sizeof request
;
403 RpcHeader
* reply
= Request(&request
, vecv
, vecp
- vecv
, &length
);
405 nperr
= NPERR_INVALID_INSTANCE_ERROR
;
407 nperr
= static_cast<NPError
>(reply
->error_code
);
413 #if NACL_LINUX && defined(MOZ_X11)
415 void NPModule::EventHandler(Widget widget
,
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
) {
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
));
431 // Other types of events should be handled here.
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
;
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
);
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
),
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_
),
473 if (bitmap_shm_
== kInvalidHtpHandle
) {
474 return NPERR_GENERIC_ERROR
;
476 bitmap_data_
= Map(NULL
, bitmap_size_
,
477 kProtRead
| kProtWrite
, kMapShared
,
479 if (bitmap_data_
== kMapFailed
) {
481 bitmap_shm_
= kInvalidHtpHandle
;
483 return NPERR_GENERIC_ERROR
;
487 return NPERR_NO_ERROR
;
490 NPError
NPModule::GetValue(NPPVariable variable
, void *value
) {
492 case NPPVpluginNameString
:
493 *static_cast<const char**>(value
) = "NativeClient NPAPI bridge plug-in";
495 case NPPVpluginDescriptionString
:
496 *static_cast<const char**>(value
) =
497 "A plug-in for NPAPI based NativeClient modules.";
499 case NPPVpluginScriptableNPObject
:
500 *reinterpret_cast<NPObject
**>(value
) = GetScriptableInstance();
501 if (!*reinterpret_cast<NPObject
**>(value
)) {
502 return NPERR_GENERIC_ERROR
;
506 return NPERR_INVALID_PARAM
;
508 return NPERR_NO_ERROR
;
513 int16_t NPModule::HandleEvent(void* event
) {
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() {
523 NPN_RetainObject(proxy_
);
528 NPError
NPModule::NewStream(NPMIMEType type
, NPStream
* stream
, NPBool seekable
,
530 *stype
= NP_ASFILEONLY
;
531 return NPERR_NO_ERROR
;
534 void NPModule::StreamAsFile(NPStream
* stream
, const char* filename
) {
536 if (filename
&& UrlToOrigin(stream
->url
) == origin_
) {
537 stream_
= OpenFile(filename
);
538 if (stream_
!= kInvalidHandle
) {
539 set_filename(filename
);
546 } else if (filename
&& subprocess_
== NULL
) {
547 // This filename is for the file specified by the src attribute.
549 // check ABI version compatibility
550 NPError np
= nacl::CheckExecutableVersion(npp(), filename
);
551 if (NPERR_NO_ERROR
== np
) {
553 // Use NaCl ABI (X86 ELF).
554 set_peer_npvariant_size(12);
557 fprintf(stderr
, "Load failed: possible ABI version mismatch\n");
560 fprintf(stderr
, "Load failed: NaCl module did not come from a whitelisted"
561 " source.\nSee npapi_plugin/origin.cc for the list.");
563 NPN_GetValue(npp(), NPNVWindowNPObject
, &window
);
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
);
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
) {
584 if (filename_
== NULL
) {
585 reason
= NPRES_NETWORK_ERR
;
588 HtpHandle handle
= kInvalidHtpHandle
;
589 if (reason
== NPRES_DONE
) {
590 handle
= CreateIoDesc(stream_
);
594 request
.type
= RPC_NOTIFY_URL
;
595 request
.error_code
= reason
;
598 vecp
->base
= &request
;
599 vecp
->length
= sizeof request
;
601 RpcStack
stack(this);
602 vecp
= stack
.SetIOVec(vecp
);
604 clear_handle_count();
605 if (handle
!= kInvalidHtpHandle
) {
610 Request(&request
, vecv
, vecp
- vecv
, &length
);