• 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/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 
PpapiHost(IPC::Sender * sender,const PpapiPermissions & perms)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 
~PpapiHost()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 
Send(IPC::Message * msg)46 bool PpapiHost::Send(IPC::Message* msg) {
47   return sender_->Send(msg);
48 }
49 
OnMessageReceived(const IPC::Message & msg)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 
SendReply(const ReplyMessageContext & context,const IPC::Message & msg)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 
SendUnsolicitedReply(PP_Resource resource,const IPC::Message & msg)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 
CreateResourceHost(const proxy::ResourceMessageCallParams & params,PP_Instance instance,const IPC::Message & nested_msg)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 
AddPendingResourceHost(scoped_ptr<ResourceHost> resource_host)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 
AddHostFactoryFilter(scoped_ptr<HostFactory> filter)143 void PpapiHost::AddHostFactoryFilter(scoped_ptr<HostFactory> filter) {
144   host_factory_filters_.push_back(filter.release());
145 }
146 
AddInstanceMessageFilter(scoped_ptr<InstanceMessageFilter> filter)147 void PpapiHost::AddInstanceMessageFilter(
148     scoped_ptr<InstanceMessageFilter> filter) {
149   instance_message_filters_.push_back(filter.release());
150 }
151 
OnHostMsgResourceCall(const proxy::ResourceMessageCallParams & params,const IPC::Message & nested_msg)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 
OnHostMsgInProcessResourceCall(int routing_id,const proxy::ResourceMessageCallParams & params,const IPC::Message & nested_msg)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 
OnHostMsgResourceSyncCall(const proxy::ResourceMessageCallParams & params,const IPC::Message & nested_msg,IPC::Message * reply_msg)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 
HandleResourceCall(const proxy::ResourceMessageCallParams & params,const IPC::Message & nested_msg,HostMessageContext * context)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 
OnHostMsgResourceCreated(const proxy::ResourceMessageCallParams & params,PP_Instance instance,const IPC::Message & nested_msg)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 
OnHostMsgAttachToPendingHost(PP_Resource pp_resource,int pending_host_id)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 
OnHostMsgResourceDestroyed(PP_Resource resource)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 
GetResourceHost(PP_Resource resource) const264 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