// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ppapi/host/ppapi_host.h" #include "base/logging.h" #include "ppapi/c/pp_errors.h" #include "ppapi/host/host_factory.h" #include "ppapi/host/host_message_context.h" #include "ppapi/host/instance_message_filter.h" #include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/resource_message_params.h" #include "ppapi/shared_impl/host_resource.h" namespace ppapi { namespace host { namespace { // Put a cap on the maximum number of resources so we don't explode if the // renderer starts spamming us. const size_t kMaxResourcesPerPlugin = 1 << 14; } // namespace PpapiHost::PpapiHost(IPC::Sender* sender, const PpapiPermissions& perms) : sender_(sender), permissions_(perms), next_pending_resource_host_id_(1) { } PpapiHost::~PpapiHost() { // Delete these explicitly before destruction since then the host is still // technically alive in case one of the filters accesses us from the // destructor. instance_message_filters_.clear(); // The resources may also want to use us in their destructors. resources_.clear(); pending_resource_hosts_.clear(); } bool PpapiHost::Send(IPC::Message* msg) { return sender_->Send(msg); } bool PpapiHost::OnMessageReceived(const IPC::Message& msg) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(PpapiHost, msg) IPC_MESSAGE_HANDLER(PpapiHostMsg_ResourceCall, OnHostMsgResourceCall) IPC_MESSAGE_HANDLER(PpapiHostMsg_InProcessResourceCall, OnHostMsgInProcessResourceCall) IPC_MESSAGE_HANDLER_DELAY_REPLY(PpapiHostMsg_ResourceSyncCall, OnHostMsgResourceSyncCall) IPC_MESSAGE_HANDLER(PpapiHostMsg_ResourceCreated, OnHostMsgResourceCreated) IPC_MESSAGE_HANDLER(PpapiHostMsg_AttachToPendingHost, OnHostMsgAttachToPendingHost) IPC_MESSAGE_HANDLER(PpapiHostMsg_ResourceDestroyed, OnHostMsgResourceDestroyed) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() if (!handled) { for (size_t i = 0; i < instance_message_filters_.size(); i++) { if (instance_message_filters_[i]->OnInstanceMessageReceived(msg)) { handled = true; break; } } } return handled; } void PpapiHost::SendReply(const ReplyMessageContext& context, const IPC::Message& msg) { TRACE_EVENT2("ppapi proxy", "PpapiHost::SendReply", "Class", IPC_MESSAGE_ID_CLASS(msg.type()), "Line", IPC_MESSAGE_ID_LINE(msg.type())); if (context.sync_reply_msg) { PpapiHostMsg_ResourceSyncCall::WriteReplyParams(context.sync_reply_msg, context.params, msg); Send(context.sync_reply_msg); } else { if (context.routing_id != MSG_ROUTING_NONE) { Send(new PpapiHostMsg_InProcessResourceReply(context.routing_id, context.params, msg)); } else { Send(new PpapiPluginMsg_ResourceReply(context.params, msg)); } } } void PpapiHost::SendUnsolicitedReply(PP_Resource resource, const IPC::Message& msg) { TRACE_EVENT2("ppapi proxy", "PpapiHost::SendUnsolicitedReply", "Class", IPC_MESSAGE_ID_CLASS(msg.type()), "Line", IPC_MESSAGE_ID_LINE(msg.type())); DCHECK(resource); // If this fails, host is probably pending. proxy::ResourceMessageReplyParams params(resource, 0); Send(new PpapiPluginMsg_ResourceReply(params, msg)); } scoped_ptr PpapiHost::CreateResourceHost( const proxy::ResourceMessageCallParams& params, PP_Instance instance, const IPC::Message& nested_msg) { scoped_ptr resource_host; DCHECK(!host_factory_filters_.empty()); // Caller forgot to add a factory. for (size_t i = 0; i < host_factory_filters_.size(); i++) { resource_host = host_factory_filters_[i]->CreateResourceHost( this, params, instance, nested_msg).Pass(); if (resource_host.get()) break; } return resource_host.Pass(); } int PpapiHost::AddPendingResourceHost(scoped_ptr resource_host) { // The resource ID should not be assigned. if (!resource_host.get() || resource_host->pp_resource() != 0) { NOTREACHED(); return 0; } if (pending_resource_hosts_.size() + resources_.size() >= kMaxResourcesPerPlugin) { return 0; } int pending_id = next_pending_resource_host_id_++; pending_resource_hosts_[pending_id] = linked_ptr(resource_host.release()); return pending_id; } void PpapiHost::AddHostFactoryFilter(scoped_ptr filter) { host_factory_filters_.push_back(filter.release()); } void PpapiHost::AddInstanceMessageFilter( scoped_ptr filter) { instance_message_filters_.push_back(filter.release()); } void PpapiHost::OnHostMsgResourceCall( const proxy::ResourceMessageCallParams& params, const IPC::Message& nested_msg) { TRACE_EVENT2("ppapi proxy", "PpapiHost::OnHostMsgResourceCall", "Class", IPC_MESSAGE_ID_CLASS(nested_msg.type()), "Line", IPC_MESSAGE_ID_LINE(nested_msg.type())); HostMessageContext context(params); HandleResourceCall(params, nested_msg, &context); } void PpapiHost::OnHostMsgInProcessResourceCall( int routing_id, const proxy::ResourceMessageCallParams& params, const IPC::Message& nested_msg) { TRACE_EVENT2("ppapi proxy", "PpapiHost::OnHostMsgInProcessResourceCall", "Class", IPC_MESSAGE_ID_CLASS(nested_msg.type()), "Line", IPC_MESSAGE_ID_LINE(nested_msg.type())); HostMessageContext context(routing_id, params); HandleResourceCall(params, nested_msg, &context); } void PpapiHost::OnHostMsgResourceSyncCall( const proxy::ResourceMessageCallParams& params, const IPC::Message& nested_msg, IPC::Message* reply_msg) { TRACE_EVENT2("ppapi proxy", "PpapiHost::OnHostMsgResourceSyncCall", "Class", IPC_MESSAGE_ID_CLASS(nested_msg.type()), "Line", IPC_MESSAGE_ID_LINE(nested_msg.type())); // Sync messages should always have callback set because they always expect // a reply from the host. DCHECK(params.has_callback()); // Stash the |reply_msg| in the context so that it can be used to reply // to the sync message. HostMessageContext context(params, reply_msg); HandleResourceCall(params, nested_msg, &context); } void PpapiHost::HandleResourceCall( const proxy::ResourceMessageCallParams& params, const IPC::Message& nested_msg, HostMessageContext* context) { ResourceHost* resource_host = GetResourceHost(params.pp_resource()); if (resource_host) { // CAUTION: Handling the message may cause the destruction of this object. resource_host->HandleMessage(nested_msg, context); } else { if (context->params.has_callback()) { ReplyMessageContext reply_context = context->MakeReplyMessageContext(); reply_context.params.set_result(PP_ERROR_BADRESOURCE); SendReply(reply_context, context->reply_msg); } } } void PpapiHost::OnHostMsgResourceCreated( const proxy::ResourceMessageCallParams& params, PP_Instance instance, const IPC::Message& nested_msg) { TRACE_EVENT2("ppapi proxy", "PpapiHost::OnHostMsgResourceCreated", "Class", IPC_MESSAGE_ID_CLASS(nested_msg.type()), "Line", IPC_MESSAGE_ID_LINE(nested_msg.type())); if (pending_resource_hosts_.size() + resources_.size() >= kMaxResourcesPerPlugin) { return; } // Run through all filters until one grabs this message. scoped_ptr resource_host = CreateResourceHost(params, instance, nested_msg); if (!resource_host.get()) { NOTREACHED(); return; } // Resource should have been assigned a nonzero PP_Resource. DCHECK(resource_host->pp_resource()); resources_[params.pp_resource()] = linked_ptr(resource_host.release()); } void PpapiHost::OnHostMsgAttachToPendingHost(PP_Resource pp_resource, int pending_host_id) { PendingHostResourceMap::iterator found = pending_resource_hosts_.find(pending_host_id); if (found == pending_resource_hosts_.end()) { // Plugin sent a bad ID. NOTREACHED(); return; } found->second->SetPPResourceForPendingHost(pp_resource); resources_[pp_resource] = found->second; pending_resource_hosts_.erase(found); } void PpapiHost::OnHostMsgResourceDestroyed(PP_Resource resource) { ResourceMap::iterator found = resources_.find(resource); if (found == resources_.end()) { NOTREACHED(); return; } // Invoking the HostResource destructor might result in looking up the // PP_Resource in resources_. std::map is not well specified as to whether the // element will be there or not. Therefore, we delay destruction of the // HostResource until after we've made sure the map no longer contains // |resource|. linked_ptr delete_at_end_of_scope(found->second); resources_.erase(found); } ResourceHost* PpapiHost::GetResourceHost(PP_Resource resource) const { ResourceMap::const_iterator found = resources_.find(resource); return found == resources_.end() ? NULL : found->second.get(); } } // namespace host } // namespace ppapi