1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Note that the single accessor, Module::Get(), is not actually implemented
6// in this file.  This is an intentional hook that allows users of ppapi's
7// C++ wrapper objects to provide different semantics for how the singleton
8// object is accessed.
9//
10// In general, users of ppapi will also link in ppp_entrypoints.cc, which
11// provides a simple default implementation of Module::Get().
12//
13// A notable exception where the default ppp_entrypoints will not work is
14// when implementing "internal plugins" that are statically linked into the
15// browser. In this case, the process may actually have multiple Modules
16// loaded at once making a traditional "singleton" unworkable.  To get around
17// this, the users of ppapi need to get creative about how to properly
18// implement the Module::Get() so that ppapi's C++ wrappers can find the
19// right Module object.  One example solution is to use thread local storage
20// to change the Module* returned based on which thread is invoking the
21// function. Leaving Module::Get() unimplemented provides a hook for
22// implementing such behavior.
23
24#include "ppapi/cpp/module.h"
25
26#include <string.h>
27
28#include "ppapi/c/pp_instance.h"
29#include "ppapi/c/pp_var.h"
30#include "ppapi/c/ppp_input_event.h"
31#include "ppapi/c/ppp_instance.h"
32#include "ppapi/c/ppp_messaging.h"
33#include "ppapi/cpp/input_event.h"
34#include "ppapi/cpp/instance.h"
35#include "ppapi/cpp/rect.h"
36#include "ppapi/cpp/resource.h"
37#include "ppapi/cpp/url_loader.h"
38#include "ppapi/cpp/var.h"
39#include "ppapi/cpp/view.h"
40
41namespace pp {
42
43// PPP_InputEvent implementation -----------------------------------------------
44
45PP_Bool InputEvent_HandleEvent(PP_Instance pp_instance, PP_Resource resource) {
46  Module* module_singleton = Module::Get();
47  if (!module_singleton)
48    return PP_FALSE;
49  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
50  if (!instance)
51    return PP_FALSE;
52
53  return PP_FromBool(instance->HandleInputEvent(InputEvent(resource)));
54}
55
56const PPP_InputEvent input_event_interface = {
57  &InputEvent_HandleEvent
58};
59
60// PPP_Instance implementation -------------------------------------------------
61
62PP_Bool Instance_DidCreate(PP_Instance pp_instance,
63                           uint32_t argc,
64                           const char* argn[],
65                           const char* argv[]) {
66  Module* module_singleton = Module::Get();
67  if (!module_singleton)
68    return PP_FALSE;
69
70  Instance* instance = module_singleton->CreateInstance(pp_instance);
71  if (!instance)
72    return PP_FALSE;
73  module_singleton->current_instances_[pp_instance] = instance;
74  return PP_FromBool(instance->Init(argc, argn, argv));
75}
76
77void Instance_DidDestroy(PP_Instance instance) {
78  Module* module_singleton = Module::Get();
79  if (!module_singleton)
80    return;
81  Module::InstanceMap::iterator found =
82      module_singleton->current_instances_.find(instance);
83  if (found == module_singleton->current_instances_.end())
84    return;
85
86  // Remove it from the map before deleting to try to catch reentrancy.
87  Instance* obj = found->second;
88  module_singleton->current_instances_.erase(found);
89  delete obj;
90}
91
92void Instance_DidChangeView(PP_Instance pp_instance,
93                            PP_Resource view_resource) {
94  Module* module_singleton = Module::Get();
95  if (!module_singleton)
96    return;
97  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
98  if (!instance)
99    return;
100  instance->DidChangeView(View(view_resource));
101}
102
103void Instance_DidChangeFocus(PP_Instance pp_instance, PP_Bool has_focus) {
104  Module* module_singleton = Module::Get();
105  if (!module_singleton)
106    return;
107  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
108  if (!instance)
109    return;
110  instance->DidChangeFocus(PP_ToBool(has_focus));
111}
112
113PP_Bool Instance_HandleDocumentLoad(PP_Instance pp_instance,
114                                    PP_Resource pp_url_loader) {
115  Module* module_singleton = Module::Get();
116  if (!module_singleton)
117    return PP_FALSE;
118  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
119  if (!instance)
120    return PP_FALSE;
121  return PP_FromBool(instance->HandleDocumentLoad(URLLoader(pp_url_loader)));
122}
123
124static PPP_Instance instance_interface = {
125  &Instance_DidCreate,
126  &Instance_DidDestroy,
127  &Instance_DidChangeView,
128  &Instance_DidChangeFocus,
129  &Instance_HandleDocumentLoad
130};
131
132// PPP_Messaging implementation ------------------------------------------------
133
134void Messaging_HandleMessage(PP_Instance pp_instance, PP_Var var) {
135  Module* module_singleton = Module::Get();
136  if (!module_singleton)
137    return;
138  Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
139  if (!instance)
140    return;
141  instance->HandleMessage(Var(PASS_REF, var));
142}
143
144static PPP_Messaging instance_messaging_interface = {
145  &Messaging_HandleMessage
146};
147
148// Module ----------------------------------------------------------------------
149
150Module::Module() : pp_module_(0), get_browser_interface_(NULL), core_(NULL) {
151}
152
153Module::~Module() {
154  delete core_;
155  core_ = NULL;
156}
157
158bool Module::Init() {
159  return true;
160}
161
162const void* Module::GetPluginInterface(const char* interface_name) {
163  if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE) == 0)
164    return &input_event_interface;
165  if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0)
166    return &instance_interface;
167  if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0)
168    return &instance_messaging_interface;
169
170  // Now see if anything was dynamically registered.
171  InterfaceMap::const_iterator found = additional_interfaces_.find(
172      std::string(interface_name));
173  if (found != additional_interfaces_.end())
174    return found->second;
175
176  return NULL;
177}
178
179const void* Module::GetBrowserInterface(const char* interface_name) {
180  return get_browser_interface_(interface_name);
181}
182
183Instance* Module::InstanceForPPInstance(PP_Instance instance) {
184  InstanceMap::iterator found = current_instances_.find(instance);
185  if (found == current_instances_.end())
186    return NULL;
187  return found->second;
188}
189
190void Module::AddPluginInterface(const std::string& interface_name,
191                                const void* vtable) {
192  // Verify that we're not trying to register an interface that's already
193  // handled, and if it is, that we're re-registering with the same vtable.
194  // Calling GetPluginInterface rather than looking it up in the map allows
195  // us to also catch "internal" ones in addition to just previously added ones.
196  const void* existing_interface = GetPluginInterface(interface_name.c_str());
197  if (existing_interface) {
198    PP_DCHECK(vtable == existing_interface);
199    return;
200  }
201  additional_interfaces_[interface_name] = vtable;
202}
203
204bool Module::InternalInit(PP_Module mod,
205                          PPB_GetInterface get_browser_interface) {
206  pp_module_ = mod;
207  get_browser_interface_ = get_browser_interface;
208
209  // Get the core interface which we require to run.
210  const PPB_Core* core = reinterpret_cast<const PPB_Core*>(GetBrowserInterface(
211      PPB_CORE_INTERFACE));
212  if (!core)
213    return false;
214  core_ = new Core(core);
215
216  return Init();
217}
218
219}  // namespace pp
220