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.
5 #include "ppapi/proxy/host_dispatcher.h"
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"
22 typedef std::map
<PP_Instance
, HostDispatcher
*> InstanceToDispatcherMap
;
23 InstanceToDispatcherMap
* g_instance_to_dispatcher
= NULL
;
25 typedef std::map
<PP_Module
, HostDispatcher
*> ModuleToDispatcherMap
;
26 ModuleToDispatcherMap
* g_module_to_dispatcher
= NULL
;
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
33 ModuleToDispatcherMap::const_iterator found
=
34 g_module_to_dispatcher
->find(module
);
35 if (found
== g_module_to_dispatcher
->end()) {
41 if (!found
->second
->Send(new PpapiMsg_ReserveInstanceId(instance
, &usable
)))
43 return PP_FromBool(usable
);
46 // Saves the state of the given bool and puts it back when it goes out of
50 BoolRestorer(bool* var
) : var_(var
), old_value_(*var
) {
62 HostDispatcher::HostDispatcher(PP_Module module
,
63 PP_GetInterface_Func local_get_interface
,
64 const PpapiPermissions
& permissions
)
65 : Dispatcher(local_get_interface
, permissions
),
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;
74 SetSerializationRules(new HostVarSerializationRules
);
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.";
80 ppb_proxy_
->SetReserveInstanceIDCallback(pp_module_
, &ReserveInstanceID
);
83 HostDispatcher::~HostDispatcher() {
84 g_module_to_dispatcher
->erase(pp_module_
);
87 bool HostDispatcher::InitHostWithChannel(
89 base::ProcessId peer_pid
,
90 const IPC::ChannelHandle
& channel_handle
,
92 const ppapi::Preferences
& preferences
) {
93 if (!Dispatcher::InitWithChannel(delegate
, peer_pid
, channel_handle
,
96 Send(new PpapiMsg_SetPreferences(preferences
));
101 HostDispatcher
* HostDispatcher::GetForInstance(PP_Instance instance
) {
102 if (!g_instance_to_dispatcher
)
104 InstanceToDispatcherMap::iterator found
= g_instance_to_dispatcher
->find(
106 if (found
== g_instance_to_dispatcher
->end())
108 return found
->second
;
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
;
120 void HostDispatcher::RemoveForInstance(PP_Instance instance
) {
121 if (!g_instance_to_dispatcher
)
123 InstanceToDispatcherMap::iterator found
= g_instance_to_dispatcher
->find(
125 if (found
!= g_instance_to_dispatcher
->end())
126 g_instance_to_dispatcher
->erase(found
);
129 bool HostDispatcher::IsPlugin() const {
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()));
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);
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())));
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);
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());
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
);
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);
178 TRACE_EVENT2("ppapi proxy", "HostDispatcher::OnMessageReceived",
179 "Class", IPC_MESSAGE_ID_CLASS(msg
.type()),
180 "Line", IPC_MESSAGE_ID_LINE(msg
.type()));
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
188 BoolRestorer
restorer(&allow_plugin_reentrancy_
);
189 allow_plugin_reentrancy_
= false;
191 for (size_t i
= 0; i
< filters_
.size(); i
++) {
192 if (filters_
[i
]->OnMessageReceived(msg
))
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()
204 return Dispatcher::OnMessageReceived(msg
);
206 // Note: |this| may be deleted once the death_grip goes out of scope!
209 void HostDispatcher::OnChannelError() {
210 Dispatcher::OnChannelError(); // Stop using the channel.
212 // Tell the host about the crash so it can clean up and display notification.
213 ppb_proxy_
->PluginCrashed(pp_module());
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.
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;
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
;
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
;
238 return proxied_interface
;
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(),
250 void HostDispatcher::RemoveSyncMessageStatusObserver(
251 SyncMessageStatusObserver
* obs
) {
252 sync_status_observer_list_
.RemoveObserver(obs
);
255 void HostDispatcher::AddFilter(IPC::Listener
* listener
) {
256 filters_
.push_back(listener
);
259 void HostDispatcher::OnInvalidMessageReceived() {
260 // TODO(brettw) bug 95345 kill the plugin when an invalid message is
264 void HostDispatcher::OnHostMsgLogWithSource(PP_Instance instance
,
266 const std::string
& source
,
267 const std::string
& value
) {
268 PP_LogLevel level
= static_cast<PP_LogLevel
>(int_log_level
);
270 PpapiGlobals::Get()->LogWithSource(instance
, level
, source
, value
);
272 PpapiGlobals::Get()->BroadcastLogWithSource(pp_module_
, level
,
277 // ScopedModuleReference -------------------------------------------------------
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());
287 ScopedModuleReference::~ScopedModuleReference() {
289 dispatcher_
->ppb_proxy()->ReleaseModule(dispatcher_
->pp_module());