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