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 "content/renderer/pepper/ppb_broker_impl.h"
6
7#include "base/logging.h"
8#include "content/common/view_messages.h"
9#include "content/renderer/pepper/host_globals.h"
10#include "content/renderer/pepper/pepper_broker.h"
11#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
12#include "content/renderer/pepper/plugin_module.h"
13#include "content/renderer/render_thread_impl.h"
14#include "content/renderer/render_view_impl.h"
15#include "ppapi/c/pp_errors.h"
16#include "ppapi/shared_impl/platform_file.h"
17#include "third_party/WebKit/public/web/WebDocument.h"
18#include "third_party/WebKit/public/web/WebElement.h"
19#include "third_party/WebKit/public/web/WebPluginContainer.h"
20
21using ppapi::PlatformFileToInt;
22using ppapi::thunk::PPB_Broker_API;
23using ppapi::TrackedCallback;
24
25namespace content {
26
27// PPB_Broker_Impl ------------------------------------------------------
28
29PPB_Broker_Impl::PPB_Broker_Impl(PP_Instance instance)
30    : Resource(ppapi::OBJECT_IS_IMPL, instance),
31      broker_(NULL),
32      connect_callback_(),
33      pipe_handle_(PlatformFileToInt(base::SyncSocket::kInvalidHandle)),
34      routing_id_(RenderThreadImpl::current()->GenerateRoutingID()) {
35  ChildThread::current()->GetRouter()->AddRoute(routing_id_, this);
36}
37
38PPB_Broker_Impl::~PPB_Broker_Impl() {
39  if (broker_) {
40    broker_->Disconnect(this);
41    broker_ = NULL;
42  }
43
44  // The plugin owns the handle.
45  pipe_handle_ = PlatformFileToInt(base::SyncSocket::kInvalidHandle);
46  ChildThread::current()->GetRouter()->RemoveRoute(routing_id_);
47}
48
49PPB_Broker_API* PPB_Broker_Impl::AsPPB_Broker_API() { return this; }
50
51int32_t PPB_Broker_Impl::Connect(
52    scoped_refptr<TrackedCallback> connect_callback) {
53  // TODO(ddorwin): Return PP_ERROR_FAILED if plugin is in-process.
54
55  if (broker_) {
56    // May only be called once.
57    return PP_ERROR_FAILED;
58  }
59
60  PepperPluginInstanceImpl* plugin_instance =
61      HostGlobals::Get()->GetInstance(pp_instance());
62  if (!plugin_instance)
63    return PP_ERROR_FAILED;
64  PluginModule* module = plugin_instance->module();
65  const base::FilePath& broker_path = module->path();
66
67  // The callback must be populated now in case we are connected to the broker
68  // and BrokerConnected is called before ConnectToBroker returns.
69  // Because it must be created now, it must be aborted and cleared if
70  // ConnectToBroker fails.
71  connect_callback_ = connect_callback;
72
73  broker_ = module->GetBroker();
74  if (!broker_) {
75    broker_ = new PepperBroker(module);
76
77    // Have the browser start the broker process for us.
78    RenderThreadImpl::current()->Send(
79        new ViewHostMsg_OpenChannelToPpapiBroker(routing_id_, broker_path));
80  }
81
82  RenderThreadImpl::current()->Send(
83      new ViewHostMsg_RequestPpapiBrokerPermission(
84          plugin_instance->render_frame()->render_view()->GetRoutingID(),
85          routing_id_,
86          GetDocumentUrl(),
87          broker_path));
88
89  // Adds a reference, ensuring that the broker is not deleted when
90  // |broker| goes out of scope.
91  broker_->AddPendingConnect(this);
92
93  return PP_OK_COMPLETIONPENDING;
94}
95
96int32_t PPB_Broker_Impl::GetHandle(int32_t* handle) {
97  if (pipe_handle_ == PlatformFileToInt(base::SyncSocket::kInvalidHandle))
98    return PP_ERROR_FAILED;  // Handle not set yet.
99  *handle = pipe_handle_;
100  return PP_OK;
101}
102
103GURL PPB_Broker_Impl::GetDocumentUrl() {
104  PepperPluginInstanceImpl* plugin_instance =
105      HostGlobals::Get()->GetInstance(pp_instance());
106  return plugin_instance->container()->element().document().url();
107}
108
109// Transfers ownership of the handle to the plugin.
110void PPB_Broker_Impl::BrokerConnected(int32_t handle, int32_t result) {
111  DCHECK(pipe_handle_ == PlatformFileToInt(base::SyncSocket::kInvalidHandle));
112  DCHECK(result == PP_OK ||
113         handle == PlatformFileToInt(base::SyncSocket::kInvalidHandle));
114
115  pipe_handle_ = handle;
116
117  // Synchronous calls are not supported.
118  DCHECK(TrackedCallback::IsPending(connect_callback_));
119
120  connect_callback_->Run(result);
121}
122
123bool PPB_Broker_Impl::OnMessageReceived(const IPC::Message& message) {
124  bool handled = true;
125  IPC_BEGIN_MESSAGE_MAP(PPB_Broker_Impl, message)
126    IPC_MESSAGE_HANDLER(ViewMsg_PpapiBrokerChannelCreated,
127                        OnPpapiBrokerChannelCreated)
128    IPC_MESSAGE_HANDLER(ViewMsg_PpapiBrokerPermissionResult,
129                        OnPpapiBrokerPermissionResult)
130    IPC_MESSAGE_UNHANDLED(handled = false)
131  IPC_END_MESSAGE_MAP()
132  return handled;
133}
134
135void PPB_Broker_Impl::OnPpapiBrokerChannelCreated(
136    base::ProcessId broker_pid,
137    const IPC::ChannelHandle& handle) {
138  broker_->OnBrokerChannelConnected(broker_pid, handle);
139}
140
141void PPB_Broker_Impl::OnPpapiBrokerPermissionResult(bool result) {
142  broker_->OnBrokerPermissionResult(this, result);
143}
144
145}  // namespace content
146