Revert "Merged all Chromoting Host code into remoting_core.dll (Windows)."
[chromium-blink-merge.git] / remoting / host / plugin / host_plugin.cc
bloba55c309206afc35b7714971609769d6c9da7f8ee
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.
5 #include <stdio.h>
6 #include <string.h>
8 #include <string>
9 #include <vector>
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")))
28 #else
29 #define EXPORT
30 #endif
32 #if defined(OS_WIN)
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.
36 extern "C" {
38 int64_t __cdecl __divdi3(int64_t a, int64_t b) {
39 return a / b;
41 uint64_t __cdecl __udivdi3(uint64_t a, uint64_t b) {
42 return a / b;
46 #endif
48 using remoting::g_npnetscape_funcs;
49 using remoting::HostLogHandler;
50 using remoting::HostNPScriptObject;
51 using remoting::StringFromNPIdentifier;
53 namespace {
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 {
61 public:
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(
73 plugin_task_runner_,
74 base::Bind(&remoting::PluginThreadTaskRunner::Quit,
75 plugin_task_runner_));
78 ~HostNPPlugin() {
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
101 // timers.
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));
113 } else {
114 LOG(ERROR) << "No Core Graphics support";
115 return false;
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,
121 &supports_cocoa);
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));
126 } else {
127 LOG(ERROR) << "No Cocoa Event Model support";
128 return false;
130 #endif // OS_MACOSX
131 net::EnableSSLServerSockets();
132 return true;
135 bool Save(NPSavedData** saved) {
136 return true;
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,
145 &Allocate,
146 &Deallocate,
147 &Invalidate,
148 &HasMethod,
149 &Invoke,
150 &InvokeDefault,
151 &HasProperty,
152 &GetProperty,
153 &SetProperty,
154 &RemoveProperty,
155 &Enumerate,
156 NULL
158 scriptable_object_ = g_npnetscape_funcs->createobject(instance_,
159 &npc_ref_object);
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);
169 } else {
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;
176 return true;
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);
187 DelayedTask task;
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());
193 task = it->second;
194 self->timers_.erase(it);
196 task.function(task.data);
199 private:
200 struct ScriptableNPObject : public NPObject {
201 HostNPScriptObject* scriptable_object;
204 struct DelayedTask {
205 void (*function)(void*);
206 void* data;
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_);
224 return object;
227 static void Deallocate(NPObject* npobj) {
228 VLOG(2) << "static Deallocate";
229 if (npobj) {
230 Invalidate(npobj);
231 g_npnetscape_funcs->memfree(npobj);
235 static void Invalidate(NPObject* npobj) {
236 if (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())
251 return false;
252 return scriptable->HasMethod(method_name_string);
255 static bool InvokeDefault(NPObject* obj,
256 const NPVariant* args,
257 uint32_t argCount,
258 NPVariant* result) {
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,
268 uint32_t argCount,
269 NPVariant* result) {
270 VLOG(2) << "static Invoke";
271 HostNPScriptObject* scriptable = ScriptableFromObject(obj);
272 if (!scriptable)
273 return false;
274 std::string method_name_string = StringFromNPIdentifier(method_name);
275 if (method_name_string.empty())
276 return false;
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())
286 return false;
287 return scriptable->HasProperty(property_name_string);
290 static bool GetProperty(NPObject* obj,
291 NPIdentifier property_name,
292 NPVariant* result) {
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())
298 return false;
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())
310 return false;
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())
320 return false;
321 return scriptable->RemoveProperty(property_name_string);
324 static bool Enumerate(NPObject* obj,
325 NPIdentifier** value,
326 uint32_t* count) {
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);
332 if (is_good) {
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) {
337 (*value)[i] =
338 g_npnetscape_funcs->getstringidentifier(values[i].c_str());
341 return is_good;
344 NPP instance_;
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,
360 NPP instance,
361 uint16 mode,
362 int16 argc,
363 char** argn,
364 char** argv,
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)) {
376 delete plugin;
377 instance->pdata = NULL;
378 return NPERR_INVALID_PLUGIN_ERROR;
379 } else {
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);
396 if (plugin) {
397 plugin->Save(save);
398 delete plugin;
399 instance->pdata = NULL;
400 return NPERR_NO_ERROR;
401 } else {
402 return NPERR_INVALID_PLUGIN_ERROR;
406 NPError GetValue(NPP instance, NPPVariable variable, void* value) {
407 switch(variable) {
408 default:
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;
414 break;
415 case NPPVpluginDescriptionString:
416 VLOG(2) << "GetValue - description string";
417 *reinterpret_cast<const char**>(value) = HOST_PLUGIN_DESCRIPTION;
418 break;
419 case NPPVpluginNeedsXEmbed:
420 VLOG(2) << "GetValue - NeedsXEmbed";
421 *(static_cast<NPBool*>(value)) = true;
422 break;
423 case NPPVpluginScriptableNPObject:
424 VLOG(2) << "GetValue - scriptable object";
425 HostNPPlugin* plugin = PluginFromInstance(instance);
426 if (!plugin)
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;
431 break;
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);
444 if (plugin) {
445 plugin->SetWindow(pNPWindow);
447 return NPERR_NO_ERROR;
450 } // namespace
452 #if defined(OS_WIN)
453 HMODULE g_hModule = NULL;
455 BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) {
456 switch (dwReason) {
457 case DLL_PROCESS_ATTACH:
458 g_hModule = hModule;
459 DisableThreadLibraryCalls(hModule);
460 break;
461 case DLL_PROCESS_DETACH:
462 case DLL_THREAD_ATTACH:
463 case DLL_THREAD_DETACH:
464 break;
466 return TRUE;
468 #endif
470 // The actual required NPAPI Entry points
472 extern "C" {
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
489 #endif
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);
505 #endif
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,
524 void* value) {
525 return GetValue((NPP)npp, variable, value);
527 #endif
529 } // extern "C"