diff options
-rw-r--r-- | chrome/chrome_common.gypi | 2 | ||||
-rw-r--r-- | remoting/host/DEPS | 1 | ||||
-rw-r--r-- | remoting/host/host_plugin-Info.plist | 28 | ||||
-rw-r--r-- | remoting/host/host_plugin.cc | 493 | ||||
-rw-r--r-- | remoting/remoting.gyp | 55 |
5 files changed, 574 insertions, 5 deletions
diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index e50fbc9..8d52f1e 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -340,7 +340,7 @@ }], ['remoting==1', { 'dependencies': [ - '../remoting/remoting.gyp:chromoting_plugin', + '../remoting/remoting.gyp:chromoting_client_plugin', ], }], ], diff --git a/remoting/host/DEPS b/remoting/host/DEPS index 387ce0e..bc54a47 100644 --- a/remoting/host/DEPS +++ b/remoting/host/DEPS @@ -5,4 +5,5 @@ include_rules = [ "+remoting/protocol", "+remoting/jingle_glue", + "+third_party/npapi", ] diff --git a/remoting/host/host_plugin-Info.plist b/remoting/host/host_plugin-Info.plist new file mode 100644 index 0000000..dd2a5fe --- /dev/null +++ b/remoting/host/host_plugin-Info.plist @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${CHROMIUM_BUNDLE_ID}.remoting.host.plugin</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>BRPL</string> + <key>CFBundleShortVersionString</key> + <string>1.0.0.0</string> + <key>CFBundleVersion</key> + <string>1.0.0.0</string> + <key>CFBundleSignature</key> + <string>${CHROMIUM_CREATOR}</string> + <key>LSMinimumSystemVersion</key> + <string>10.5.0</string> + <key>WebPluginName</key> + <string>${EXECUTABLE_NAME}</string> + <key>WebPluginDescription</key> + <string>Remoting Host Plugin</string> +</dict> +</plist> diff --git a/remoting/host/host_plugin.cc b/remoting/host/host_plugin.cc new file mode 100644 index 0000000..a7f56fb --- /dev/null +++ b/remoting/host/host_plugin.cc @@ -0,0 +1,493 @@ +// 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 <stdio.h> +#include <string.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npfunctions.h" +#include "third_party/npapi/bindings/npruntime.h" + +#if defined (OS_WIN) +#define OSCALL __declspec(dllexport) +#else +#define OSCALL __attribute__((visibility("default"))) +#endif + +/* + Supported Javascript interface: + readonly attribute string supportID; + readonly attribute int state; + state: { + disconnected: 1, + connecting: 2, + connected: 3, + affirmingConnection: 4, + error: 5, + initializing: 6 + } + attribute Function void debugInfo(string info); + attribute Function void onStateChanged(); + + void connect(string uid, string auth_token, function done); + void disconnect(); +*/ + +namespace { + +// Global netscape functions initialized in NP_Initialize. +NPNetscapeFuncs* g_npnetscape_funcs = NULL; + +// Values returned in GetValue +const char* g_plugin_name = "Remoting Host Plugin"; +const char* g_plugin_description = "Remoting Host Plugin"; + +// Convert an NPIdentifier into a std::string +std::string StringFromNPIdentifier(NPIdentifier identifier) { + if (!g_npnetscape_funcs->identifierisstring(identifier)) + return std::string(); + NPUTF8* np_string = g_npnetscape_funcs->utf8fromidentifier(identifier); + std::string string(np_string); + g_npnetscape_funcs->memfree(np_string); + return string; +} + +// NPAPI plugin implementation for remoting host script object +class HostNPScriptObject { + public: + explicit HostNPScriptObject(NPObject* parent) : parent_(parent) {} + + bool Init() { + return true; + } + + bool HasMethod(std::string method_name) { + VLOG(1) << "HasMethod" << method_name; + return (method_name == kFuncNameConnect || + method_name == kFuncNameDisconnect); + } + + bool InvokeDefault(const NPVariant* args, + uint32_t argCount, + NPVariant* result) { + VLOG(1) << "InvokeDefault"; + g_npnetscape_funcs->setexception(parent_, "exception during invocation"); + return false; + } + + bool Invoke(std::string method_name, + const NPVariant* args, + uint32_t argCount, + NPVariant* result) { + VLOG(1) << "Invoke " << method_name; + if (method_name == kFuncNameConnect) { + return Connect(args, argCount, result); + } else if (method_name == kFuncNameDisconnect) { + return Disconnect(args, argCount, result); + } else { + g_npnetscape_funcs->setexception(parent_, "exception during invocation"); + return false; + } + } + + bool HasProperty(std::string property_name) { + VLOG(1) << "HasProperty " << property_name; + return (property_name == kAttrNameSupportID || + property_name == kAttrNameState || + property_name == kAttrNameDebugInfo || + property_name == kAttrNameOnStateChanged); + } + + bool GetProperty(std::string property_name, NPVariant* result) { + VLOG(1) << "GetProperty " << property_name; + NOTIMPLEMENTED(); + return false; + } + + bool SetProperty(std::string property_name, const NPVariant* value) { + VLOG(1) << "SetProperty " << property_name; + // Read-only + if (property_name == kAttrNameSupportID || + property_name == kAttrNameState) + return false; + + NOTIMPLEMENTED(); + + return false; + } + + bool RemoveProperty(std::string property_name) { + VLOG(1) << "RemoveProperty " << property_name; + return false; + } + + bool Enumerate(std::vector<std::string>* values) { + VLOG(1) << "Enumerate"; + const char* entries[] = { + kAttrNameSupportID, + kAttrNameState, + kAttrNameDebugInfo, + kAttrNameOnStateChanged, + kFuncNameConnect, + kFuncNameDisconnect + }; + for (size_t i = 0; i < arraysize(entries); ++i) { + values->push_back(entries[i]); + } + return true; + } + + private: + // JS string + static const char* kAttrNameSupportID; + // JS int + static const char* kAttrNameState; + // JS func(string) + static const char* kAttrNameDebugInfo; + // JS func onStateChanged() + static const char* kAttrNameOnStateChanged; + // void connect(string uid, string auth_token, function done); + static const char* kFuncNameConnect; + // void disconnect(); + static const char* kFuncNameDisconnect; + + NPObject* parent_; + + bool Connect(const NPVariant* args, uint32_t argCount, NPVariant* result) { + NOTIMPLEMENTED(); + return false; + } + + bool Disconnect(const NPVariant* args, uint32_t argCount, NPVariant* result) { + NOTIMPLEMENTED(); + return false; + } +}; + +const char* HostNPScriptObject::kAttrNameSupportID = "supportID"; +const char* HostNPScriptObject::kAttrNameState = "state"; +const char* HostNPScriptObject::kAttrNameDebugInfo = "debugInfo"; +const char* HostNPScriptObject::kAttrNameOnStateChanged = "onStateChanged"; +const char* HostNPScriptObject::kFuncNameConnect = "connect"; +const char* HostNPScriptObject::kFuncNameDisconnect = "disconnect"; + + +// 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) { + return true; + } + + bool Save(NPSavedData** saved) { + return true; + } + + NPObject* GetScriptableObject() { + if (!scriptable_object_) { + 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<ScriptableNPObject*>(obj)->scriptable_object; + } + + static NPObject* Allocate(NPP npp, NPClass* aClass) { + ScriptableNPObject* object = + reinterpret_cast<ScriptableNPObject*>( + g_npnetscape_funcs->memalloc(sizeof(ScriptableNPObject))); + + object->_class = aClass; + object->referenceCount = 1; + object->scriptable_object = new HostNPScriptObject(object); + if (!object->scriptable_object->Init()) { + Deallocate(object); + object = NULL; + } + return object; + } + + static void Deallocate(NPObject* npobj) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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_; +}; + +// 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(1) << "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(1) << "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(1) << "GetValue - default " << variable; + return NPERR_GENERIC_ERROR; + case NPPVpluginNameString: + VLOG(1) << "GetValue - name string"; + *reinterpret_cast<const char**>(value) = g_plugin_name; + break; + case NPPVpluginDescriptionString: + VLOG(1) << "GetValue - description string"; + *reinterpret_cast<const char**>(value) = g_plugin_description; + break; + case NPPVpluginScriptableNPObject: + VLOG(1) << "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(1) << "HandleEvent"; + return NPERR_NO_ERROR; +} + +NPError SetWindow(NPP instance, NPWindow* pNPWindow) { + VLOG(1) << "SetWindow"; + return NPERR_NO_ERROR; +} + +} // namespace + +// The actual required NPAPI Entry points + +extern "C" { + +OSCALL NPError NP_GetEntryPoints(NPPluginFuncs* nppfuncs) { + VLOG(1) << "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; +} + +OSCALL NPError NP_Initialize(NPNetscapeFuncs* npnetscape_funcs +#if defined(OS_LINUX) + , NPPluginFuncs* nppfuncs +#endif // OS_LINUX + ) { + VLOG(1) << "NP_Initialize"; + 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_LINUX) + NP_GetEntryPoints(nppfuncs); +#endif // OS_LINUX + return NPERR_NO_ERROR; +} + +OSCALL NPError NP_Shutdown() { + VLOG(1) << "NP_Shutdown"; + return NPERR_NO_ERROR; +} + +OSCALL const char* NP_GetMIMEDescription(void) { + VLOG(1) << "NP_GetMIMEDescription"; + return ""; +} + +OSCALL NPError NP_GetValue(void* npp, NPPVariable variable, void* value) { + return GetValue((NPP)npp, variable, value); +} + +} // extern "C" diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index e4d0169c..25aed80 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -4,7 +4,23 @@ { 'variables': { + # TODO(dmaclach): can we pick this up some other way? Right now it's + # duplicated from chrome.gyp 'chromium_code': 1, + 'conditions': [ + ['OS=="mac"', { + 'conditions': [ + ['branding=="Chrome"', { + 'mac_bundle_id': 'com.google.Chrome', + 'mac_creator': 'rimZ', + }, { # else: branding!="Chrome" + 'mac_bundle_id': 'org.chromium.Chromium', + 'mac_creator': 'Cr24', + }], # branding + ], # conditions + + }], + ], }, 'target_defaults': { @@ -59,12 +75,11 @@ }, # end of target 'chromoting_x11_client' ], }], # end of OS conditions for x11 client - ], # end of 'conditions' 'targets': [ { - 'target_name': 'chromoting_plugin', + 'target_name': 'chromoting_client_plugin', 'type': 'static_library', 'defines': [ 'HAVE_STDINT_H', # Required by on2_integer.h @@ -108,8 +123,40 @@ '../media/base/yuv_row_win.cc', '../media/base/yuv_row_posix.cc', ], - }, # end of target 'chromoting_plugin' - + }, # end of target 'chromoting_client_plugin' + { + 'target_name': 'chromoting_host_plugin', + 'type': 'loadable_module', + 'defines': [ + ], + 'dependencies': [ + 'chromoting_base', + 'chromoting_host', + '../third_party/npapi/npapi.gyp:npapi', + ], + 'sources': [ + 'host/host_plugin.cc', + ], + 'conditions': [ + ['OS=="mac"', { + 'mac_bundle': 1, + 'xcode_settings': { + 'CHROMIUM_BUNDLE_ID': '<(mac_bundle_id)', + 'INFOPLIST_FILE': 'host/host_plugin-Info.plist', + 'WRAPPER_EXTENSION': 'plugin', + }, + # TODO(mark): Come up with a fancier way to do this. It should + # only be necessary to list framework-Info.plist once, not the + # three times it is listed here. + 'mac_bundle_resources': [ + 'host/host_plugin-Info.plist', + ], + 'mac_bundle_resources!': [ + 'host/host_plugin-Info.plist', + ], + }], + ], + }, # end of target 'chromoting_host_plugin' { 'target_name': 'chromoting_base', 'type': '<(library)', |