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