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 "ppapi/proxy/ppp_messaging_proxy.h"
6
7#include <algorithm>
8
9#include "ppapi/c/ppp_messaging.h"
10#include "ppapi/proxy/host_dispatcher.h"
11#include "ppapi/proxy/message_handler.h"
12#include "ppapi/proxy/plugin_dispatcher.h"
13#include "ppapi/proxy/plugin_resource_tracker.h"
14#include "ppapi/proxy/plugin_var_tracker.h"
15#include "ppapi/proxy/ppapi_messages.h"
16#include "ppapi/proxy/serialized_var.h"
17#include "ppapi/shared_impl/ppapi_globals.h"
18#include "ppapi/shared_impl/proxy_lock.h"
19#include "ppapi/shared_impl/scoped_pp_var.h"
20#include "ppapi/shared_impl/var_tracker.h"
21
22namespace ppapi {
23namespace proxy {
24
25namespace {
26
27MessageHandler* GetMessageHandler(Dispatcher* dispatcher,
28                                  PP_Instance instance) {
29  if (!dispatcher || !dispatcher->IsPlugin()) {
30    NOTREACHED();
31    return NULL;
32  }
33  PluginDispatcher* plugin_dispatcher =
34      static_cast<PluginDispatcher*>(dispatcher);
35  InstanceData* instance_data = plugin_dispatcher->GetInstanceData(instance);
36  if (!instance_data)
37    return NULL;
38
39  return instance_data->message_handler.get();
40}
41
42void ResetMessageHandler(Dispatcher* dispatcher, PP_Instance instance) {
43  if (!dispatcher || !dispatcher->IsPlugin()) {
44    NOTREACHED();
45    return;
46  }
47  PluginDispatcher* plugin_dispatcher =
48      static_cast<PluginDispatcher*>(dispatcher);
49  InstanceData* instance_data = plugin_dispatcher->GetInstanceData(instance);
50  if (!instance_data)
51    return;
52
53  instance_data->message_handler.reset();
54}
55
56}  // namespace
57
58PPP_Messaging_Proxy::PPP_Messaging_Proxy(Dispatcher* dispatcher)
59    : InterfaceProxy(dispatcher),
60      ppp_messaging_impl_(NULL) {
61  if (dispatcher->IsPlugin()) {
62    ppp_messaging_impl_ = static_cast<const PPP_Messaging*>(
63        dispatcher->local_get_interface()(PPP_MESSAGING_INTERFACE));
64  }
65}
66
67PPP_Messaging_Proxy::~PPP_Messaging_Proxy() {
68}
69
70bool PPP_Messaging_Proxy::OnMessageReceived(const IPC::Message& msg) {
71  if (!dispatcher()->IsPlugin())
72    return false;
73
74  bool handled = true;
75  IPC_BEGIN_MESSAGE_MAP(PPP_Messaging_Proxy, msg)
76    IPC_MESSAGE_HANDLER(PpapiMsg_PPPMessaging_HandleMessage,
77                        OnMsgHandleMessage)
78    IPC_MESSAGE_HANDLER_DELAY_REPLY(
79        PpapiMsg_PPPMessageHandler_HandleBlockingMessage,
80        OnMsgHandleBlockingMessage)
81    IPC_MESSAGE_UNHANDLED(handled = false)
82  IPC_END_MESSAGE_MAP()
83  return handled;
84}
85
86void PPP_Messaging_Proxy::OnMsgHandleMessage(
87    PP_Instance instance, SerializedVarReceiveInput message_data) {
88  PP_Var received_var(message_data.GetForInstance(dispatcher(), instance));
89  MessageHandler* message_handler = GetMessageHandler(dispatcher(), instance);
90  if (message_handler) {
91    if (message_handler->LoopIsValid()) {
92      message_handler->HandleMessage(ScopedPPVar(received_var));
93      return;
94    } else {
95      // If the MessageHandler's loop has been quit, then we should treat it as
96      // though it has been unregistered and start sending messages to the
97      // default handler. This might mean the plugin has lost messages, but
98      // there's not really anything sane we can do about it. They should have
99      // used UnregisterMessageHandler.
100      ResetMessageHandler(dispatcher(), instance);
101    }
102  }
103  // If we reach this point, then there's no message handler registered, so
104  // we send to the default PPP_Messaging one for the instance.
105
106  // SerializedVarReceiveInput will decrement the reference count, but we want
107  // to give the recipient a reference in the legacy API.
108  PpapiGlobals::Get()->GetVarTracker()->AddRefVar(received_var);
109  CallWhileUnlocked(ppp_messaging_impl_->HandleMessage,
110                    instance,
111                    received_var);
112}
113
114void PPP_Messaging_Proxy::OnMsgHandleBlockingMessage(
115    PP_Instance instance,
116    SerializedVarReceiveInput message_data,
117    IPC::Message* reply_msg) {
118  ScopedPPVar received_var(message_data.GetForInstance(dispatcher(), instance));
119  MessageHandler* message_handler = GetMessageHandler(dispatcher(), instance);
120  if (message_handler) {
121    if (message_handler->LoopIsValid()) {
122      message_handler->HandleBlockingMessage(
123          received_var, scoped_ptr<IPC::Message>(reply_msg));
124      return;
125    } else {
126      // If the MessageHandler's loop has been quit, then we should treat it as
127      // though it has been unregistered. Also see the note for PostMessage.
128      ResetMessageHandler(dispatcher(), instance);
129    }
130  }
131  // We have no handler, but we still need to respond to unblock the renderer
132  // and inform the JavaScript caller.
133  PpapiMsg_PPPMessageHandler_HandleBlockingMessage::WriteReplyParams(
134      reply_msg,
135      SerializedVarReturnValue::Convert(dispatcher(), PP_MakeUndefined()),
136      false /* was_handled */);
137  dispatcher()->Send(reply_msg);
138}
139
140
141}  // namespace proxy
142}  // namespace ppapi
143