// 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.
// Note that the single accessor, Module::Get(), is not actually implemented
// in this file. This is an intentional hook that allows users of ppapi's
// C++ wrapper objects to provide different semantics for how the singleton
// object is accessed.
//
// In general, users of ppapi will also link in ppp_entrypoints.cc, which
// provides a simple default implementation of Module::Get().
//
// A notable exception where the default ppp_entrypoints will not work is
// when implementing "internal plugins" that are statically linked into the
// browser. In this case, the process may actually have multiple Modules
// loaded at once making a traditional "singleton" unworkable. To get around
// this, the users of ppapi need to get creative about how to properly
// implement the Module::Get() so that ppapi's C++ wrappers can find the
// right Module object. One example solution is to use thread local storage
// to change the Module* returned based on which thread is invoking the
// function. Leaving Module::Get() unimplemented provides a hook for
// implementing such behavior.
#include "ppapi/cpp/module.h"
#include <string.h>
#include "ppapi/c/pp_instance.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppp_input_event.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/c/ppp_messaging.h"
#include "ppapi/cpp/input_event.h"
#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/rect.h"
#include "ppapi/cpp/resource.h"
#include "ppapi/cpp/url_loader.h"
#include "ppapi/cpp/var.h"
#include "ppapi/cpp/view.h"
namespace pp {
// PPP_InputEvent implementation -----------------------------------------------
PP_Bool InputEvent_HandleEvent(PP_Instance pp_instance, PP_Resource resource) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return PP_FALSE;
Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
if (!instance)
return PP_FALSE;
return PP_FromBool(instance->HandleInputEvent(InputEvent(resource)));
}
const PPP_InputEvent input_event_interface = {
&InputEvent_HandleEvent
};
// PPP_Instance implementation -------------------------------------------------
PP_Bool Instance_DidCreate(PP_Instance pp_instance,
uint32_t argc,
const char* argn[],
const char* argv[]) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return PP_FALSE;
Instance* instance = module_singleton->CreateInstance(pp_instance);
if (!instance)
return PP_FALSE;
module_singleton->current_instances_[pp_instance] = instance;
return PP_FromBool(instance->Init(argc, argn, argv));
}
void Instance_DidDestroy(PP_Instance instance) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return;
Module::InstanceMap::iterator found =
module_singleton->current_instances_.find(instance);
if (found == module_singleton->current_instances_.end())
return;
// Remove it from the map before deleting to try to catch reentrancy.
Instance* obj = found->second;
module_singleton->current_instances_.erase(found);
delete obj;
}
void Instance_DidChangeView(PP_Instance pp_instance,
PP_Resource view_resource) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return;
Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
if (!instance)
return;
instance->DidChangeView(View(view_resource));
}
void Instance_DidChangeFocus(PP_Instance pp_instance, PP_Bool has_focus) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return;
Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
if (!instance)
return;
instance->DidChangeFocus(PP_ToBool(has_focus));
}
PP_Bool Instance_HandleDocumentLoad(PP_Instance pp_instance,
PP_Resource pp_url_loader) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return PP_FALSE;
Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
if (!instance)
return PP_FALSE;
return PP_FromBool(instance->HandleDocumentLoad(URLLoader(pp_url_loader)));
}
static PPP_Instance instance_interface = {
&Instance_DidCreate,
&Instance_DidDestroy,
&Instance_DidChangeView,
&Instance_DidChangeFocus,
&Instance_HandleDocumentLoad
};
// PPP_Messaging implementation ------------------------------------------------
void Messaging_HandleMessage(PP_Instance pp_instance, PP_Var var) {
Module* module_singleton = Module::Get();
if (!module_singleton)
return;
Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
if (!instance)
return;
instance->HandleMessage(Var(PASS_REF, var));
}
static PPP_Messaging instance_messaging_interface = {
&Messaging_HandleMessage
};
// Module ----------------------------------------------------------------------
Module::Module() : pp_module_(0), get_browser_interface_(NULL), core_(NULL) {
}
Module::~Module() {
delete core_;
core_ = NULL;
}
bool Module::Init() {
return true;
}
const void* Module::GetPluginInterface(const char* interface_name) {
if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE) == 0)
return &input_event_interface;
if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0)
return &instance_interface;
if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0)
return &instance_messaging_interface;
// Now see if anything was dynamically registered.
InterfaceMap::const_iterator found = additional_interfaces_.find(
std::string(interface_name));
if (found != additional_interfaces_.end())
return found->second;
return NULL;
}
const void* Module::GetBrowserInterface(const char* interface_name) {
return get_browser_interface_(interface_name);
}
Instance* Module::InstanceForPPInstance(PP_Instance instance) {
InstanceMap::iterator found = current_instances_.find(instance);
if (found == current_instances_.end())
return NULL;
return found->second;
}
void Module::AddPluginInterface(const std::string& interface_name,
const void* vtable) {
// Verify that we're not trying to register an interface that's already
// handled, and if it is, that we're re-registering with the same vtable.
// Calling GetPluginInterface rather than looking it up in the map allows
// us to also catch "internal" ones in addition to just previously added ones.
const void* existing_interface = GetPluginInterface(interface_name.c_str());
if (existing_interface) {
PP_DCHECK(vtable == existing_interface);
return;
}
additional_interfaces_[interface_name] = vtable;
}
bool Module::InternalInit(PP_Module mod,
PPB_GetInterface get_browser_interface) {
pp_module_ = mod;
get_browser_interface_ = get_browser_interface;
// Get the core interface which we require to run.
const PPB_Core* core = reinterpret_cast<const PPB_Core*>(GetBrowserInterface(
PPB_CORE_INTERFACE));
if (!core)
return false;
core_ = new Core(core);
return Init();
}
} // namespace pp