1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
11 #include "base/at_exit.h"
12 #include "base/basictypes.h"
13 #include "base/logging.h"
14 #include "base/stringize_macros.h"
15 #include "net/socket/ssl_server_socket.h"
16 #include "remoting/base/plugin_thread_task_runner.h"
17 #include "remoting/host/plugin/constants.h"
18 #include "remoting/host/plugin/host_log_handler.h"
19 #include "remoting/host/plugin/host_plugin_utils.h"
20 #include "remoting/host/plugin/host_script_object.h"
21 #include "third_party/npapi/bindings/npapi.h"
22 #include "third_party/npapi/bindings/npfunctions.h"
23 #include "third_party/npapi/bindings/npruntime.h"
25 // Symbol export is handled with a separate def file on Windows.
26 #if defined (__GNUC__) && __GNUC__ >= 4
27 #define EXPORT __attribute__((visibility("default")))
33 // TODO(wez): libvpx expects these 64-bit division functions to be provided
34 // by libgcc.a, which we aren't linked against. These implementations can
35 // be removed once we have native MSVC libvpx builds for Windows.
38 int64_t __cdecl
__divdi3(int64_t a
, int64_t b
) {
41 uint64_t __cdecl
__udivdi3(uint64_t a
, uint64_t b
) {
48 using remoting::g_npnetscape_funcs
;
49 using remoting::HostLogHandler
;
50 using remoting::HostNPScriptObject
;
51 using remoting::StringFromNPIdentifier
;
55 base::AtExitManager
* g_at_exit_manager
= NULL
;
57 // NPAPI plugin implementation for remoting host.
58 // Documentation for most of the calls in this class can be found here:
59 // https://developer.mozilla.org/en/Gecko_Plugin_API_Reference/Scripting_plugins
60 class HostNPPlugin
: public remoting::PluginThreadTaskRunner::Delegate
{
62 // |mode| is the display mode of plug-in. Values:
63 // NP_EMBED: (1) Instance was created by an EMBED tag and shares the browser
64 // window with other content.
65 // NP_FULL: (2) Instance was created by a separate file and is the primary
66 // content in the window.
67 HostNPPlugin(NPP instance
, uint16 mode
)
68 : instance_(instance
),
69 scriptable_object_(NULL
) {
70 plugin_task_runner_
= new remoting::PluginThreadTaskRunner(this);
71 plugin_auto_task_runner_
=
72 new remoting::AutoThreadTaskRunner(
74 base::Bind(&remoting::PluginThreadTaskRunner::Quit
,
75 plugin_task_runner_
));
79 if (scriptable_object_
) {
80 DCHECK_EQ(scriptable_object_
->referenceCount
, 1UL);
81 g_npnetscape_funcs
->releaseobject(scriptable_object_
);
82 scriptable_object_
= NULL
;
85 // Process tasks on |plugin_task_runner_| until all references to
86 // |plugin_auto_task_runner_| have been dropped. This requires that the
87 // browser has dropped any script object references - see above.
88 plugin_auto_task_runner_
= NULL
;
89 plugin_task_runner_
->DetachAndRunShutdownLoop();
92 bool Init(int16 argc
, char** argn
, char** argv
, NPSavedData
* saved
) {
93 #if defined(OS_MACOSX)
94 // Use the modern CoreGraphics and Cocoa models when available, since
95 // QuickDraw and Carbon are deprecated.
96 // The drawing and event models don't change anything for this plugin, since
97 // none of the functions affected by the models actually do anything.
98 // This does however keep the plugin from breaking when Chromium eventually
99 // drops support for QuickDraw and Carbon, and it also keeps the browser
100 // from sending Null Events once a second to support old Carbon based
102 // Chromium should always be supporting the newer models.
104 // Sanity check to see if Chromium supports the CoreGraphics drawing model.
105 NPBool supports_core_graphics
= false;
106 NPError err
= g_npnetscape_funcs
->getvalue(instance_
,
107 NPNVsupportsCoreGraphicsBool
,
108 &supports_core_graphics
);
109 if (err
== NPERR_NO_ERROR
&& supports_core_graphics
) {
110 // Switch to CoreGraphics drawing model.
111 g_npnetscape_funcs
->setvalue(instance_
, NPPVpluginDrawingModel
,
112 reinterpret_cast<void*>(NPDrawingModelCoreGraphics
));
114 LOG(ERROR
) << "No Core Graphics support";
118 // Sanity check to see if Chromium supports the Cocoa event model.
119 NPBool supports_cocoa
= false;
120 err
= g_npnetscape_funcs
->getvalue(instance_
, NPNVsupportsCocoaBool
,
122 if (err
== NPERR_NO_ERROR
&& supports_cocoa
) {
123 // Switch to Cocoa event model.
124 g_npnetscape_funcs
->setvalue(instance_
, NPPVpluginEventModel
,
125 reinterpret_cast<void*>(NPEventModelCocoa
));
127 LOG(ERROR
) << "No Cocoa Event Model support";
131 net::EnableSSLServerSockets();
135 bool Save(NPSavedData
** saved
) {
139 NPObject
* GetScriptableObject() {
140 if (!scriptable_object_
) {
141 // Must be static. If it is a temporary, objects created by this
142 // method will fail in weird and wonderful ways later.
143 static NPClass npc_ref_object
= {
144 NP_CLASS_STRUCT_VERSION
,
158 scriptable_object_
= g_npnetscape_funcs
->createobject(instance_
,
161 return scriptable_object_
;
164 // PluginThreadTaskRunner::Delegate implementation.
165 virtual bool RunOnPluginThread(
166 base::TimeDelta delay
, void(function
)(void*), void* data
) OVERRIDE
{
167 if (delay
== base::TimeDelta()) {
168 g_npnetscape_funcs
->pluginthreadasynccall(instance_
, function
, data
);
170 base::AutoLock
auto_lock(timers_lock_
);
171 uint32_t timer_id
= g_npnetscape_funcs
->scheduletimer(
172 instance_
, delay
.InMilliseconds(), false, &NPDelayedTaskSpringboard
);
173 DelayedTask task
= {function
, data
};
174 timers_
[timer_id
] = task
;
179 void SetWindow(NPWindow
* np_window
) {
180 if (scriptable_object_
) {
181 ScriptableFromObject(scriptable_object_
)->SetWindow(np_window
);
185 static void NPDelayedTaskSpringboard(NPP npp
, uint32_t timer_id
) {
186 HostNPPlugin
* self
= reinterpret_cast<HostNPPlugin
*>(npp
->pdata
);
189 base::AutoLock
auto_lock(self
->timers_lock_
);
190 std::map
<uint32_t, DelayedTask
>::iterator it
=
191 self
->timers_
.find(timer_id
);
192 CHECK(it
!= self
->timers_
.end());
194 self
->timers_
.erase(it
);
196 task
.function(task
.data
);
200 struct ScriptableNPObject
: public NPObject
{
201 HostNPScriptObject
* scriptable_object
;
205 void (*function
)(void*);
209 static HostNPScriptObject
* ScriptableFromObject(NPObject
* obj
) {
210 return reinterpret_cast<ScriptableNPObject
*>(obj
)->scriptable_object
;
213 static NPObject
* Allocate(NPP npp
, NPClass
* aClass
) {
214 VLOG(2) << "static Allocate";
215 ScriptableNPObject
* object
=
216 reinterpret_cast<ScriptableNPObject
*>(
217 g_npnetscape_funcs
->memalloc(sizeof(ScriptableNPObject
)));
218 HostNPPlugin
* plugin
= reinterpret_cast<HostNPPlugin
*>(npp
->pdata
);
220 object
->_class
= aClass
;
221 object
->referenceCount
= 1;
222 object
->scriptable_object
=
223 new HostNPScriptObject(npp
, object
, plugin
->plugin_auto_task_runner_
);
227 static void Deallocate(NPObject
* npobj
) {
228 VLOG(2) << "static Deallocate";
231 g_npnetscape_funcs
->memfree(npobj
);
235 static void Invalidate(NPObject
* npobj
) {
237 ScriptableNPObject
* object
= reinterpret_cast<ScriptableNPObject
*>(npobj
);
238 if (object
->scriptable_object
) {
239 delete object
->scriptable_object
;
240 object
->scriptable_object
= NULL
;
245 static bool HasMethod(NPObject
* obj
, NPIdentifier method_name
) {
246 VLOG(2) << "static HasMethod";
247 HostNPScriptObject
* scriptable
= ScriptableFromObject(obj
);
248 if (!scriptable
) return false;
249 std::string method_name_string
= StringFromNPIdentifier(method_name
);
250 if (method_name_string
.empty())
252 return scriptable
->HasMethod(method_name_string
);
255 static bool InvokeDefault(NPObject
* obj
,
256 const NPVariant
* args
,
259 VLOG(2) << "static InvokeDefault";
260 HostNPScriptObject
* scriptable
= ScriptableFromObject(obj
);
261 if (!scriptable
) return false;
262 return scriptable
->InvokeDefault(args
, argCount
, result
);
265 static bool Invoke(NPObject
* obj
,
266 NPIdentifier method_name
,
267 const NPVariant
* args
,
270 VLOG(2) << "static Invoke";
271 HostNPScriptObject
* scriptable
= ScriptableFromObject(obj
);
274 std::string method_name_string
= StringFromNPIdentifier(method_name
);
275 if (method_name_string
.empty())
277 return scriptable
->Invoke(method_name_string
, args
, argCount
, result
);
280 static bool HasProperty(NPObject
* obj
, NPIdentifier property_name
) {
281 VLOG(2) << "static HasProperty";
282 HostNPScriptObject
* scriptable
= ScriptableFromObject(obj
);
283 if (!scriptable
) return false;
284 std::string property_name_string
= StringFromNPIdentifier(property_name
);
285 if (property_name_string
.empty())
287 return scriptable
->HasProperty(property_name_string
);
290 static bool GetProperty(NPObject
* obj
,
291 NPIdentifier property_name
,
293 VLOG(2) << "static GetProperty";
294 HostNPScriptObject
* scriptable
= ScriptableFromObject(obj
);
295 if (!scriptable
) return false;
296 std::string property_name_string
= StringFromNPIdentifier(property_name
);
297 if (property_name_string
.empty())
299 return scriptable
->GetProperty(property_name_string
, result
);
302 static bool SetProperty(NPObject
* obj
,
303 NPIdentifier property_name
,
304 const NPVariant
* value
) {
305 VLOG(2) << "static SetProperty";
306 HostNPScriptObject
* scriptable
= ScriptableFromObject(obj
);
307 if (!scriptable
) return false;
308 std::string property_name_string
= StringFromNPIdentifier(property_name
);
309 if (property_name_string
.empty())
311 return scriptable
->SetProperty(property_name_string
, value
);
314 static bool RemoveProperty(NPObject
* obj
, NPIdentifier property_name
) {
315 VLOG(2) << "static RemoveProperty";
316 HostNPScriptObject
* scriptable
= ScriptableFromObject(obj
);
317 if (!scriptable
) return false;
318 std::string property_name_string
= StringFromNPIdentifier(property_name
);
319 if (property_name_string
.empty())
321 return scriptable
->RemoveProperty(property_name_string
);
324 static bool Enumerate(NPObject
* obj
,
325 NPIdentifier
** value
,
327 VLOG(2) << "static Enumerate";
328 HostNPScriptObject
* scriptable
= ScriptableFromObject(obj
);
329 if (!scriptable
) return false;
330 std::vector
<std::string
> values
;
331 bool is_good
= scriptable
->Enumerate(&values
);
333 *count
= values
.size();
334 *value
= reinterpret_cast<NPIdentifier
*>(
335 g_npnetscape_funcs
->memalloc(sizeof(NPIdentifier
) * (*count
)));
336 for (uint32_t i
= 0; i
< *count
; ++i
) {
338 g_npnetscape_funcs
->getstringidentifier(values
[i
].c_str());
345 NPObject
* scriptable_object_
;
347 scoped_refptr
<remoting::PluginThreadTaskRunner
> plugin_task_runner_
;
348 scoped_refptr
<remoting::AutoThreadTaskRunner
> plugin_auto_task_runner_
;
350 std::map
<uint32_t, DelayedTask
> timers_
;
351 base::Lock timers_lock_
;
354 // Utility functions to map NPAPI Entry Points to C++ Objects.
355 HostNPPlugin
* PluginFromInstance(NPP instance
) {
356 return reinterpret_cast<HostNPPlugin
*>(instance
->pdata
);
359 NPError
CreatePlugin(NPMIMEType pluginType
,
365 NPSavedData
* saved
) {
366 VLOG(2) << "CreatePlugin";
368 // Register a global log handler.
369 // The LogMessage registration code is not thread-safe, so we need to perform
370 // this while we're running in a single thread.
371 HostLogHandler::RegisterLogMessageHandler();
373 HostNPPlugin
* plugin
= new HostNPPlugin(instance
, mode
);
374 instance
->pdata
= plugin
;
375 if (!plugin
->Init(argc
, argn
, argv
, saved
)) {
377 instance
->pdata
= NULL
;
378 return NPERR_INVALID_PLUGIN_ERROR
;
380 return NPERR_NO_ERROR
;
384 NPError
DestroyPlugin(NPP instance
,
385 NPSavedData
** save
) {
386 VLOG(2) << "DestroyPlugin";
388 // Normally, we would unregister the global log handler that we registered
389 // in CreatePlugin. However, the LogHandler registration code is not thread-
390 // safe so we could crash if we update (register or unregister) the
391 // LogHandler while it's being read on another thread.
392 // At this point, all our threads should be shutdown, but it's safer to leave
393 // the handler registered until we're completely destroyed.
395 HostNPPlugin
* plugin
= PluginFromInstance(instance
);
399 instance
->pdata
= NULL
;
400 return NPERR_NO_ERROR
;
402 return NPERR_INVALID_PLUGIN_ERROR
;
406 NPError
GetValue(NPP instance
, NPPVariable variable
, void* value
) {
409 VLOG(2) << "GetValue - default " << variable
;
410 return NPERR_GENERIC_ERROR
;
411 case NPPVpluginNameString
:
412 VLOG(2) << "GetValue - name string";
413 *reinterpret_cast<const char**>(value
) = HOST_PLUGIN_NAME
;
415 case NPPVpluginDescriptionString
:
416 VLOG(2) << "GetValue - description string";
417 *reinterpret_cast<const char**>(value
) = HOST_PLUGIN_DESCRIPTION
;
419 case NPPVpluginNeedsXEmbed
:
420 VLOG(2) << "GetValue - NeedsXEmbed";
421 *(static_cast<NPBool
*>(value
)) = true;
423 case NPPVpluginScriptableNPObject
:
424 VLOG(2) << "GetValue - scriptable object";
425 HostNPPlugin
* plugin
= PluginFromInstance(instance
);
427 return NPERR_INVALID_PLUGIN_ERROR
;
428 NPObject
* scriptable_object
= plugin
->GetScriptableObject();
429 g_npnetscape_funcs
->retainobject(scriptable_object
);
430 *reinterpret_cast<NPObject
**>(value
) = scriptable_object
;
433 return NPERR_NO_ERROR
;
436 NPError
HandleEvent(NPP instance
, void* ev
) {
437 VLOG(2) << "HandleEvent";
438 return NPERR_NO_ERROR
;
441 NPError
SetWindow(NPP instance
, NPWindow
* pNPWindow
) {
442 VLOG(2) << "SetWindow";
443 HostNPPlugin
* plugin
= PluginFromInstance(instance
);
445 plugin
->SetWindow(pNPWindow
);
447 return NPERR_NO_ERROR
;
453 HMODULE g_hModule
= NULL
;
455 BOOL APIENTRY
DllMain(HMODULE hModule
, DWORD dwReason
, LPVOID lpReserved
) {
457 case DLL_PROCESS_ATTACH
:
459 DisableThreadLibraryCalls(hModule
);
461 case DLL_PROCESS_DETACH
:
462 case DLL_THREAD_ATTACH
:
463 case DLL_THREAD_DETACH
:
470 // The actual required NPAPI Entry points
474 EXPORT NPError API_CALL
NP_GetEntryPoints(NPPluginFuncs
* nppfuncs
) {
475 VLOG(2) << "NP_GetEntryPoints";
476 nppfuncs
->version
= (NP_VERSION_MAJOR
<< 8) | NP_VERSION_MINOR
;
477 nppfuncs
->newp
= &CreatePlugin
;
478 nppfuncs
->destroy
= &DestroyPlugin
;
479 nppfuncs
->getvalue
= &GetValue
;
480 nppfuncs
->event
= &HandleEvent
;
481 nppfuncs
->setwindow
= &SetWindow
;
483 return NPERR_NO_ERROR
;
486 EXPORT NPError API_CALL
NP_Initialize(NPNetscapeFuncs
* npnetscape_funcs
487 #if defined(OS_POSIX) && !defined(OS_MACOSX)
488 , NPPluginFuncs
* nppfuncs
491 VLOG(2) << "NP_Initialize";
492 if (g_at_exit_manager
)
493 return NPERR_MODULE_LOAD_FAILED_ERROR
;
495 if(npnetscape_funcs
== NULL
)
496 return NPERR_INVALID_FUNCTABLE_ERROR
;
498 if(((npnetscape_funcs
->version
& 0xff00) >> 8) > NP_VERSION_MAJOR
)
499 return NPERR_INCOMPATIBLE_VERSION_ERROR
;
501 g_at_exit_manager
= new base::AtExitManager
;
502 g_npnetscape_funcs
= npnetscape_funcs
;
503 #if defined(OS_POSIX) && !defined(OS_MACOSX)
504 NP_GetEntryPoints(nppfuncs
);
506 return NPERR_NO_ERROR
;
509 EXPORT NPError API_CALL
NP_Shutdown() {
510 VLOG(2) << "NP_Shutdown";
511 delete g_at_exit_manager
;
512 g_at_exit_manager
= NULL
;
513 return NPERR_NO_ERROR
;
516 #if defined(OS_POSIX) && !defined(OS_MACOSX)
517 EXPORT
const char* API_CALL
NP_GetMIMEDescription(void) {
518 VLOG(2) << "NP_GetMIMEDescription";
519 return HOST_PLUGIN_MIME_TYPE
"::";
522 EXPORT NPError API_CALL
NP_GetValue(void* npp
,
523 NPPVariable variable
,
525 return GetValue((NPP
)npp
, variable
, value
);