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