Home | History | Annotate | Download | only in host
      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/host/ppapi_host.h"
      6 
      7 #include "base/logging.h"
      8 #include "ppapi/c/pp_errors.h"
      9 #include "ppapi/host/host_factory.h"
     10 #include "ppapi/host/host_message_context.h"
     11 #include "ppapi/host/instance_message_filter.h"
     12 #include "ppapi/host/resource_host.h"
     13 #include "ppapi/proxy/ppapi_messages.h"
     14 #include "ppapi/proxy/resource_message_params.h"
     15 #include "ppapi/proxy/serialized_handle.h"
     16 #include "ppapi/shared_impl/host_resource.h"
     17 
     18 namespace ppapi {
     19 namespace host {
     20 
     21 using proxy::SerializedHandle;
     22 
     23 namespace {
     24 
     25 // Put a cap on the maximum number of resources so we don't explode if the
     26 // renderer starts spamming us.
     27 const size_t kMaxResourcesPerPlugin = 1 << 14;
     28 
     29 }  // namespace
     30 
     31 PpapiHost::PpapiHost(IPC::Sender* sender,
     32                      const PpapiPermissions& perms)
     33     : sender_(sender),
     34       permissions_(perms),
     35       next_pending_resource_host_id_(1) {
     36 }
     37 
     38 PpapiHost::~PpapiHost() {
     39   // Delete these explicitly before destruction since then the host is still
     40   // technically alive in case one of the filters accesses us from the
     41   // destructor.
     42   instance_message_filters_.clear();
     43 
     44   // The resources may also want to use us in their destructors.
     45   resources_.clear();
     46   pending_resource_hosts_.clear();
     47 }
     48 
     49 bool PpapiHost::Send(IPC::Message* msg) {
     50   return sender_->Send(msg);
     51 }
     52 
     53 bool PpapiHost::OnMessageReceived(const IPC::Message& msg) {
     54   bool handled = true;
     55   IPC_BEGIN_MESSAGE_MAP(PpapiHost, msg)
     56     IPC_MESSAGE_HANDLER(PpapiHostMsg_ResourceCall,
     57                         OnHostMsgResourceCall)
     58     IPC_MESSAGE_HANDLER(PpapiHostMsg_InProcessResourceCall,
     59                         OnHostMsgInProcessResourceCall)
     60     IPC_MESSAGE_HANDLER_DELAY_REPLY(PpapiHostMsg_ResourceSyncCall,
     61                                     OnHostMsgResourceSyncCall)
     62     IPC_MESSAGE_HANDLER(PpapiHostMsg_ResourceCreated,
     63                         OnHostMsgResourceCreated)
     64     IPC_MESSAGE_HANDLER(PpapiHostMsg_AttachToPendingHost,
     65                         OnHostMsgAttachToPendingHost)
     66     IPC_MESSAGE_HANDLER(PpapiHostMsg_ResourceDestroyed,
     67                         OnHostMsgResourceDestroyed)
     68     IPC_MESSAGE_UNHANDLED(handled = false)
     69   IPC_END_MESSAGE_MAP()
     70 
     71   if (!handled) {
     72     for (size_t i = 0; i < instance_message_filters_.size(); i++) {
     73       if (instance_message_filters_[i]->OnInstanceMessageReceived(msg)) {
     74         handled = true;
     75         break;
     76       }
     77     }
     78   }
     79 
     80   return handled;
     81 }
     82 
     83 void PpapiHost::SendReply(const ReplyMessageContext& context,
     84                           const IPC::Message& msg) {
     85   TRACE_EVENT2("ppapi proxy", "PpapiHost::SendReply",
     86                "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
     87                "Line", IPC_MESSAGE_ID_LINE(msg.type()));
     88   if (context.sync_reply_msg) {
     89     PpapiHostMsg_ResourceSyncCall::WriteReplyParams(context.sync_reply_msg,
     90                                                     context.params, msg);
     91     Send(context.sync_reply_msg);
     92   } else {
     93     if (context.routing_id != MSG_ROUTING_NONE) {
     94       Send(new PpapiHostMsg_InProcessResourceReply(context.routing_id,
     95                                                    context.params,
     96                                                    msg));
     97     } else {
     98       Send(new PpapiPluginMsg_ResourceReply(context.params, msg));
     99     }
    100   }
    101 }
    102 
    103 void PpapiHost::SendUnsolicitedReply(PP_Resource resource,
    104                                      const IPC::Message& msg) {
    105   SendUnsolicitedReplyWithHandles(
    106       resource, msg, std::vector<SerializedHandle>());
    107 }
    108 
    109 void PpapiHost::SendUnsolicitedReplyWithHandles(
    110     PP_Resource resource,
    111     const IPC::Message& msg,
    112     const std::vector<SerializedHandle>& handles) {
    113   TRACE_EVENT2("ppapi proxy", "PpapiHost::SendUnsolicitedReplyWithHandles",
    114                "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
    115                "Line", IPC_MESSAGE_ID_LINE(msg.type()));
    116   DCHECK(resource);  // If this fails, host is probably pending.
    117   proxy::ResourceMessageReplyParams params(resource, 0);
    118   for (std::vector<SerializedHandle>::const_iterator it = handles.begin();
    119       it != handles.end(); ++it) {
    120     params.AppendHandle(*it);
    121   }
    122   Send(new PpapiPluginMsg_ResourceReply(params, msg));
    123 }
    124 
    125 scoped_ptr<ResourceHost> PpapiHost::CreateResourceHost(
    126     const proxy::ResourceMessageCallParams& params,
    127     PP_Instance instance,
    128     const IPC::Message& nested_msg) {
    129   scoped_ptr<ResourceHost> resource_host;
    130   DCHECK(!host_factory_filters_.empty());  // Caller forgot to add a factory.
    131   for (size_t i = 0; i < host_factory_filters_.size(); i++) {
    132     resource_host = host_factory_filters_[i]->CreateResourceHost(
    133         this, params, instance, nested_msg).Pass();
    134     if (resource_host.get())
    135       break;
    136   }
    137   return resource_host.Pass();
    138 }
    139 
    140 int PpapiHost::AddPendingResourceHost(scoped_ptr<ResourceHost> resource_host) {
    141   // The resource ID should not be assigned.
    142   if (!resource_host.get() || resource_host->pp_resource() != 0) {
    143     NOTREACHED();
    144     return 0;
    145   }
    146 
    147   if (pending_resource_hosts_.size() + resources_.size()
    148       >= kMaxResourcesPerPlugin) {
    149     return 0;
    150   }
    151 
    152   int pending_id = next_pending_resource_host_id_++;
    153   pending_resource_hosts_[pending_id] =
    154       linked_ptr<ResourceHost>(resource_host.release());
    155   return pending_id;
    156 }
    157 
    158 void PpapiHost::AddHostFactoryFilter(scoped_ptr<HostFactory> filter) {
    159   host_factory_filters_.push_back(filter.release());
    160 }
    161 
    162 void PpapiHost::AddInstanceMessageFilter(
    163     scoped_ptr<InstanceMessageFilter> filter) {
    164   instance_message_filters_.push_back(filter.release());
    165 }
    166 
    167 void PpapiHost::OnHostMsgResourceCall(
    168     const proxy::ResourceMessageCallParams& params,
    169     const IPC::Message& nested_msg) {
    170   TRACE_EVENT2("ppapi proxy", "PpapiHost::OnHostMsgResourceCall",
    171                "Class", IPC_MESSAGE_ID_CLASS(nested_msg.type()),
    172                "Line", IPC_MESSAGE_ID_LINE(nested_msg.type()));
    173   HostMessageContext context(params);
    174   HandleResourceCall(params, nested_msg, &context);
    175 }
    176 
    177 void PpapiHost::OnHostMsgInProcessResourceCall(
    178     int routing_id,
    179     const proxy::ResourceMessageCallParams& params,
    180     const IPC::Message& nested_msg) {
    181   TRACE_EVENT2("ppapi proxy", "PpapiHost::OnHostMsgInProcessResourceCall",
    182                "Class", IPC_MESSAGE_ID_CLASS(nested_msg.type()),
    183                "Line", IPC_MESSAGE_ID_LINE(nested_msg.type()));
    184   HostMessageContext context(routing_id, params);
    185   HandleResourceCall(params, nested_msg, &context);
    186 }
    187 
    188 void PpapiHost::OnHostMsgResourceSyncCall(
    189     const proxy::ResourceMessageCallParams& params,
    190     const IPC::Message& nested_msg,
    191     IPC::Message* reply_msg) {
    192   TRACE_EVENT2("ppapi proxy", "PpapiHost::OnHostMsgResourceSyncCall",
    193                "Class", IPC_MESSAGE_ID_CLASS(nested_msg.type()),
    194                "Line", IPC_MESSAGE_ID_LINE(nested_msg.type()));
    195   // Sync messages should always have callback set because they always expect
    196   // a reply from the host.
    197   DCHECK(params.has_callback());
    198   // Stash the |reply_msg| in the context so that it can be used to reply
    199   // to the sync message.
    200   HostMessageContext context(params, reply_msg);
    201   HandleResourceCall(params, nested_msg, &context);
    202 }
    203 
    204 void PpapiHost::HandleResourceCall(
    205     const proxy::ResourceMessageCallParams& params,
    206     const IPC::Message& nested_msg,
    207     HostMessageContext* context) {
    208   ResourceHost* resource_host = GetResourceHost(params.pp_resource());
    209   if (resource_host) {
    210     // CAUTION: Handling the message may cause the destruction of this object.
    211     resource_host->HandleMessage(nested_msg, context);
    212   } else {
    213     if (context->params.has_callback()) {
    214       ReplyMessageContext reply_context = context->MakeReplyMessageContext();
    215       reply_context.params.set_result(PP_ERROR_BADRESOURCE);
    216       SendReply(reply_context, context->reply_msg);
    217     }
    218   }
    219 }
    220 
    221 void PpapiHost::OnHostMsgResourceCreated(
    222     const proxy::ResourceMessageCallParams& params,
    223     PP_Instance instance,
    224     const IPC::Message& nested_msg) {
    225   TRACE_EVENT2("ppapi proxy", "PpapiHost::OnHostMsgResourceCreated",
    226                "Class", IPC_MESSAGE_ID_CLASS(nested_msg.type()),
    227                "Line", IPC_MESSAGE_ID_LINE(nested_msg.type()));
    228 
    229   if (pending_resource_hosts_.size() + resources_.size()
    230       >= kMaxResourcesPerPlugin) {
    231     return;
    232   }
    233 
    234   // Run through all filters until one grabs this message.
    235   scoped_ptr<ResourceHost> resource_host = CreateResourceHost(params, instance,
    236                                                               nested_msg);
    237 
    238   if (!resource_host.get()) {
    239     NOTREACHED();
    240     return;
    241   }
    242 
    243   // Resource should have been assigned a nonzero PP_Resource.
    244   DCHECK(resource_host->pp_resource());
    245 
    246   resources_[params.pp_resource()] =
    247       linked_ptr<ResourceHost>(resource_host.release());
    248 }
    249 
    250 void PpapiHost::OnHostMsgAttachToPendingHost(PP_Resource pp_resource,
    251                                              int pending_host_id) {
    252   PendingHostResourceMap::iterator found =
    253       pending_resource_hosts_.find(pending_host_id);
    254   if (found == pending_resource_hosts_.end()) {
    255     // Plugin sent a bad ID.
    256     NOTREACHED();
    257     return;
    258   }
    259   found->second->SetPPResourceForPendingHost(pp_resource);
    260   resources_[pp_resource] = found->second;
    261   pending_resource_hosts_.erase(found);
    262 }
    263 
    264 void PpapiHost::OnHostMsgResourceDestroyed(PP_Resource resource) {
    265   ResourceMap::iterator found = resources_.find(resource);
    266   if (found == resources_.end()) {
    267     NOTREACHED();
    268     return;
    269   }
    270   // Invoking the HostResource destructor might result in looking up the
    271   // PP_Resource in resources_. std::map is not well specified as to whether the
    272   // element will be there or not. Therefore, we delay destruction of the
    273   // HostResource until after we've made sure the map no longer contains
    274   // |resource|.
    275   linked_ptr<ResourceHost> delete_at_end_of_scope(found->second);
    276   resources_.erase(found);
    277 }
    278 
    279 ResourceHost* PpapiHost::GetResourceHost(PP_Resource resource) const {
    280   ResourceMap::const_iterator found = resources_.find(resource);
    281   return found == resources_.end() ? NULL : found->second.get();
    282 }
    283 
    284 }  // namespace host
    285 }  // namespace ppapi
    286