Index: remoting/host/plugin/host_plugin.cc |
diff --git a/remoting/host/plugin/host_plugin.cc b/remoting/host/plugin/host_plugin.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..213ea89d78b3e7f29f181f78f5adb1413e0c991b |
--- /dev/null |
+++ b/remoting/host/plugin/host_plugin.cc |
@@ -0,0 +1,574 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <stdio.h> |
+#include <string.h> |
+ |
+#include <string> |
+#include <vector> |
+ |
+#include "base/at_exit.h" |
+#include "base/basictypes.h" |
+#include "base/command_line.h" |
+#include "base/logging.h" |
+#include "base/strings/stringize_macros.h" |
+#include "net/socket/ssl_server_socket.h" |
+#include "remoting/base/plugin_thread_task_runner.h" |
+#include "remoting/base/resources.h" |
+#include "remoting/base/string_resources.h" |
+#include "remoting/host/plugin/host_log_handler.h" |
+#include "remoting/host/plugin/host_plugin_utils.h" |
+#include "remoting/host/plugin/host_script_object.h" |
+#if defined(OS_WIN) |
+#include "ui/gfx/win/dpi.h" |
+#endif |
+#include "third_party/npapi/bindings/npapi.h" |
+#include "third_party/npapi/bindings/npfunctions.h" |
+#include "third_party/npapi/bindings/npruntime.h" |
+#include "ui/base/l10n/l10n_util.h" |
+ |
+// Symbol export is handled with a separate def file on Windows. |
+#if defined (__GNUC__) && __GNUC__ >= 4 |
+#define EXPORT __attribute__((visibility("default"))) |
+#else |
+#define EXPORT |
+#endif |
+ |
+#if defined(OS_WIN) |
+// TODO(wez): libvpx expects these 64-bit division functions to be provided |
+// by libgcc.a, which we aren't linked against. These implementations can |
+// be removed once we have native MSVC libvpx builds for Windows. |
+extern "C" { |
+ |
+int64_t __cdecl __divdi3(int64_t a, int64_t b) { |
+ return a / b; |
+} |
+uint64_t __cdecl __udivdi3(uint64_t a, uint64_t b) { |
+ return a / b; |
+} |
+ |
+} |
+#endif |
+ |
+using remoting::g_npnetscape_funcs; |
+using remoting::HostLogHandler; |
+using remoting::HostNPScriptObject; |
+using remoting::StringFromNPIdentifier; |
+ |
+namespace { |
+ |
+bool g_initialized = false; |
+ |
+base::AtExitManager* g_at_exit_manager = NULL; |
+ |
+// The plugin name and description returned by GetValue(). |
+std::string* g_ui_name = NULL; |
+std::string* g_ui_description = NULL; |
+ |
+// NPAPI plugin implementation for remoting host. |
+// Documentation for most of the calls in this class can be found here: |
+// https://developer.mozilla.org/en/Gecko_Plugin_API_Reference/Scripting_plugins |
+class HostNPPlugin : public remoting::PluginThreadTaskRunner::Delegate { |
+ public: |
+ // |mode| is the display mode of plug-in. Values: |
+ // NP_EMBED: (1) Instance was created by an EMBED tag and shares the browser |
+ // window with other content. |
+ // NP_FULL: (2) Instance was created by a separate file and is the primary |
+ // content in the window. |
+ HostNPPlugin(NPP instance, uint16 mode) |
+ : instance_(instance), |
+ scriptable_object_(NULL) { |
+ plugin_task_runner_ = new remoting::PluginThreadTaskRunner(this); |
+ plugin_auto_task_runner_ = |
+ new remoting::AutoThreadTaskRunner( |
+ plugin_task_runner_, |
+ base::Bind(&remoting::PluginThreadTaskRunner::Quit, |
+ plugin_task_runner_)); |
+ } |
+ |
+ virtual ~HostNPPlugin() { |
+ if (scriptable_object_) { |
+ DCHECK_EQ(scriptable_object_->referenceCount, 1UL); |
+ g_npnetscape_funcs->releaseobject(scriptable_object_); |
+ scriptable_object_ = NULL; |
+ } |
+ |
+ // Process tasks on |plugin_task_runner_| until all references to |
+ // |plugin_auto_task_runner_| have been dropped. This requires that the |
+ // browser has dropped any script object references - see above. |
+ plugin_auto_task_runner_ = NULL; |
+ plugin_task_runner_->DetachAndRunShutdownLoop(); |
+ } |
+ |
+ bool Init(int16 argc, char** argn, char** argv, NPSavedData* saved) { |
+#if defined(OS_MACOSX) |
+ // Use the modern CoreGraphics and Cocoa models when available, since |
+ // QuickDraw and Carbon are deprecated. |
+ // The drawing and event models don't change anything for this plugin, since |
+ // none of the functions affected by the models actually do anything. |
+ // This does however keep the plugin from breaking when Chromium eventually |
+ // drops support for QuickDraw and Carbon, and it also keeps the browser |
+ // from sending Null Events once a second to support old Carbon based |
+ // timers. |
+ // Chromium should always be supporting the newer models. |
+ |
+ // Sanity check to see if Chromium supports the CoreGraphics drawing model. |
+ NPBool supports_core_graphics = false; |
+ NPError err = g_npnetscape_funcs->getvalue(instance_, |
+ NPNVsupportsCoreGraphicsBool, |
+ &supports_core_graphics); |
+ if (err == NPERR_NO_ERROR && supports_core_graphics) { |
+ // Switch to CoreGraphics drawing model. |
+ g_npnetscape_funcs->setvalue(instance_, NPPVpluginDrawingModel, |
+ reinterpret_cast<void*>(NPDrawingModelCoreGraphics)); |
+ } else { |
+ LOG(ERROR) << "No Core Graphics support"; |
+ return false; |
+ } |
+ |
+ // Sanity check to see if Chromium supports the Cocoa event model. |
+ NPBool supports_cocoa = false; |
+ err = g_npnetscape_funcs->getvalue(instance_, NPNVsupportsCocoaBool, |
+ &supports_cocoa); |
+ if (err == NPERR_NO_ERROR && supports_cocoa) { |
+ // Switch to Cocoa event model. |
+ g_npnetscape_funcs->setvalue(instance_, NPPVpluginEventModel, |
+ reinterpret_cast<void*>(NPEventModelCocoa)); |
+ } else { |
+ LOG(ERROR) << "No Cocoa Event Model support"; |
+ return false; |
+ } |
+#endif // OS_MACOSX |
+ net::EnableSSLServerSockets(); |
+ return true; |
+ } |
+ |
+ bool Save(NPSavedData** saved) { |
+ return true; |
+ } |
+ |
+ NPObject* GetScriptableObject() { |
+ if (!scriptable_object_) { |
+ // Must be static. If it is a temporary, objects created by this |
+ // method will fail in weird and wonderful ways later. |
+ static NPClass npc_ref_object = { |
+ NP_CLASS_STRUCT_VERSION, |
+ &Allocate, |
+ &Deallocate, |
+ &Invalidate, |
+ &HasMethod, |
+ &Invoke, |
+ &InvokeDefault, |
+ &HasProperty, |
+ &GetProperty, |
+ &SetProperty, |
+ &RemoveProperty, |
+ &Enumerate, |
+ NULL |
+ }; |
+ scriptable_object_ = g_npnetscape_funcs->createobject(instance_, |
+ &npc_ref_object); |
+ } |
+ return scriptable_object_; |
+ } |
+ |
+ // PluginThreadTaskRunner::Delegate implementation. |
+ virtual bool RunOnPluginThread( |
+ base::TimeDelta delay, void(function)(void*), void* data) OVERRIDE { |
+ if (delay == base::TimeDelta()) { |
+ g_npnetscape_funcs->pluginthreadasynccall(instance_, function, data); |
+ } else { |
+ base::AutoLock auto_lock(timers_lock_); |
+ uint32_t timer_id = g_npnetscape_funcs->scheduletimer( |
+ instance_, delay.InMilliseconds(), false, &NPDelayedTaskSpringboard); |
+ DelayedTask task = {function, data}; |
+ timers_[timer_id] = task; |
+ } |
+ return true; |
+ } |
+ |
+ void SetWindow(NPWindow* np_window) { |
+ if (scriptable_object_) { |
+ ScriptableFromObject(scriptable_object_)->SetWindow(np_window); |
+ } |
+ } |
+ |
+ static void NPDelayedTaskSpringboard(NPP npp, uint32_t timer_id) { |
+ HostNPPlugin* self = reinterpret_cast<HostNPPlugin*>(npp->pdata); |
+ DelayedTask task; |
+ { |
+ base::AutoLock auto_lock(self->timers_lock_); |
+ std::map<uint32_t, DelayedTask>::iterator it = |
+ self->timers_.find(timer_id); |
+ CHECK(it != self->timers_.end()); |
+ task = it->second; |
+ self->timers_.erase(it); |
+ } |
+ task.function(task.data); |
+ } |
+ |
+ private: |
+ struct ScriptableNPObject : public NPObject { |
+ HostNPScriptObject* scriptable_object; |
+ }; |
+ |
+ struct DelayedTask { |
+ void (*function)(void*); |
+ void* data; |
+ }; |
+ |
+ static HostNPScriptObject* ScriptableFromObject(NPObject* obj) { |
+ return reinterpret_cast<ScriptableNPObject*>(obj)->scriptable_object; |
+ } |
+ |
+ static NPObject* Allocate(NPP npp, NPClass* aClass) { |
+ VLOG(2) << "static Allocate"; |
+ ScriptableNPObject* object = |
+ reinterpret_cast<ScriptableNPObject*>( |
+ g_npnetscape_funcs->memalloc(sizeof(ScriptableNPObject))); |
+ HostNPPlugin* plugin = reinterpret_cast<HostNPPlugin*>(npp->pdata); |
+ |
+ object->_class = aClass; |
+ object->referenceCount = 1; |
+ object->scriptable_object = |
+ new HostNPScriptObject(npp, object, plugin->plugin_auto_task_runner_); |
+ return object; |
+ } |
+ |
+ static void Deallocate(NPObject* npobj) { |
+ VLOG(2) << "static Deallocate"; |
+ if (npobj) { |
+ Invalidate(npobj); |
+ g_npnetscape_funcs->memfree(npobj); |
+ } |
+ } |
+ |
+ static void Invalidate(NPObject* npobj) { |
+ if (npobj) { |
+ ScriptableNPObject* object = reinterpret_cast<ScriptableNPObject*>(npobj); |
+ if (object->scriptable_object) { |
+ delete object->scriptable_object; |
+ object->scriptable_object = NULL; |
+ } |
+ } |
+ } |
+ |
+ static bool HasMethod(NPObject* obj, NPIdentifier method_name) { |
+ VLOG(2) << "static HasMethod"; |
+ HostNPScriptObject* scriptable = ScriptableFromObject(obj); |
+ if (!scriptable) return false; |
+ std::string method_name_string = StringFromNPIdentifier(method_name); |
+ if (method_name_string.empty()) |
+ return false; |
+ return scriptable->HasMethod(method_name_string); |
+ } |
+ |
+ static bool InvokeDefault(NPObject* obj, |
+ const NPVariant* args, |
+ uint32_t argCount, |
+ NPVariant* result) { |
+ VLOG(2) << "static InvokeDefault"; |
+ HostNPScriptObject* scriptable = ScriptableFromObject(obj); |
+ if (!scriptable) return false; |
+ return scriptable->InvokeDefault(args, argCount, result); |
+ } |
+ |
+ static bool Invoke(NPObject* obj, |
+ NPIdentifier method_name, |
+ const NPVariant* args, |
+ uint32_t argCount, |
+ NPVariant* result) { |
+ VLOG(2) << "static Invoke"; |
+ HostNPScriptObject* scriptable = ScriptableFromObject(obj); |
+ if (!scriptable) |
+ return false; |
+ std::string method_name_string = StringFromNPIdentifier(method_name); |
+ if (method_name_string.empty()) |
+ return false; |
+ return scriptable->Invoke(method_name_string, args, argCount, result); |
+ } |
+ |
+ static bool HasProperty(NPObject* obj, NPIdentifier property_name) { |
+ VLOG(2) << "static HasProperty"; |
+ HostNPScriptObject* scriptable = ScriptableFromObject(obj); |
+ if (!scriptable) return false; |
+ std::string property_name_string = StringFromNPIdentifier(property_name); |
+ if (property_name_string.empty()) |
+ return false; |
+ return scriptable->HasProperty(property_name_string); |
+ } |
+ |
+ static bool GetProperty(NPObject* obj, |
+ NPIdentifier property_name, |
+ NPVariant* result) { |
+ VLOG(2) << "static GetProperty"; |
+ HostNPScriptObject* scriptable = ScriptableFromObject(obj); |
+ if (!scriptable) return false; |
+ std::string property_name_string = StringFromNPIdentifier(property_name); |
+ if (property_name_string.empty()) |
+ return false; |
+ return scriptable->GetProperty(property_name_string, result); |
+ } |
+ |
+ static bool SetProperty(NPObject* obj, |
+ NPIdentifier property_name, |
+ const NPVariant* value) { |
+ VLOG(2) << "static SetProperty"; |
+ HostNPScriptObject* scriptable = ScriptableFromObject(obj); |
+ if (!scriptable) return false; |
+ std::string property_name_string = StringFromNPIdentifier(property_name); |
+ if (property_name_string.empty()) |
+ return false; |
+ return scriptable->SetProperty(property_name_string, value); |
+ } |
+ |
+ static bool RemoveProperty(NPObject* obj, NPIdentifier property_name) { |
+ VLOG(2) << "static RemoveProperty"; |
+ HostNPScriptObject* scriptable = ScriptableFromObject(obj); |
+ if (!scriptable) return false; |
+ std::string property_name_string = StringFromNPIdentifier(property_name); |
+ if (property_name_string.empty()) |
+ return false; |
+ return scriptable->RemoveProperty(property_name_string); |
+ } |
+ |
+ static bool Enumerate(NPObject* obj, |
+ NPIdentifier** value, |
+ uint32_t* count) { |
+ VLOG(2) << "static Enumerate"; |
+ HostNPScriptObject* scriptable = ScriptableFromObject(obj); |
+ if (!scriptable) return false; |
+ std::vector<std::string> values; |
+ bool is_good = scriptable->Enumerate(&values); |
+ if (is_good) { |
+ *count = values.size(); |
+ *value = reinterpret_cast<NPIdentifier*>( |
+ g_npnetscape_funcs->memalloc(sizeof(NPIdentifier) * (*count))); |
+ for (uint32_t i = 0; i < *count; ++i) { |
+ (*value)[i] = |
+ g_npnetscape_funcs->getstringidentifier(values[i].c_str()); |
+ } |
+ } |
+ return is_good; |
+ } |
+ |
+ NPP instance_; |
+ NPObject* scriptable_object_; |
+ |
+ scoped_refptr<remoting::PluginThreadTaskRunner> plugin_task_runner_; |
+ scoped_refptr<remoting::AutoThreadTaskRunner> plugin_auto_task_runner_; |
+ |
+ std::map<uint32_t, DelayedTask> timers_; |
+ base::Lock timers_lock_; |
+}; |
+ |
+void InitializePlugin() { |
+ if (g_initialized) |
+ return; |
+ |
+ g_initialized = true; |
+ g_at_exit_manager = new base::AtExitManager; |
+ |
+ // Init an empty command line for common objects that use it. |
+ base::CommandLine::Init(0, NULL); |
+ |
+ if (remoting::LoadResources("")) { |
+ g_ui_name = new std::string( |
+ l10n_util::GetStringUTF8(IDS_REMOTING_HOST_PLUGIN_NAME)); |
+ g_ui_description = new std::string( |
+ l10n_util::GetStringUTF8(IDS_REMOTING_HOST_PLUGIN_DESCRIPTION)); |
+ } else { |
+ g_ui_name = new std::string(); |
+ g_ui_description = new std::string(); |
+ } |
+} |
+ |
+void ShutdownPlugin() { |
+ delete g_ui_name; |
+ delete g_ui_description; |
+ |
+ remoting::UnloadResources(); |
+ |
+ delete g_at_exit_manager; |
+} |
+ |
+// Utility functions to map NPAPI Entry Points to C++ Objects. |
+HostNPPlugin* PluginFromInstance(NPP instance) { |
+ return reinterpret_cast<HostNPPlugin*>(instance->pdata); |
+} |
+ |
+NPError CreatePlugin(NPMIMEType pluginType, |
+ NPP instance, |
+ uint16 mode, |
+ int16 argc, |
+ char** argn, |
+ char** argv, |
+ NPSavedData* saved) { |
+ VLOG(2) << "CreatePlugin"; |
+ |
+ // Register a global log handler. |
+ // The LogMessage registration code is not thread-safe, so we need to perform |
+ // this while we're running in a single thread. |
+ HostLogHandler::RegisterLogMessageHandler(); |
+ |
+ HostNPPlugin* plugin = new HostNPPlugin(instance, mode); |
+ instance->pdata = plugin; |
+ if (!plugin->Init(argc, argn, argv, saved)) { |
+ delete plugin; |
+ instance->pdata = NULL; |
+ return NPERR_INVALID_PLUGIN_ERROR; |
+ } else { |
+ return NPERR_NO_ERROR; |
+ } |
+} |
+ |
+NPError DestroyPlugin(NPP instance, |
+ NPSavedData** save) { |
+ VLOG(2) << "DestroyPlugin"; |
+ |
+ // Normally, we would unregister the global log handler that we registered |
+ // in CreatePlugin. However, the LogHandler registration code is not thread- |
+ // safe so we could crash if we update (register or unregister) the |
+ // LogHandler while it's being read on another thread. |
+ // At this point, all our threads should be shutdown, but it's safer to leave |
+ // the handler registered until we're completely destroyed. |
+ |
+ HostNPPlugin* plugin = PluginFromInstance(instance); |
+ if (plugin) { |
+ plugin->Save(save); |
+ delete plugin; |
+ instance->pdata = NULL; |
+ return NPERR_NO_ERROR; |
+ } else { |
+ return NPERR_INVALID_PLUGIN_ERROR; |
+ } |
+} |
+ |
+NPError GetValue(NPP instance, NPPVariable variable, void* value) { |
+ // NP_GetValue() can be called before NP_Initialize(). |
+ InitializePlugin(); |
+ |
+ switch (variable) { |
+ default: |
+ VLOG(2) << "GetValue - default " << variable; |
+ return NPERR_GENERIC_ERROR; |
+ case NPPVpluginNameString: |
+ VLOG(2) << "GetValue - name string"; |
+ *reinterpret_cast<const char**>(value) = g_ui_name->c_str(); |
+ break; |
+ case NPPVpluginDescriptionString: |
+ VLOG(2) << "GetValue - description string"; |
+ *reinterpret_cast<const char**>(value) = g_ui_description->c_str(); |
+ break; |
+ case NPPVpluginNeedsXEmbed: |
+ VLOG(2) << "GetValue - NeedsXEmbed"; |
+ *(static_cast<NPBool*>(value)) = true; |
+ break; |
+ case NPPVpluginScriptableNPObject: |
+ VLOG(2) << "GetValue - scriptable object"; |
+ HostNPPlugin* plugin = PluginFromInstance(instance); |
+ if (!plugin) |
+ return NPERR_INVALID_PLUGIN_ERROR; |
+ NPObject* scriptable_object = plugin->GetScriptableObject(); |
+ g_npnetscape_funcs->retainobject(scriptable_object); |
+ *reinterpret_cast<NPObject**>(value) = scriptable_object; |
+ break; |
+ } |
+ return NPERR_NO_ERROR; |
+} |
+ |
+NPError HandleEvent(NPP instance, void* ev) { |
+ VLOG(2) << "HandleEvent"; |
+ return NPERR_NO_ERROR; |
+} |
+ |
+NPError SetWindow(NPP instance, NPWindow* pNPWindow) { |
+ VLOG(2) << "SetWindow"; |
+ HostNPPlugin* plugin = PluginFromInstance(instance); |
+ if (plugin) { |
+ plugin->SetWindow(pNPWindow); |
+ } |
+ return NPERR_NO_ERROR; |
+} |
+ |
+} // namespace |
+ |
+#if defined(OS_WIN) |
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { |
+ switch (dwReason) { |
+ case DLL_PROCESS_ATTACH: |
+ DisableThreadLibraryCalls(hModule); |
+ break; |
+ case DLL_PROCESS_DETACH: |
+ case DLL_THREAD_ATTACH: |
+ case DLL_THREAD_DETACH: |
+ break; |
+ } |
+ return TRUE; |
+} |
+#endif |
+ |
+// The actual required NPAPI Entry points |
+ |
+extern "C" { |
+ |
+EXPORT NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* nppfuncs) { |
+ VLOG(2) << "NP_GetEntryPoints"; |
+ nppfuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; |
+ nppfuncs->newp = &CreatePlugin; |
+ nppfuncs->destroy = &DestroyPlugin; |
+ nppfuncs->getvalue = &GetValue; |
+ nppfuncs->event = &HandleEvent; |
+ nppfuncs->setwindow = &SetWindow; |
+ |
+ return NPERR_NO_ERROR; |
+} |
+ |
+EXPORT NPError API_CALL NP_Initialize(NPNetscapeFuncs* npnetscape_funcs |
+#if defined(OS_POSIX) && !defined(OS_MACOSX) |
+ , NPPluginFuncs* nppfuncs |
+#endif |
+ ) { |
+ VLOG(2) << "NP_Initialize"; |
+ InitializePlugin(); |
+ |
+ if (npnetscape_funcs == NULL) |
+ return NPERR_INVALID_FUNCTABLE_ERROR; |
+ |
+ if (((npnetscape_funcs->version & 0xff00) >> 8) > NP_VERSION_MAJOR) |
+ return NPERR_INCOMPATIBLE_VERSION_ERROR; |
+ |
+ g_npnetscape_funcs = npnetscape_funcs; |
+#if defined(OS_POSIX) && !defined(OS_MACOSX) |
+ NP_GetEntryPoints(nppfuncs); |
+#endif |
+ |
+#if defined(OS_WIN) |
+ gfx::EnableHighDPISupport(); |
+#endif |
+ |
+ return NPERR_NO_ERROR; |
+} |
+ |
+EXPORT NPError API_CALL NP_Shutdown() { |
+ VLOG(2) << "NP_Shutdown"; |
+ ShutdownPlugin(); |
+ |
+ return NPERR_NO_ERROR; |
+} |
+ |
+#if defined(OS_POSIX) && !defined(OS_MACOSX) |
+EXPORT const char* API_CALL NP_GetMIMEDescription(void) { |
+ VLOG(2) << "NP_GetMIMEDescription"; |
+ return STRINGIZE(HOST_PLUGIN_MIME_TYPE) "::"; |
+} |
+ |
+EXPORT NPError API_CALL NP_GetValue(void* npp, |
+ NPPVariable variable, |
+ void* value) { |
+ return GetValue((NPP)npp, variable, value); |
+} |
+#endif |
+ |
+} // extern "C" |