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