blob: 077637276f1768d00f579542df08f0139839ba7f [file] [log] [blame]
// 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/at_exit.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/stringize_macros.h"
#include "remoting/base/plugin_message_loop_proxy.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 remoting::PluginMessageLoopProxy::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),
np_thread_id_(base::PlatformThread::CurrentId()) {
}
~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<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
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_;
}
// PluginMessageLoopProxy::Delegate implementation.
virtual bool RunOnPluginThread(
int delay_ms, void(function)(void*), void* data) OVERRIDE {
if (delay_ms == 0) {
g_npnetscape_funcs->pluginthreadasynccall(instance_, function, data);
} else {
base::AutoLock auto_lock(timers_lock_);
uint32_t timer_id = g_npnetscape_funcs->scheduletimer(
instance_, delay_ms, false, &NPDelayedTaskSpringboard);
DelayedTask task = {function, data};
timers_[timer_id] = task;
}
return true;
}
virtual bool IsPluginThread() OVERRIDE {
return np_thread_id_ == base::PlatformThread::CurrentId();
}
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);
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<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_;
base::PlatformThreadId np_thread_id_;
std::map<uint32_t, DelayedTask> timers_;
base::Lock timers_lock_;
};
// 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";
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<const char**>(value) = HOST_PLUGIN_NAME;
break;
case NPPVpluginDescriptionString:
VLOG(2) << "GetValue - description string";
*reinterpret_cast<const char**>(value) = HOST_PLUGIN_DESCRIPTION;
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";
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"