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/pepper_in_process_router.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "content/public/renderer/render_thread.h"
10#include "content/public/renderer/render_view.h"
11#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
12#include "ipc/ipc_message.h"
13#include "ipc/ipc_sender.h"
14#include "ppapi/proxy/ppapi_messages.h"
15#include "ppapi/shared_impl/ppapi_globals.h"
16#include "ppapi/shared_impl/resource_tracker.h"
17
18using ppapi::UnpackMessage;
19
20namespace content {
21
22class PepperInProcessRouter::Channel : public IPC::Sender {
23 public:
24  Channel(const base::Callback<bool(IPC::Message*)>& callback)
25      : callback_(callback) {}
26
27  virtual ~Channel() {}
28
29  virtual bool Send(IPC::Message* message) OVERRIDE {
30    return callback_.Run(message);
31  }
32
33 private:
34  base::Callback<bool(IPC::Message*)> callback_;
35};
36
37PepperInProcessRouter::PepperInProcessRouter(
38    RendererPpapiHostImpl* host_impl)
39    : host_impl_(host_impl),
40      pending_message_id_(0),
41      reply_result_(false),
42      weak_factory_(this) {
43  browser_channel_.reset(
44      new Channel(base::Bind(&PepperInProcessRouter::SendToBrowser,
45                             base::Unretained(this))));
46  host_to_plugin_router_.reset(
47      new Channel(base::Bind(&PepperInProcessRouter::SendToPlugin,
48                             base::Unretained(this))));
49  plugin_to_host_router_.reset(
50      new Channel(base::Bind(&PepperInProcessRouter::SendToHost,
51                             base::Unretained(this))));
52}
53
54PepperInProcessRouter::~PepperInProcessRouter() {
55}
56
57IPC::Sender* PepperInProcessRouter::GetPluginToRendererSender() {
58  return plugin_to_host_router_.get();
59}
60
61IPC::Sender* PepperInProcessRouter::GetRendererToPluginSender() {
62  return host_to_plugin_router_.get();
63}
64
65ppapi::proxy::Connection PepperInProcessRouter::GetPluginConnection(
66    PP_Instance instance) {
67  int routing_id = 0;
68  RenderView* view = host_impl_->GetRenderViewForInstance(instance);
69  if (view)
70    routing_id = view->GetRoutingID();
71  return ppapi::proxy::Connection(browser_channel_.get(),
72                                  plugin_to_host_router_.get(),
73                                  routing_id);
74}
75
76// static
77bool PepperInProcessRouter::OnPluginMsgReceived(const IPC::Message& msg) {
78  // Emulate the proxy by dispatching the relevant message here.
79  ppapi::proxy::ResourceMessageReplyParams reply_params;
80  IPC::Message nested_msg;
81
82  if (msg.type() == PpapiPluginMsg_ResourceReply::ID) {
83    // Resource reply from the renderer (no routing id).
84    if (!UnpackMessage<PpapiPluginMsg_ResourceReply>(msg, &reply_params,
85                                                     &nested_msg)) {
86      NOTREACHED();
87      return false;
88    }
89  } else if (msg.type() == PpapiHostMsg_InProcessResourceReply::ID) {
90    // Resource reply from the browser (has a routing id).
91    if (!UnpackMessage<PpapiHostMsg_InProcessResourceReply>(msg, &reply_params,
92                                                            &nested_msg)) {
93      NOTREACHED();
94      return false;
95    }
96  } else {
97    return false;
98  }
99  ppapi::Resource* resource =
100      ppapi::PpapiGlobals::Get()->GetResourceTracker()->GetResource(
101          reply_params.pp_resource());
102  // If the resource doesn't exist, it may have been destroyed so just ignore
103  // the message.
104  if (resource)
105    resource->OnReplyReceived(reply_params, nested_msg);
106  return true;
107}
108
109bool PepperInProcessRouter::SendToHost(IPC::Message* msg) {
110  scoped_ptr<IPC::Message> message(msg);
111
112  if (!message->is_sync()) {
113    // If this is a resource destroyed message, post a task to dispatch it.
114    // Dispatching it synchronously can cause the host to re-enter the proxy
115    // code while we're still in the resource destructor, leading to a crash.
116    // http://crbug.com/276368.
117    // This won't cause message reordering problems because the resource
118    // destroyed message is always the last one sent for a resource.
119    if (message->type() == PpapiHostMsg_ResourceDestroyed::ID) {
120      base::MessageLoop::current()->PostTask(
121          FROM_HERE,
122          base::Bind(&PepperInProcessRouter::DispatchHostMsg,
123                     weak_factory_.GetWeakPtr(),
124                     base::Owned(message.release())));
125      return true;
126    } else {
127      bool result = host_impl_->GetPpapiHost()->OnMessageReceived(*message);
128      DCHECK(result) << "The message was not handled by the host.";
129      return true;
130    }
131  }
132
133  pending_message_id_ = IPC::SyncMessage::GetMessageId(*message);
134  reply_deserializer_.reset(
135      static_cast<IPC::SyncMessage*>(message.get())->GetReplyDeserializer());
136  reply_result_ = false;
137
138  bool result = host_impl_->GetPpapiHost()->OnMessageReceived(*message);
139  DCHECK(result) << "The message was not handled by the host.";
140
141  pending_message_id_ = 0;
142  reply_deserializer_.reset(NULL);
143  return reply_result_;
144}
145
146bool PepperInProcessRouter::SendToPlugin(IPC::Message* msg) {
147  scoped_ptr<IPC::Message> message(msg);
148  CHECK(!msg->is_sync());
149  if (IPC::SyncMessage::IsMessageReplyTo(*message, pending_message_id_)) {
150    if (!msg->is_reply_error())
151      reply_result_ = reply_deserializer_->SerializeOutputParameters(*message);
152  } else {
153    CHECK(!pending_message_id_);
154    // Dispatch plugin messages from the message loop.
155    base::MessageLoop::current()->PostTask(
156        FROM_HERE,
157        base::Bind(&PepperInProcessRouter::DispatchPluginMsg,
158                   weak_factory_.GetWeakPtr(),
159                   base::Owned(message.release())));
160  }
161  return true;
162}
163
164void PepperInProcessRouter::DispatchHostMsg(IPC::Message* msg) {
165  bool handled = host_impl_->GetPpapiHost()->OnMessageReceived(*msg);
166  DCHECK(handled);
167}
168
169void PepperInProcessRouter::DispatchPluginMsg(IPC::Message* msg) {
170  bool handled = OnPluginMsgReceived(*msg);
171  DCHECK(handled);
172}
173
174bool PepperInProcessRouter::SendToBrowser(IPC::Message *msg) {
175  return RenderThread::Get()->Send(msg);
176}
177
178}  // namespace content
179