• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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