// 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/proxy/plugin_resource.h" #include #include "ppapi/proxy/plugin_globals.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/ppapi_globals.h" namespace ppapi { namespace proxy { PluginResource::PluginResource(Connection connection, PP_Instance instance) : Resource(OBJECT_IS_PROXY, instance), connection_(connection), next_sequence_number_(1), sent_create_to_browser_(false), sent_create_to_renderer_(false), resource_reply_thread_registrar_( PpapiGlobals::Get()->IsPluginGlobals() ? PluginGlobals::Get()->resource_reply_thread_registrar() : NULL) { } PluginResource::~PluginResource() { if (sent_create_to_browser_) { connection_.browser_sender->Send( new PpapiHostMsg_ResourceDestroyed(pp_resource())); } if (sent_create_to_renderer_) { connection_.renderer_sender->Send( new PpapiHostMsg_ResourceDestroyed(pp_resource())); } if (resource_reply_thread_registrar_.get()) resource_reply_thread_registrar_->Unregister(pp_resource()); } void PluginResource::OnReplyReceived( const proxy::ResourceMessageReplyParams& params, const IPC::Message& msg) { TRACE_EVENT2("ppapi proxy", "PluginResource::OnReplyReceived", "Class", IPC_MESSAGE_ID_CLASS(msg.type()), "Line", IPC_MESSAGE_ID_LINE(msg.type())); // Grab the callback for the reply sequence number and run it with |msg|. CallbackMap::iterator it = callbacks_.find(params.sequence()); if (it == callbacks_.end()) { DCHECK(false) << "Callback does not exist for an expected sequence number."; } else { scoped_refptr callback = it->second; callbacks_.erase(it); callback->Run(params, msg); } } void PluginResource::NotifyLastPluginRefWasDeleted() { Resource::NotifyLastPluginRefWasDeleted(); // The callbacks may hold referrences to this object. Normally, we will get // reply messages from the host side and remove them. However, it is possible // that some replies from the host never arrive, e.g., the corresponding // renderer crashes. In that case, we have to clean up the callbacks, // otherwise this object will live forever. callbacks_.clear(); } void PluginResource::NotifyInstanceWasDeleted() { Resource::NotifyInstanceWasDeleted(); // Please see comments in NotifyLastPluginRefWasDeleted() about why we must // clean up the callbacks. // It is possible that NotifyLastPluginRefWasDeleted() is never called for a // resource. For example, those singleton-style resources such as // GamepadResource never expose references to the plugin and thus won't // receive a NotifyLastPluginRefWasDeleted() call. For those resources, we // need to clean up callbacks when the instance goes away. callbacks_.clear(); } void PluginResource::SendCreate(Destination dest, const IPC::Message& msg) { TRACE_EVENT2("ppapi proxy", "PluginResource::SendCreate", "Class", IPC_MESSAGE_ID_CLASS(msg.type()), "Line", IPC_MESSAGE_ID_LINE(msg.type())); if (dest == RENDERER) { DCHECK(!sent_create_to_renderer_); sent_create_to_renderer_ = true; } else { DCHECK(!sent_create_to_browser_); sent_create_to_browser_ = true; } ResourceMessageCallParams params(pp_resource(), GetNextSequence()); GetSender(dest)->Send( new PpapiHostMsg_ResourceCreated(params, pp_instance(), msg)); } void PluginResource::AttachToPendingHost(Destination dest, int pending_host_id) { // Connecting to a pending host is a replacement for "create". if (dest == RENDERER) { DCHECK(!sent_create_to_renderer_); sent_create_to_renderer_ = true; } else { DCHECK(!sent_create_to_browser_); sent_create_to_browser_ = true; } GetSender(dest)->Send( new PpapiHostMsg_AttachToPendingHost(pp_resource(), pending_host_id)); } void PluginResource::Post(Destination dest, const IPC::Message& msg) { TRACE_EVENT2("ppapi proxy", "PluginResource::Post", "Class", IPC_MESSAGE_ID_CLASS(msg.type()), "Line", IPC_MESSAGE_ID_LINE(msg.type())); ResourceMessageCallParams params(pp_resource(), GetNextSequence()); SendResourceCall(dest, params, msg); } bool PluginResource::SendResourceCall( Destination dest, const ResourceMessageCallParams& call_params, const IPC::Message& nested_msg) { // For in-process plugins, we need to send the routing ID with the request. // The browser then uses that routing ID when sending the reply so it will be // routed back to the correct RenderFrameImpl. if (dest == BROWSER && connection_.in_process) { return GetSender(dest)->Send(new PpapiHostMsg_InProcessResourceCall( connection_.browser_sender_routing_id, call_params, nested_msg)); } else { return GetSender(dest)->Send( new PpapiHostMsg_ResourceCall(call_params, nested_msg)); } } int32_t PluginResource::GenericSyncCall( Destination dest, const IPC::Message& msg, IPC::Message* reply, ResourceMessageReplyParams* reply_params) { TRACE_EVENT2("ppapi proxy", "PluginResource::GenericSyncCall", "Class", IPC_MESSAGE_ID_CLASS(msg.type()), "Line", IPC_MESSAGE_ID_LINE(msg.type())); ResourceMessageCallParams params(pp_resource(), GetNextSequence()); params.set_has_callback(); bool success = GetSender(dest)->Send(new PpapiHostMsg_ResourceSyncCall( params, msg, reply_params, reply)); if (success) return reply_params->result(); return PP_ERROR_FAILED; } int32_t PluginResource::GetNextSequence() { // Return the value with wraparound, making sure we don't make a sequence // number with a 0 ID. Note that signed wraparound is undefined in C++ so we // manually check. int32_t ret = next_sequence_number_; if (next_sequence_number_ == std::numeric_limits::max()) next_sequence_number_ = 1; // Skip 0 which is invalid. else next_sequence_number_++; return ret; } } // namespace proxy } // namespace ppapi