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.
5 #include "ppapi/host/ppapi_host.h"
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"
21 using proxy::SerializedHandle
;
25 // Put a cap on the maximum number of resources so we don't explode if the
26 // renderer starts spamming us.
27 const size_t kMaxResourcesPerPlugin
= 1 << 14;
31 PpapiHost::PpapiHost(IPC::Sender
* sender
,
32 const PpapiPermissions
& perms
)
35 next_pending_resource_host_id_(1) {
38 PpapiHost::~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
42 instance_message_filters_
.clear();
44 // The resources may also want to use us in their destructors.
46 pending_resource_hosts_
.clear();
49 bool PpapiHost::Send(IPC::Message
* msg
) {
50 return sender_
->Send(msg
);
53 bool PpapiHost::OnMessageReceived(const IPC::Message
& msg
) {
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)
72 for (size_t i
= 0; i
< instance_message_filters_
.size(); i
++) {
73 if (instance_message_filters_
[i
]->OnInstanceMessageReceived(msg
)) {
83 void 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
,
91 Send(context
.sync_reply_msg
);
93 if (context
.routing_id
!= MSG_ROUTING_NONE
) {
94 Send(new PpapiHostMsg_InProcessResourceReply(context
.routing_id
,
98 Send(new PpapiPluginMsg_ResourceReply(context
.params
, msg
));
103 void PpapiHost::SendUnsolicitedReply(PP_Resource resource
,
104 const IPC::Message
& msg
) {
105 SendUnsolicitedReplyWithHandles(
106 resource
, msg
, std::vector
<SerializedHandle
>());
109 void 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
);
122 Send(new PpapiPluginMsg_ResourceReply(params
, msg
));
125 scoped_ptr
<ResourceHost
> PpapiHost::CreateResourceHost(
126 PP_Resource resource
,
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
++) {
133 host_factory_filters_
[i
]
134 ->CreateResourceHost(this, resource
, instance
, nested_msg
)
136 if (resource_host
.get())
139 return resource_host
.Pass();
142 int PpapiHost::AddPendingResourceHost(scoped_ptr
<ResourceHost
> resource_host
) {
143 // The resource ID should not be assigned.
144 if (!resource_host
.get() || resource_host
->pp_resource() != 0) {
149 if (pending_resource_hosts_
.size() + resources_
.size()
150 >= kMaxResourcesPerPlugin
) {
154 int pending_id
= next_pending_resource_host_id_
++;
155 pending_resource_hosts_
[pending_id
] =
156 linked_ptr
<ResourceHost
>(resource_host
.release());
160 void PpapiHost::AddHostFactoryFilter(scoped_ptr
<HostFactory
> filter
) {
161 host_factory_filters_
.push_back(filter
.release());
164 void PpapiHost::AddInstanceMessageFilter(
165 scoped_ptr
<InstanceMessageFilter
> filter
) {
166 instance_message_filters_
.push_back(filter
.release());
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
);
179 void PpapiHost::OnHostMsgInProcessResourceCall(
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
);
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
);
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());
212 // CAUTION: Handling the message may cause the destruction of this object.
213 resource_host
->HandleMessage(nested_msg
, context
);
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
);
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()));
231 if (pending_resource_hosts_
.size() + resources_
.size()
232 >= kMaxResourcesPerPlugin
) {
236 // Run through all filters until one grabs this message.
237 scoped_ptr
<ResourceHost
> resource_host
=
238 CreateResourceHost(params
.pp_resource(), instance
, nested_msg
);
240 if (!resource_host
.get()) {
245 // Resource should have been assigned a nonzero PP_Resource.
246 DCHECK(resource_host
->pp_resource());
248 resources_
[params
.pp_resource()] =
249 linked_ptr
<ResourceHost
>(resource_host
.release());
252 void PpapiHost::OnHostMsgAttachToPendingHost(PP_Resource pp_resource
,
253 int pending_host_id
) {
254 PendingHostResourceMap::iterator found
=
255 pending_resource_hosts_
.find(pending_host_id
);
256 if (found
== pending_resource_hosts_
.end()) {
257 // Plugin sent a bad ID.
261 found
->second
->SetPPResourceForPendingHost(pp_resource
);
262 resources_
[pp_resource
] = found
->second
;
263 pending_resource_hosts_
.erase(found
);
266 void PpapiHost::OnHostMsgResourceDestroyed(PP_Resource resource
) {
267 ResourceMap::iterator found
= resources_
.find(resource
);
268 if (found
== resources_
.end()) {
272 // Invoking the HostResource destructor might result in looking up the
273 // PP_Resource in resources_. std::map is not well specified as to whether the
274 // element will be there or not. Therefore, we delay destruction of the
275 // HostResource until after we've made sure the map no longer contains
277 linked_ptr
<ResourceHost
> delete_at_end_of_scope(found
->second
);
278 resources_
.erase(found
);
281 ResourceHost
* PpapiHost::GetResourceHost(PP_Resource resource
) const {
282 ResourceMap::const_iterator found
= resources_
.find(resource
);
283 return found
== resources_
.end() ? NULL
: found
->second
.get();