• 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,SyncMessageStatusReceiver * sync_status,const PpapiPermissions & permissions)62 HostDispatcher::HostDispatcher(PP_Module module,
63                                PP_GetInterface_Func local_get_interface,
64                                SyncMessageStatusReceiver* sync_status,
65                                const PpapiPermissions& permissions)
66     : Dispatcher(local_get_interface, permissions),
67       sync_status_(sync_status),
68       pp_module_(module),
69       ppb_proxy_(NULL),
70       allow_plugin_reentrancy_(false) {
71   if (!g_module_to_dispatcher)
72     g_module_to_dispatcher = new ModuleToDispatcherMap;
73   (*g_module_to_dispatcher)[pp_module_] = this;
74 
75   SetSerializationRules(new HostVarSerializationRules);
76 
77   ppb_proxy_ = reinterpret_cast<const PPB_Proxy_Private*>(
78       local_get_interface(PPB_PROXY_PRIVATE_INTERFACE));
79   DCHECK(ppb_proxy_) << "The proxy interface should always be supported.";
80 
81   ppb_proxy_->SetReserveInstanceIDCallback(pp_module_, &ReserveInstanceID);
82 }
83 
~HostDispatcher()84 HostDispatcher::~HostDispatcher() {
85   g_module_to_dispatcher->erase(pp_module_);
86 }
87 
InitHostWithChannel(Delegate * delegate,base::ProcessId peer_pid,const IPC::ChannelHandle & channel_handle,bool is_client,const ppapi::Preferences & preferences)88 bool HostDispatcher::InitHostWithChannel(
89     Delegate* delegate,
90     base::ProcessId peer_pid,
91     const IPC::ChannelHandle& channel_handle,
92     bool is_client,
93     const ppapi::Preferences& preferences) {
94   if (!Dispatcher::InitWithChannel(delegate, peer_pid, channel_handle,
95                                    is_client))
96     return false;
97   AddIOThreadMessageFilter(sync_status_.get());
98 
99   Send(new PpapiMsg_SetPreferences(preferences));
100   return true;
101 }
102 
103 // static
GetForInstance(PP_Instance instance)104 HostDispatcher* HostDispatcher::GetForInstance(PP_Instance instance) {
105   if (!g_instance_to_dispatcher)
106     return NULL;
107   InstanceToDispatcherMap::iterator found = g_instance_to_dispatcher->find(
108       instance);
109   if (found == g_instance_to_dispatcher->end())
110     return NULL;
111   return found->second;
112 }
113 
114 // static
SetForInstance(PP_Instance instance,HostDispatcher * dispatcher)115 void HostDispatcher::SetForInstance(PP_Instance instance,
116                                     HostDispatcher* dispatcher) {
117   if (!g_instance_to_dispatcher)
118     g_instance_to_dispatcher = new InstanceToDispatcherMap;
119   (*g_instance_to_dispatcher)[instance] = dispatcher;
120 }
121 
122 // static
RemoveForInstance(PP_Instance instance)123 void HostDispatcher::RemoveForInstance(PP_Instance instance) {
124   if (!g_instance_to_dispatcher)
125     return;
126   InstanceToDispatcherMap::iterator found = g_instance_to_dispatcher->find(
127       instance);
128   if (found != g_instance_to_dispatcher->end())
129     g_instance_to_dispatcher->erase(found);
130 }
131 
IsPlugin() const132 bool HostDispatcher::IsPlugin() const {
133   return false;
134 }
135 
Send(IPC::Message * msg)136 bool HostDispatcher::Send(IPC::Message* msg) {
137   TRACE_EVENT2("ppapi proxy", "HostDispatcher::Send",
138                "Class", IPC_MESSAGE_ID_CLASS(msg->type()),
139                "Line", IPC_MESSAGE_ID_LINE(msg->type()));
140 
141   // Normal sync messages are set to unblock, which would normally cause the
142   // plugin to be reentered to process them. We only want to do this when we
143   // know the plugin is in a state to accept reentrancy. Since the plugin side
144   // never clears this flag on messages it sends, we can't get deadlock, but we
145   // may still get reentrancy in the host as a result.
146   if (!allow_plugin_reentrancy_)
147     msg->set_unblock(false);
148 
149   if (msg->is_sync()) {
150     // Don't allow sending sync messages during module shutdown. Seee the "else"
151     // block below for why.
152     CHECK(!PP_ToBool(ppb_proxy()->IsInModuleDestructor(pp_module())));
153 
154     // Prevent the dispatcher from going away during sync calls. Scenarios
155     // where this could happen include a Send for a sync message which while
156     // waiting for the reply, dispatches an incoming ExecuteScript call which
157     // destroys the plugin module and in turn the dispatcher.
158     ScopedModuleReference scoped_ref(this);
159 
160     sync_status_->BeginBlockOnSyncMessage();
161     bool result = Dispatcher::Send(msg);
162     sync_status_->EndBlockOnSyncMessage();
163 
164     return result;
165   } else {
166     // We don't want to have a scoped ref for async message cases since since
167     // async messages are sent during module desruction. In this case, the
168     // module will have a 0 refcount and addrefing and releasing it will
169     // reenter the destructor and it will crash.
170     return Dispatcher::Send(msg);
171   }
172 }
173 
OnMessageReceived(const IPC::Message & msg)174 bool HostDispatcher::OnMessageReceived(const IPC::Message& msg) {
175   // Prevent the dispatcher from going away during a message handler. This must
176   // be at the outermost scope so it's released last.
177   ScopedModuleReference death_grip(this);
178 
179   TRACE_EVENT2("ppapi proxy", "HostDispatcher::OnMessageReceived",
180                "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
181                "Line", IPC_MESSAGE_ID_LINE(msg.type()));
182 
183   // We only want to allow reentrancy when the most recent message from the
184   // plugin was a scripting message. We save the old state of the flag on the
185   // stack in case we're (we are the host) being reentered ourselves. The flag
186   // is set to false here for all messages, and then the scripting API will
187   // explicitly set it to true during processing of those messages that can be
188   // reentered.
189   BoolRestorer restorer(&allow_plugin_reentrancy_);
190   allow_plugin_reentrancy_ = false;
191 
192   for (size_t i = 0; i < filters_.size(); i++) {
193     if (filters_[i]->OnMessageReceived(msg))
194       return true;
195   }
196 
197   bool handled = true;
198   IPC_BEGIN_MESSAGE_MAP(HostDispatcher, msg)
199     IPC_MESSAGE_HANDLER(PpapiHostMsg_LogWithSource, OnHostMsgLogWithSource)
200     IPC_MESSAGE_UNHANDLED(handled = false)
201   IPC_END_MESSAGE_MAP()
202 
203   if (handled)
204     return true;
205   return Dispatcher::OnMessageReceived(msg);
206 
207   // Note: |this| may be deleted once the death_grip goes out of scope!
208 }
209 
OnChannelError()210 void HostDispatcher::OnChannelError() {
211   Dispatcher::OnChannelError();  // Stop using the channel.
212 
213   // Tell the host about the crash so it can clean up and display notification.
214   ppb_proxy_->PluginCrashed(pp_module());
215 }
216 
GetProxiedInterface(const std::string & iface_name)217 const void* HostDispatcher::GetProxiedInterface(const std::string& iface_name) {
218   const void* proxied_interface =
219       InterfaceList::GetInstance()->GetInterfaceForPPP(iface_name);
220   if (!proxied_interface)
221     return NULL;  // Don't have a proxy for this interface, don't query further.
222 
223   PluginSupportedMap::iterator iter(plugin_supported_.find(iface_name));
224   if (iter == plugin_supported_.end()) {
225     // Need to query. Cache the result so we only do this once.
226     bool supported = false;
227 
228     bool previous_reentrancy_value = allow_plugin_reentrancy_;
229     allow_plugin_reentrancy_ = true;
230     Send(new PpapiMsg_SupportsInterface(iface_name, &supported));
231     allow_plugin_reentrancy_ = previous_reentrancy_value;
232 
233     std::pair<PluginSupportedMap::iterator, bool> iter_success_pair;
234     iter_success_pair = plugin_supported_.insert(
235         PluginSupportedMap::value_type(iface_name, supported));
236     iter = iter_success_pair.first;
237   }
238   if (iter->second)
239     return proxied_interface;
240   return NULL;
241 }
242 
AddFilter(IPC::Listener * listener)243 void HostDispatcher::AddFilter(IPC::Listener* listener) {
244   filters_.push_back(listener);
245 }
246 
OnInvalidMessageReceived()247 void HostDispatcher::OnInvalidMessageReceived() {
248   // TODO(brettw) bug 95345 kill the plugin when an invalid message is
249   // received.
250 }
251 
OnHostMsgLogWithSource(PP_Instance instance,int int_log_level,const std::string & source,const std::string & value)252 void HostDispatcher::OnHostMsgLogWithSource(PP_Instance instance,
253                                             int int_log_level,
254                                             const std::string& source,
255                                             const std::string& value) {
256   PP_LogLevel level = static_cast<PP_LogLevel>(int_log_level);
257   if (instance) {
258     PpapiGlobals::Get()->LogWithSource(instance, level, source, value);
259   } else {
260     PpapiGlobals::Get()->BroadcastLogWithSource(pp_module_, level,
261                                                 source, value);
262   }
263 }
264 
265 // ScopedModuleReference -------------------------------------------------------
266 
ScopedModuleReference(Dispatcher * dispatcher)267 ScopedModuleReference::ScopedModuleReference(Dispatcher* dispatcher)
268     : dispatcher_(NULL) {
269   if (!dispatcher->IsPlugin()) {
270     dispatcher_ = static_cast<HostDispatcher*>(dispatcher);
271     dispatcher_->ppb_proxy()->AddRefModule(dispatcher_->pp_module());
272   }
273 }
274 
~ScopedModuleReference()275 ScopedModuleReference::~ScopedModuleReference() {
276   if (dispatcher_)
277     dispatcher_->ppb_proxy()->ReleaseModule(dispatcher_->pp_module());
278 }
279 
280 }  // namespace proxy
281 }  // namespace ppapi
282