// 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 "content/renderer/pepper/pepper_broker.h" #include "build/build_config.h" #include "content/renderer/pepper/pepper_proxy_channel_delegate_impl.h" #include "content/renderer/pepper/plugin_module.h" #include "content/renderer/pepper/ppb_broker_impl.h" #include "content/renderer/pepper/renderer_restrict_dispatch_group.h" #include "ipc/ipc_channel_handle.h" #include "ppapi/proxy/broker_dispatcher.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/platform_file.h" #if defined(OS_WIN) #include <windows.h> #endif namespace content { namespace { base::SyncSocket::Handle DuplicateHandle(base::SyncSocket::Handle handle) { base::SyncSocket::Handle out_handle = base::kInvalidPlatformFileValue; #if defined(OS_WIN) DWORD options = DUPLICATE_SAME_ACCESS; if (!::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), &out_handle, 0, FALSE, options)) { out_handle = base::kInvalidPlatformFileValue; } #elif defined(OS_POSIX) // If asked to close the source, we can simply re-use the source fd instead of // dup()ing and close()ing. out_handle = ::dup(handle); #else #error Not implemented. #endif return out_handle; } } // namespace PepperBrokerDispatcherWrapper::PepperBrokerDispatcherWrapper() { } PepperBrokerDispatcherWrapper::~PepperBrokerDispatcherWrapper() { } bool PepperBrokerDispatcherWrapper::Init( base::ProcessId broker_pid, const IPC::ChannelHandle& channel_handle) { if (channel_handle.name.empty()) return false; #if defined(OS_POSIX) DCHECK_NE(-1, channel_handle.socket.fd); if (channel_handle.socket.fd == -1) return false; #endif dispatcher_delegate_.reset(new PepperProxyChannelDelegateImpl); dispatcher_.reset( new ppapi::proxy::BrokerHostDispatcher()); if (!dispatcher_->InitBrokerWithChannel(dispatcher_delegate_.get(), broker_pid, channel_handle, true)) { // Client. dispatcher_.reset(); dispatcher_delegate_.reset(); return false; } dispatcher_->channel()->SetRestrictDispatchChannelGroup( kRendererRestrictDispatchGroup_Pepper); return true; } // Does not take ownership of the local pipe. int32_t PepperBrokerDispatcherWrapper::SendHandleToBroker( PP_Instance instance, base::SyncSocket::Handle handle) { IPC::PlatformFileForTransit foreign_socket_handle = dispatcher_->ShareHandleWithRemote(handle, false); if (foreign_socket_handle == IPC::InvalidPlatformFileForTransit()) return PP_ERROR_FAILED; int32_t result; if (!dispatcher_->Send( new PpapiMsg_ConnectToPlugin(instance, foreign_socket_handle, &result))) { // The plugin did not receive the handle, so it must be closed. // The easiest way to clean it up is to just put it in an object // and then close it. This failure case is not performance critical. // The handle could still leak if Send succeeded but the IPC later failed. base::SyncSocket temp_socket( IPC::PlatformFileForTransitToPlatformFile(foreign_socket_handle)); return PP_ERROR_FAILED; } return result; } PepperBroker::PepperBroker(PluginModule* plugin_module) : plugin_module_(plugin_module) { DCHECK(plugin_module_); plugin_module_->SetBroker(this); } PepperBroker::~PepperBroker() { ReportFailureToClients(PP_ERROR_ABORTED); plugin_module_->SetBroker(NULL); plugin_module_ = NULL; } // If the channel is not ready, queue the connection. void PepperBroker::AddPendingConnect(PPB_Broker_Impl* client) { DCHECK(pending_connects_.find(client) == pending_connects_.end()) << "Connect was already called for this client"; // Ensure this object and the associated broker exist as long as the // client exists. There is a corresponding Release() call in Disconnect(), // which is called when the PPB_Broker_Impl is destroyed. The only other // possible reference is in pending_connect_broker_, which only holds a // transient reference. This ensures the broker is available as long as the // plugin needs it and allows the plugin to release the broker when it is no // longer using it. AddRef(); pending_connects_[client].client = client->AsWeakPtr(); } void PepperBroker::Disconnect(PPB_Broker_Impl* client) { // Remove the pending connect if one exists. This class will not call client's // callback. pending_connects_.erase(client); // TODO(ddorwin): Send message disconnect message using dispatcher_. // Release the reference added in Connect(). // This must be the last statement because it may delete this object. Release(); } void PepperBroker::OnBrokerChannelConnected( base::ProcessId broker_pid, const IPC::ChannelHandle& channel_handle) { scoped_ptr<PepperBrokerDispatcherWrapper> dispatcher( new PepperBrokerDispatcherWrapper); if (!dispatcher->Init(broker_pid, channel_handle)) { ReportFailureToClients(PP_ERROR_FAILED); return; } dispatcher_.reset(dispatcher.release()); // Process all pending channel requests from the plugins. for (ClientMap::iterator i = pending_connects_.begin(); i != pending_connects_.end();) { base::WeakPtr<PPB_Broker_Impl>& weak_ptr = i->second.client; if (!i->second.is_authorized) { ++i; continue; } if (weak_ptr.get()) ConnectPluginToBroker(weak_ptr.get()); pending_connects_.erase(i++); } } void PepperBroker::OnBrokerPermissionResult(PPB_Broker_Impl* client, bool result) { ClientMap::iterator entry = pending_connects_.find(client); if (entry == pending_connects_.end()) return; if (!entry->second.client.get()) { // Client has gone away. pending_connects_.erase(entry); return; } if (!result) { // Report failure. client->BrokerConnected( ppapi::PlatformFileToInt(base::kInvalidPlatformFileValue), PP_ERROR_NOACCESS); pending_connects_.erase(entry); return; } if (dispatcher_) { ConnectPluginToBroker(client); pending_connects_.erase(entry); return; } // Mark the request as authorized, continue waiting for the broker // connection. DCHECK(!entry->second.is_authorized); entry->second.is_authorized = true; } PepperBroker::PendingConnection::PendingConnection() : is_authorized(false) { } PepperBroker::PendingConnection::~PendingConnection() { } void PepperBroker::ReportFailureToClients(int error_code) { DCHECK_NE(PP_OK, error_code); for (ClientMap::iterator i = pending_connects_.begin(); i != pending_connects_.end(); ++i) { base::WeakPtr<PPB_Broker_Impl>& weak_ptr = i->second.client; if (weak_ptr.get()) { weak_ptr->BrokerConnected( ppapi::PlatformFileToInt(base::kInvalidPlatformFileValue), error_code); } } pending_connects_.clear(); } void PepperBroker::ConnectPluginToBroker(PPB_Broker_Impl* client) { base::SyncSocket::Handle plugin_handle = base::kInvalidPlatformFileValue; int32_t result = PP_OK; // The socket objects will be deleted when this function exits, closing the // handles. Any uses of the socket must duplicate them. scoped_ptr<base::SyncSocket> broker_socket(new base::SyncSocket()); scoped_ptr<base::SyncSocket> plugin_socket(new base::SyncSocket()); if (base::SyncSocket::CreatePair(broker_socket.get(), plugin_socket.get())) { result = dispatcher_->SendHandleToBroker(client->pp_instance(), broker_socket->handle()); // If the broker has its pipe handle, duplicate the plugin's handle. // Otherwise, the plugin's handle will be automatically closed. if (result == PP_OK) plugin_handle = DuplicateHandle(plugin_socket->handle()); } else { result = PP_ERROR_FAILED; } // TOOD(ddorwin): Change the IPC to asynchronous: Queue an object containing // client and plugin_socket.release(), then return. // That message handler will then call client->BrokerConnected() with the // saved pipe handle. // Temporarily, just call back. client->BrokerConnected(ppapi::PlatformFileToInt(plugin_handle), result); } } // namespace content