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 #include "ppapi/proxy/host_dispatcher.h"
6
7 #include "base/debug/trace_event.h"
8 #include "base/logging.h"
9 #include "ppapi/c/private/ppb_proxy_private.h"
10 #include "ppapi/c/ppb_var.h"
11 #include "ppapi/proxy/host_var_serialization_rules.h"
12 #include "ppapi/proxy/interface_list.h"
13 #include "ppapi/proxy/ppapi_messages.h"
14 #include "ppapi/proxy/resource_creation_proxy.h"
15 #include "ppapi/shared_impl/ppapi_globals.h"
16
17 namespace ppapi {
18 namespace proxy {
19
20 namespace {
21
22 typedef std::map<PP_Instance, HostDispatcher*> InstanceToDispatcherMap;
23 InstanceToDispatcherMap* g_instance_to_dispatcher = NULL;
24
25 typedef std::map<PP_Module, HostDispatcher*> ModuleToDispatcherMap;
26 ModuleToDispatcherMap* g_module_to_dispatcher = NULL;
27
ReserveInstanceID(PP_Module module,PP_Instance instance)28 PP_Bool ReserveInstanceID(PP_Module module, PP_Instance instance) {
29 // Default to returning true (usable) failure. Otherwise, if there's some
30 // kind of communication error or the plugin just crashed, we'll get into an
31 // infinite loop generating new instnace IDs since we think they're all in
32 // use.
33 ModuleToDispatcherMap::const_iterator found =
34 g_module_to_dispatcher->find(module);
35 if (found == g_module_to_dispatcher->end()) {
36 NOTREACHED();
37 return PP_TRUE;
38 }
39
40 bool usable = true;
41 if (!found->second->Send(new PpapiMsg_ReserveInstanceId(instance, &usable)))
42 return PP_TRUE;
43 return PP_FromBool(usable);
44 }
45
46 // Saves the state of the given bool and puts it back when it goes out of
47 // scope.
48 class BoolRestorer {
49 public:
BoolRestorer(bool * var)50 BoolRestorer(bool* var) : var_(var), old_value_(*var) {
51 }
~BoolRestorer()52 ~BoolRestorer() {
53 *var_ = old_value_;
54 }
55 private:
56 bool* var_;
57 bool old_value_;
58 };
59
60 } // namespace
61
HostDispatcher(PP_Module module,PP_GetInterface_Func local_get_interface,const PpapiPermissions & permissions)62 HostDispatcher::HostDispatcher(PP_Module module,
63 PP_GetInterface_Func local_get_interface,
64 const PpapiPermissions& permissions)
65 : Dispatcher(local_get_interface, permissions),
66 pp_module_(module),
67 ppb_proxy_(NULL),
68 allow_plugin_reentrancy_(false),
69 weak_ptr_factory_(this) {
70 if (!g_module_to_dispatcher)
71 g_module_to_dispatcher = new ModuleToDispatcherMap;
72 (*g_module_to_dispatcher)[pp_module_] = this;
73
74 SetSerializationRules(new HostVarSerializationRules);
75
76 ppb_proxy_ = reinterpret_cast<const PPB_Proxy_Private*>(
77 local_get_interface(PPB_PROXY_PRIVATE_INTERFACE));
78 DCHECK(ppb_proxy_) << "The proxy interface should always be supported.";
79
80 ppb_proxy_->SetReserveInstanceIDCallback(pp_module_, &ReserveInstanceID);
81 }
82
~HostDispatcher()83 HostDispatcher::~HostDispatcher() {
84 g_module_to_dispatcher->erase(pp_module_);
85 }
86
InitHostWithChannel(Delegate * delegate,base::ProcessId peer_pid,const IPC::ChannelHandle & channel_handle,bool is_client,const ppapi::Preferences & preferences)87 bool HostDispatcher::InitHostWithChannel(
88 Delegate* delegate,
89 base::ProcessId peer_pid,
90 const IPC::ChannelHandle& channel_handle,
91 bool is_client,
92 const ppapi::Preferences& preferences) {
93 if (!Dispatcher::InitWithChannel(delegate, peer_pid, channel_handle,
94 is_client))
95 return false;
96 Send(new PpapiMsg_SetPreferences(preferences));
97 return true;
98 }
99
100 // static
GetForInstance(PP_Instance instance)101 HostDispatcher* HostDispatcher::GetForInstance(PP_Instance instance) {
102 if (!g_instance_to_dispatcher)
103 return NULL;
104 InstanceToDispatcherMap::iterator found = g_instance_to_dispatcher->find(
105 instance);
106 if (found == g_instance_to_dispatcher->end())
107 return NULL;
108 return found->second;
109 }
110
111 // static
SetForInstance(PP_Instance instance,HostDispatcher * dispatcher)112 void HostDispatcher::SetForInstance(PP_Instance instance,
113 HostDispatcher* dispatcher) {
114 if (!g_instance_to_dispatcher)
115 g_instance_to_dispatcher = new InstanceToDispatcherMap;
116 (*g_instance_to_dispatcher)[instance] = dispatcher;
117 }
118
119 // static
RemoveForInstance(PP_Instance instance)120 void HostDispatcher::RemoveForInstance(PP_Instance instance) {
121 if (!g_instance_to_dispatcher)
122 return;
123 InstanceToDispatcherMap::iterator found = g_instance_to_dispatcher->find(
124 instance);
125 if (found != g_instance_to_dispatcher->end())
126 g_instance_to_dispatcher->erase(found);
127 }
128
IsPlugin() const129 bool HostDispatcher::IsPlugin() const {
130 return false;
131 }
132
Send(IPC::Message * msg)133 bool HostDispatcher::Send(IPC::Message* msg) {
134 TRACE_EVENT2("ppapi proxy", "HostDispatcher::Send",
135 "Class", IPC_MESSAGE_ID_CLASS(msg->type()),
136 "Line", IPC_MESSAGE_ID_LINE(msg->type()));
137
138 // Normal sync messages are set to unblock, which would normally cause the
139 // plugin to be reentered to process them. We only want to do this when we
140 // know the plugin is in a state to accept reentrancy. Since the plugin side
141 // never clears this flag on messages it sends, we can't get deadlock, but we
142 // may still get reentrancy in the host as a result.
143 if (!allow_plugin_reentrancy_)
144 msg->set_unblock(false);
145
146 if (msg->is_sync()) {
147 // Don't allow sending sync messages during module shutdown. Seee the "else"
148 // block below for why.
149 CHECK(!PP_ToBool(ppb_proxy()->IsInModuleDestructor(pp_module())));
150
151 // Prevent the dispatcher from going away during sync calls. Scenarios
152 // where this could happen include a Send for a sync message which while
153 // waiting for the reply, dispatches an incoming ExecuteScript call which
154 // destroys the plugin module and in turn the dispatcher.
155 ScopedModuleReference scoped_ref(this);
156
157 FOR_EACH_OBSERVER(SyncMessageStatusObserver, sync_status_observer_list_,
158 BeginBlockOnSyncMessage());
159 bool result = Dispatcher::Send(msg);
160 FOR_EACH_OBSERVER(SyncMessageStatusObserver, sync_status_observer_list_,
161 EndBlockOnSyncMessage());
162
163 return result;
164 } else {
165 // We don't want to have a scoped ref for async message cases since since
166 // async messages are sent during module desruction. In this case, the
167 // module will have a 0 refcount and addrefing and releasing it will
168 // reenter the destructor and it will crash.
169 return Dispatcher::Send(msg);
170 }
171 }
172
OnMessageReceived(const IPC::Message & msg)173 bool HostDispatcher::OnMessageReceived(const IPC::Message& msg) {
174 // Prevent the dispatcher from going away during a message handler. This must
175 // be at the outermost scope so it's released last.
176 ScopedModuleReference death_grip(this);
177
178 TRACE_EVENT2("ppapi proxy", "HostDispatcher::OnMessageReceived",
179 "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
180 "Line", IPC_MESSAGE_ID_LINE(msg.type()));
181
182 // We only want to allow reentrancy when the most recent message from the
183 // plugin was a scripting message. We save the old state of the flag on the
184 // stack in case we're (we are the host) being reentered ourselves. The flag
185 // is set to false here for all messages, and then the scripting API will
186 // explicitly set it to true during processing of those messages that can be
187 // reentered.
188 BoolRestorer restorer(&allow_plugin_reentrancy_);
189 allow_plugin_reentrancy_ = false;
190
191 for (size_t i = 0; i < filters_.size(); i++) {
192 if (filters_[i]->OnMessageReceived(msg))
193 return true;
194 }
195
196 bool handled = true;
197 IPC_BEGIN_MESSAGE_MAP(HostDispatcher, msg)
198 IPC_MESSAGE_HANDLER(PpapiHostMsg_LogWithSource, OnHostMsgLogWithSource)
199 IPC_MESSAGE_UNHANDLED(handled = false)
200 IPC_END_MESSAGE_MAP()
201
202 if (handled)
203 return true;
204 return Dispatcher::OnMessageReceived(msg);
205
206 // Note: |this| may be deleted once the death_grip goes out of scope!
207 }
208
OnChannelError()209 void HostDispatcher::OnChannelError() {
210 Dispatcher::OnChannelError(); // Stop using the channel.
211
212 // Tell the host about the crash so it can clean up and display notification.
213 ppb_proxy_->PluginCrashed(pp_module());
214 }
215
GetProxiedInterface(const std::string & iface_name)216 const void* HostDispatcher::GetProxiedInterface(const std::string& iface_name) {
217 const void* proxied_interface =
218 InterfaceList::GetInstance()->GetInterfaceForPPP(iface_name);
219 if (!proxied_interface)
220 return NULL; // Don't have a proxy for this interface, don't query further.
221
222 PluginSupportedMap::iterator iter(plugin_supported_.find(iface_name));
223 if (iter == plugin_supported_.end()) {
224 // Need to query. Cache the result so we only do this once.
225 bool supported = false;
226
227 bool previous_reentrancy_value = allow_plugin_reentrancy_;
228 allow_plugin_reentrancy_ = true;
229 Send(new PpapiMsg_SupportsInterface(iface_name, &supported));
230 allow_plugin_reentrancy_ = previous_reentrancy_value;
231
232 std::pair<PluginSupportedMap::iterator, bool> iter_success_pair;
233 iter_success_pair = plugin_supported_.insert(
234 PluginSupportedMap::value_type(iface_name, supported));
235 iter = iter_success_pair.first;
236 }
237 if (iter->second)
238 return proxied_interface;
239 return NULL;
240 }
241
AddSyncMessageStatusObserver(SyncMessageStatusObserver * obs)242 base::Closure HostDispatcher::AddSyncMessageStatusObserver(
243 SyncMessageStatusObserver* obs) {
244 sync_status_observer_list_.AddObserver(obs);
245 return base::Bind(&HostDispatcher::RemoveSyncMessageStatusObserver,
246 weak_ptr_factory_.GetWeakPtr(),
247 obs);
248 }
249
RemoveSyncMessageStatusObserver(SyncMessageStatusObserver * obs)250 void HostDispatcher::RemoveSyncMessageStatusObserver(
251 SyncMessageStatusObserver* obs) {
252 sync_status_observer_list_.RemoveObserver(obs);
253 }
254
AddFilter(IPC::Listener * listener)255 void HostDispatcher::AddFilter(IPC::Listener* listener) {
256 filters_.push_back(listener);
257 }
258
OnInvalidMessageReceived()259 void HostDispatcher::OnInvalidMessageReceived() {
260 // TODO(brettw) bug 95345 kill the plugin when an invalid message is
261 // received.
262 }
263
OnHostMsgLogWithSource(PP_Instance instance,int int_log_level,const std::string & source,const std::string & value)264 void HostDispatcher::OnHostMsgLogWithSource(PP_Instance instance,
265 int int_log_level,
266 const std::string& source,
267 const std::string& value) {
268 PP_LogLevel level = static_cast<PP_LogLevel>(int_log_level);
269 if (instance) {
270 PpapiGlobals::Get()->LogWithSource(instance, level, source, value);
271 } else {
272 PpapiGlobals::Get()->BroadcastLogWithSource(pp_module_, level,
273 source, value);
274 }
275 }
276
277 // ScopedModuleReference -------------------------------------------------------
278
ScopedModuleReference(Dispatcher * dispatcher)279 ScopedModuleReference::ScopedModuleReference(Dispatcher* dispatcher)
280 : dispatcher_(NULL) {
281 if (!dispatcher->IsPlugin()) {
282 dispatcher_ = static_cast<HostDispatcher*>(dispatcher);
283 dispatcher_->ppb_proxy()->AddRefModule(dispatcher_->pp_module());
284 }
285 }
286
~ScopedModuleReference()287 ScopedModuleReference::~ScopedModuleReference() {
288 if (dispatcher_)
289 dispatcher_->ppb_proxy()->ReleaseModule(dispatcher_->pp_module());
290 }
291
292 } // namespace proxy
293 } // namespace ppapi
294