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
17namespace ppapi {
18namespace host {
19
20namespace {
21
22// Put a cap on the maximum number of resources so we don't explode if the
23// renderer starts spamming us.
24const size_t kMaxResourcesPerPlugin = 1 << 14;
25
26}  // namespace
27
28PpapiHost::PpapiHost(IPC::Sender* sender,
29                     const PpapiPermissions& perms)
30    : sender_(sender),
31      permissions_(perms),
32      next_pending_resource_host_id_(1) {
33}
34
35PpapiHost::~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
46bool PpapiHost::Send(IPC::Message* msg) {
47  return sender_->Send(msg);
48}
49
50bool 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
80void 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
100void 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
110scoped_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
125int 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
143void PpapiHost::AddHostFactoryFilter(scoped_ptr<HostFactory> filter) {
144  host_factory_filters_.push_back(filter.release());
145}
146
147void PpapiHost::AddInstanceMessageFilter(
148    scoped_ptr<InstanceMessageFilter> filter) {
149  instance_message_filters_.push_back(filter.release());
150}
151
152void 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
162void 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
173void 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
189void 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
206void 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
235void 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
249void 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
264ResourceHost* 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