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
17namespace ppapi {
18namespace proxy {
19
20namespace {
21
22typedef std::map<PP_Instance, HostDispatcher*> InstanceToDispatcherMap;
23InstanceToDispatcherMap* g_instance_to_dispatcher = NULL;
24
25typedef std::map<PP_Module, HostDispatcher*> ModuleToDispatcherMap;
26ModuleToDispatcherMap* g_module_to_dispatcher = NULL;
27
28PP_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.
48class BoolRestorer {
49 public:
50  BoolRestorer(bool* var) : var_(var), old_value_(*var) {
51  }
52  ~BoolRestorer() {
53    *var_ = old_value_;
54  }
55 private:
56  bool* var_;
57  bool old_value_;
58};
59
60}  // namespace
61
62HostDispatcher::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
83HostDispatcher::~HostDispatcher() {
84  g_module_to_dispatcher->erase(pp_module_);
85}
86
87bool 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
101HostDispatcher* 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
112void 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
120void 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
129bool HostDispatcher::IsPlugin() const {
130  return false;
131}
132
133bool 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
173bool 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
209void 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
216const 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
242base::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
250void HostDispatcher::RemoveSyncMessageStatusObserver(
251    SyncMessageStatusObserver* obs) {
252  sync_status_observer_list_.RemoveObserver(obs);
253}
254
255void HostDispatcher::AddFilter(IPC::Listener* listener) {
256  filters_.push_back(listener);
257}
258
259void HostDispatcher::OnInvalidMessageReceived() {
260  // TODO(brettw) bug 95345 kill the plugin when an invalid message is
261  // received.
262}
263
264void 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
279ScopedModuleReference::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
287ScopedModuleReference::~ScopedModuleReference() {
288  if (dispatcher_)
289    dispatcher_->ppb_proxy()->ReleaseModule(dispatcher_->pp_module());
290}
291
292}  // namespace proxy
293}  // namespace ppapi
294