Home | History | Annotate | Download | only in proxy
      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 
     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:
     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 
     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 
     83 HostDispatcher::~HostDispatcher() {
     84   g_module_to_dispatcher->erase(pp_module_);
     85 }
     86 
     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
    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
    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
    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 
    129 bool HostDispatcher::IsPlugin() const {
    130   return false;
    131 }
    132 
    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 
    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 
    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 
    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 
    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 
    250 void HostDispatcher::RemoveSyncMessageStatusObserver(
    251     SyncMessageStatusObserver* obs) {
    252   sync_status_observer_list_.RemoveObserver(obs);
    253 }
    254 
    255 void HostDispatcher::AddFilter(IPC::Listener* listener) {
    256   filters_.push_back(listener);
    257 }
    258 
    259 void HostDispatcher::OnInvalidMessageReceived() {
    260   // TODO(brettw) bug 95345 kill the plugin when an invalid message is
    261   // received.
    262 }
    263 
    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 
    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 
    287 ScopedModuleReference::~ScopedModuleReference() {
    288   if (dispatcher_)
    289     dispatcher_->ppb_proxy()->ReleaseModule(dispatcher_->pp_module());
    290 }
    291 
    292 }  // namespace proxy
    293 }  // namespace ppapi
    294