| Index: ppapi/proxy/ppb_broker_proxy.cc
|
| diff --git a/ppapi/proxy/ppb_broker_proxy.cc b/ppapi/proxy/ppb_broker_proxy.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..44747a9791324f13c89ceead76ddf65f956eb5b2
|
| --- /dev/null
|
| +++ b/ppapi/proxy/ppb_broker_proxy.cc
|
| @@ -0,0 +1,284 @@
|
| +// Copyright (c) 2011 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/ppb_broker_proxy.h"
|
| +
|
| +#include "ppapi/c/pp_errors.h"
|
| +#include "ppapi/c/trusted/ppb_broker_trusted.h"
|
| +#include "ppapi/proxy/plugin_dispatcher.h"
|
| +#include "ppapi/proxy/plugin_resource.h"
|
| +#include "ppapi/proxy/ppapi_messages.h"
|
| +
|
| +namespace pp {
|
| +namespace proxy {
|
| +
|
| +class Broker : public PluginResource {
|
| + public:
|
| + explicit Broker(const HostResource& resource);
|
| + virtual ~Broker();
|
| +
|
| + // Resource overrides.
|
| + virtual Broker* AsBroker() { return this; }
|
| +
|
| + bool called_connect_;
|
| + PP_CompletionCallback current_connect_callback_;
|
| +
|
| + // The plugin module owns the handle.
|
| + // The host side transfers ownership of the handle to the plugin side when it
|
| + // sends the IPC. This member holds the handle value for the plugin module
|
| + // to read, but the plugin side of the proxy never takes ownership.
|
| + base::SyncSocket::Handle socket_handle_;
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(Broker);
|
| +};
|
| +
|
| +Broker::Broker(const HostResource& resource)
|
| + : PluginResource(resource),
|
| + called_connect_(false),
|
| + current_connect_callback_(PP_MakeCompletionCallback(NULL, NULL)),
|
| + socket_handle_(base::kInvalidPlatformFileValue) {
|
| +}
|
| +
|
| +Broker::~Broker() {
|
| + // Ensure the callback is always fired.
|
| + if (current_connect_callback_.func) {
|
| + // TODO(brettw) the callbacks at this level should be refactored with a
|
| + // more automatic tracking system like we have in the renderer.
|
| + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableFunction(
|
| + current_connect_callback_.func, current_connect_callback_.user_data,
|
| + static_cast<int32_t>(PP_ERROR_ABORTED)));
|
| + }
|
| +
|
| + socket_handle_ = base::kInvalidPlatformFileValue;
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +base::PlatformFile IntToPlatformFile(int32_t handle) {
|
| +#if defined(OS_WIN)
|
| + return reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
|
| +#elif defined(OS_POSIX)
|
| + return handle;
|
| +#else
|
| + #error Not implemented.
|
| +#endif
|
| +}
|
| +
|
| +int32_t PlatformFileToInt(base::PlatformFile handle) {
|
| +#if defined(OS_WIN)
|
| + return static_cast<int32_t>(reinterpret_cast<intptr_t>(handle));
|
| +#elif defined(OS_POSIX)
|
| + return handle;
|
| +#else
|
| + #error Not implemented.
|
| +#endif
|
| +}
|
| +
|
| +PP_Resource CreateTrusted(PP_Instance instance) {
|
| + PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
|
| + if (!dispatcher)
|
| + return 0;
|
| +
|
| + HostResource result;
|
| + dispatcher->Send(new PpapiHostMsg_PPBBroker_Create(
|
| + INTERFACE_ID_PPB_BROKER, instance, &result));
|
| + if (result.is_null())
|
| + return 0;
|
| +
|
| + linked_ptr<Broker> object(new Broker(result));
|
| + return PluginResourceTracker::GetInstance()->AddResource(object);
|
| +}
|
| +
|
| +PP_Bool IsBrokerTrusted(PP_Resource resource) {
|
| + Broker* object = PluginResource::GetAs<Broker>(resource);
|
| + return BoolToPPBool(!!object);
|
| +}
|
| +
|
| +int32_t Connect(PP_Resource resource,
|
| + PP_CompletionCallback connect_callback) {
|
| + Broker* object = PluginResource::GetAs<Broker>(resource);
|
| + if (!object)
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + Dispatcher* dispatcher = PluginDispatcher::GetForInstance(object->instance());
|
| + if (!dispatcher)
|
| + return PP_ERROR_BADRESOURCE;
|
| +
|
| + if (!connect_callback.func) {
|
| + // Synchronous calls are not supported.
|
| + return PP_ERROR_BADARGUMENT;
|
| + }
|
| +
|
| + if (object->current_connect_callback_.func)
|
| + return PP_ERROR_INPROGRESS;
|
| + else if (object->called_connect_)
|
| + return PP_ERROR_FAILED;
|
| +
|
| + object->current_connect_callback_ = connect_callback;
|
| + object->called_connect_ = true;
|
| +
|
| + bool success = dispatcher->Send(new PpapiHostMsg_PPBBroker_Connect(
|
| + INTERFACE_ID_PPB_BROKER,
|
| + object->host_resource()));
|
| + return success ? PP_OK_COMPLETIONPENDING : PP_ERROR_FAILED;
|
| +}
|
| +
|
| +int32_t GetHandle(PP_Resource resource, int32_t* handle) {
|
| + Broker* object = PluginResource::GetAs<Broker>(resource);
|
| + if (!object)
|
| + return PP_ERROR_BADRESOURCE;
|
| + *handle = PlatformFileToInt(object->socket_handle_);
|
| + return PP_OK;
|
| +}
|
| +
|
| +const PPB_BrokerTrusted broker_interface = {
|
| + &CreateTrusted,
|
| + &IsBrokerTrusted,
|
| + &Connect,
|
| + &GetHandle,
|
| +};
|
| +
|
| +InterfaceProxy* CreateBrokerProxy(Dispatcher* dispatcher,
|
| + const void* target_interface) {
|
| + return new PPB_Broker_Proxy(dispatcher, target_interface);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +PPB_Broker_Proxy::PPB_Broker_Proxy(Dispatcher* dispatcher,
|
| + const void* target_interface)
|
| + : InterfaceProxy(dispatcher, target_interface) ,
|
| + callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)){
|
| +}
|
| +
|
| +PPB_Broker_Proxy::~PPB_Broker_Proxy() {
|
| +}
|
| +
|
| +// static
|
| +const InterfaceProxy::Info* PPB_Broker_Proxy::GetInfo() {
|
| + static const Info info = {
|
| + &broker_interface,
|
| + PPB_BROKER_TRUSTED_INTERFACE,
|
| + INTERFACE_ID_PPB_BROKER,
|
| + true,
|
| + &CreateBrokerProxy,
|
| + };
|
| + return &info;
|
| +}
|
| +
|
| +bool PPB_Broker_Proxy::OnMessageReceived(const IPC::Message& msg) {
|
| + bool handled = true;
|
| + IPC_BEGIN_MESSAGE_MAP(PPB_Broker_Proxy, msg)
|
| + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBBroker_Create, OnMsgCreate)
|
| + IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBBroker_Connect, OnMsgConnect)
|
| + IPC_MESSAGE_HANDLER(PpapiMsg_PPBBroker_ConnectComplete,
|
| + OnMsgConnectComplete)
|
| + IPC_MESSAGE_UNHANDLED(handled = false)
|
| + IPC_END_MESSAGE_MAP()
|
| + return handled;
|
| +}
|
| +
|
| +void PPB_Broker_Proxy::OnMsgCreate(PP_Instance instance,
|
| + HostResource* result_resource) {
|
| + result_resource->SetHostResource(
|
| + instance,
|
| + ppb_broker_target()->CreateTrusted(instance));
|
| +}
|
| +
|
| +void PPB_Broker_Proxy::OnMsgConnect(const HostResource& broker) {
|
| + CompletionCallback callback = callback_factory_.NewCallback(
|
| + &PPB_Broker_Proxy::ConnectCompleteInHost, broker);
|
| +
|
| + int32_t result = ppb_broker_target()->Connect(
|
| + broker.host_resource(),
|
| + callback.pp_completion_callback());
|
| + if (result != PP_OK_COMPLETIONPENDING)
|
| + callback.Run(result);
|
| +}
|
| +
|
| +// Called in the plugin to handle the connect callback.
|
| +// The proxy owns the handle and transfers it to the Broker. At that point,
|
| +// the plugin owns the handle and is responsible for closing it.
|
| +// The caller guarantees that socket_handle is not valid if result is not PP_OK.
|
| +void PPB_Broker_Proxy::OnMsgConnectComplete(
|
| + const HostResource& broker,
|
| + IPC::PlatformFileForTransit socket_handle,
|
| + int32_t result) {
|
| + DCHECK(result == PP_OK ||
|
| + socket_handle == IPC::InvalidPlatformFileForTransit());
|
| +
|
| + Broker* object = NULL;
|
| + if (result == PP_OK) {
|
| + object = PluginResource::GetAs<Broker>(
|
| + PluginResourceTracker::GetInstance()->PluginResourceForHostResource(
|
| + broker));
|
| + if (!object)
|
| + result = PP_ERROR_BADRESOURCE;
|
| + }
|
| +
|
| + if (result == PP_OK) {
|
| + object->socket_handle_ =
|
| + IPC::PlatformFileForTransitToPlatformFile(socket_handle);
|
| + } else {
|
| + // The caller may still have given us a handle in the failure case.
|
| + // The easiest way to clean it up is to just put it in an object
|
| + // and then close them. This failure case is not performance critical.
|
| + base::SyncSocket temp_socket(
|
| + IPC::PlatformFileForTransitToPlatformFile(socket_handle));
|
| + }
|
| +
|
| + if (!object->current_connect_callback_.func) {
|
| + // The handle might leak if the plugin never calls GetHandle().
|
| + return;
|
| + }
|
| +
|
| + PP_CompletionCallback callback = object->current_connect_callback_;
|
| + object->current_connect_callback_ = PP_MakeCompletionCallback(NULL, NULL);
|
| + PP_RunCompletionCallback(&callback, result);
|
| +}
|
| +
|
| +// Callback on the host side.
|
| +// Transfers ownership of the handle to the plugin side. This function must
|
| +// either successfully call the callback or close the handle.
|
| +void PPB_Broker_Proxy::ConnectCompleteInHost(int32_t result,
|
| + const HostResource& broker) {
|
| + IPC::PlatformFileForTransit foreign_socket_handle =
|
| + IPC::InvalidPlatformFileForTransit();
|
| + if (result == PP_OK) {
|
| + int32_t socket_handle = PlatformFileToInt(base::kInvalidPlatformFileValue);
|
| + result = ppb_broker_target()->GetHandle(broker.host_resource(),
|
| + &socket_handle);
|
| + DCHECK(result == PP_OK ||
|
| + socket_handle == PlatformFileToInt(base::kInvalidPlatformFileValue));
|
| +
|
| + if (result == PP_OK) {
|
| + foreign_socket_handle =
|
| + dispatcher()->ShareHandleWithRemote(IntToPlatformFile(socket_handle),
|
| + true);
|
| + if (foreign_socket_handle == IPC::InvalidPlatformFileForTransit()) {
|
| + result = PP_ERROR_FAILED;
|
| + // Assume the local handle was closed even if the foreign handle could
|
| + // not be created.
|
| + }
|
| + }
|
| + }
|
| + DCHECK(result == PP_OK ||
|
| + foreign_socket_handle == IPC::InvalidPlatformFileForTransit());
|
| +
|
| + bool success = dispatcher()->Send(new PpapiMsg_PPBBroker_ConnectComplete(
|
| + INTERFACE_ID_PPB_FILE_SYSTEM, broker, foreign_socket_handle, result));
|
| +
|
| + if (!success || result == PP_OK) {
|
| + // 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));
|
| + }
|
| +}
|
| +
|
| +} // namespace proxy
|
| +} // namespace pp
|
|
|