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
18namespace ppapi {
19namespace host {
20
21using proxy::SerializedHandle;
22
23namespace {
24
25// Put a cap on the maximum number of resources so we don't explode if the
26// renderer starts spamming us.
27const size_t kMaxResourcesPerPlugin = 1 << 14;
28
29}  // namespace
30
31PpapiHost::PpapiHost(IPC::Sender* sender,
32                     const PpapiPermissions& perms)
33    : sender_(sender),
34      permissions_(perms),
35      next_pending_resource_host_id_(1) {
36}
37
38PpapiHost::~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
49bool PpapiHost::Send(IPC::Message* msg) {
50  return sender_->Send(msg);
51}
52
53bool 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
83void 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
103void PpapiHost::SendUnsolicitedReply(PP_Resource resource,
104                                     const IPC::Message& msg) {
105  SendUnsolicitedReplyWithHandles(
106      resource, msg, std::vector<SerializedHandle>());
107}
108
109void 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
125scoped_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
140int 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
158void PpapiHost::AddHostFactoryFilter(scoped_ptr<HostFactory> filter) {
159  host_factory_filters_.push_back(filter.release());
160}
161
162void PpapiHost::AddInstanceMessageFilter(
163    scoped_ptr<InstanceMessageFilter> filter) {
164  instance_message_filters_.push_back(filter.release());
165}
166
167void 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
177void 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
188void 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
204void 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
221void 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
250void 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
264void 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
279ResourceHost* 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