// Copyright (c) 2011 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 #include #include #include #include "base/at_exit.h" #include "base/basictypes.h" #include "base/logging.h" #include "base/stringize_macros.h" #include "remoting/host/plugin/host_plugin_utils.h" #include "remoting/host/plugin/host_script_object.h" #include "third_party/npapi/bindings/npapi.h" #include "third_party/npapi/bindings/npfunctions.h" #include "third_party/npapi/bindings/npruntime.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::HostNPScriptObject; using remoting::StringFromNPIdentifier; namespace { base::AtExitManager* g_at_exit_manager = NULL; // The name and description are returned by GetValue, but are also // combined with the MIME type to satisfy GetMIMEDescription, so we // use macros here to allow that to happen at compile-time. #define HOST_PLUGIN_NAME "Remoting Host Plugin" #define HOST_PLUGIN_DESCRIPTION "Remoting Host Plugin" // 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: // |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) {} ~HostNPPlugin() { if (scriptable_object_) { g_npnetscape_funcs->releaseobject(scriptable_object_); scriptable_object_ = NULL; } } 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(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(NPEventModelCocoa)); } else { LOG(ERROR) << "No Cocoa Event Model support"; return false; } #endif // OS_MACOSX 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_; } private: struct ScriptableNPObject : public NPObject { HostNPScriptObject* scriptable_object; }; static HostNPScriptObject* ScriptableFromObject(NPObject* obj) { return reinterpret_cast(obj)->scriptable_object; } static NPObject* Allocate(NPP npp, NPClass* aClass) { VLOG(2) << "static Allocate"; ScriptableNPObject* object = reinterpret_cast( g_npnetscape_funcs->memalloc(sizeof(ScriptableNPObject))); object->_class = aClass; object->referenceCount = 1; object->scriptable_object = new HostNPScriptObject(npp, object); if (!object->scriptable_object->Init()) { Deallocate(object); object = NULL; } 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(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 values; bool is_good = scriptable->Enumerate(&values); if (is_good) { *count = values.size(); *value = reinterpret_cast( 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_; }; // Utility functions to map NPAPI Entry Points to C++ Objects. HostNPPlugin* PluginFromInstance(NPP instance) { return reinterpret_cast(instance->pdata); } NPError CreatePlugin(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char** argn, char** argv, NPSavedData* saved) { VLOG(2) << "CreatePlugin"; 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"; 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) { switch(variable) { default: VLOG(2) << "GetValue - default " << variable; return NPERR_GENERIC_ERROR; case NPPVpluginNameString: VLOG(2) << "GetValue - name string"; *reinterpret_cast(value) = HOST_PLUGIN_NAME; break; case NPPVpluginDescriptionString: VLOG(2) << "GetValue - description string"; *reinterpret_cast(value) = HOST_PLUGIN_DESCRIPTION; break; case NPPVpluginNeedsXEmbed: VLOG(2) << "GetValue - NeedsXEmbed"; *(static_cast(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(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"; return NPERR_NO_ERROR; } } // namespace #if defined(OS_WIN) HMODULE g_hModule = NULL; BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: g_hModule = hModule; 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"; if (g_at_exit_manager) return NPERR_MODULE_LOAD_FAILED_ERROR; if(npnetscape_funcs == NULL) return NPERR_INVALID_FUNCTABLE_ERROR; if(((npnetscape_funcs->version & 0xff00) >> 8) > NP_VERSION_MAJOR) return NPERR_INCOMPATIBLE_VERSION_ERROR; g_at_exit_manager = new base::AtExitManager; g_npnetscape_funcs = npnetscape_funcs; #if defined(OS_POSIX) && !defined(OS_MACOSX) NP_GetEntryPoints(nppfuncs); #endif return NPERR_NO_ERROR; } EXPORT NPError API_CALL NP_Shutdown() { VLOG(2) << "NP_Shutdown"; delete g_at_exit_manager; g_at_exit_manager = NULL; 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) ":" HOST_PLUGIN_NAME ":" HOST_PLUGIN_DESCRIPTION; } EXPORT NPError API_CALL NP_GetValue(void* npp, NPPVariable variable, void* value) { return GetValue((NPP)npp, variable, value); } #endif } // extern "C"