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