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#ifndef PPAPI_PROXY_PLUGIN_RESOURCE_H_
6#define PPAPI_PROXY_PLUGIN_RESOURCE_H_
7
8#include <map>
9
10#include "base/basictypes.h"
11#include "base/compiler_specific.h"
12#include "base/memory/ref_counted.h"
13#include "ipc/ipc_message.h"
14#include "ipc/ipc_sender.h"
15#include "ppapi/c/pp_errors.h"
16#include "ppapi/proxy/connection.h"
17#include "ppapi/proxy/plugin_resource_callback.h"
18#include "ppapi/proxy/ppapi_message_utils.h"
19#include "ppapi/proxy/ppapi_proxy_export.h"
20#include "ppapi/proxy/resource_message_params.h"
21#include "ppapi/proxy/resource_reply_thread_registrar.h"
22#include "ppapi/shared_impl/resource.h"
23#include "ppapi/shared_impl/tracked_callback.h"
24namespace ppapi {
25namespace proxy {
26
27class PluginDispatcher;
28
29class PPAPI_PROXY_EXPORT PluginResource : public Resource {
30 public:
31  enum Destination {
32    RENDERER = 0,
33    BROWSER = 1
34  };
35
36  PluginResource(Connection connection, PP_Instance instance);
37  virtual ~PluginResource();
38
39  // Returns true if we've previously sent a create message to the browser
40  // or renderer. Generally resources will use these to tell if they should
41  // lazily send create messages.
42  bool sent_create_to_browser() const { return sent_create_to_browser_; }
43  bool sent_create_to_renderer() const { return sent_create_to_renderer_; }
44
45  // This handles a reply to a resource call. It works by looking up the
46  // callback that was registered when CallBrowser/CallRenderer was called
47  // and calling it with |params| and |msg|.
48  virtual void OnReplyReceived(const proxy::ResourceMessageReplyParams& params,
49                               const IPC::Message& msg) OVERRIDE;
50
51  // Resource overrides.
52  // Note: Subclasses shouldn't override these methods directly. Instead, they
53  // should implement LastPluginRefWasDeleted() or InstanceWasDeleted() to get
54  // notified.
55  virtual void NotifyLastPluginRefWasDeleted() OVERRIDE;
56  virtual void NotifyInstanceWasDeleted() OVERRIDE;
57
58
59  // Sends a create message to the browser or renderer for the current resource.
60  void SendCreate(Destination dest, const IPC::Message& msg);
61
62  // When the host returnes a resource to the plugin, it will create a pending
63  // ResourceHost and send an ID back to the plugin that identifies the pending
64  // object. The plugin uses this function to connect the plugin resource with
65  // the pending host resource. See also PpapiHostMsg_AttachToPendingHost. This
66  // is in lieu of sending a create message.
67  void AttachToPendingHost(Destination dest, int pending_host_id);
68
69  // Sends the given IPC message as a resource request to the host
70  // corresponding to this resource object and does not expect a reply.
71  void Post(Destination dest, const IPC::Message& msg);
72
73  // Like Post() but expects a response. |callback| is a |base::Callback| that
74  // will be run when a reply message with a sequence number matching that of
75  // the call is received. |ReplyMsgClass| is the type of the reply message that
76  // is expected. An example of usage:
77  //
78  // Call<PpapiPluginMsg_MyResourceType_MyReplyMessage>(
79  //     BROWSER,
80  //     PpapiHostMsg_MyResourceType_MyRequestMessage(),
81  //     base::Bind(&MyPluginResource::ReplyHandler, base::Unretained(this)));
82  //
83  // If a reply message to this call is received whose type does not match
84  // |ReplyMsgClass| (for example, in the case of an error), the callback will
85  // still be invoked but with the default values of the message parameters.
86  //
87  // Returns the new request's sequence number which can be used to identify
88  // the callback. This value will never be 0, which you can use to identify
89  // an invalid callback.
90  //
91  // Note: 1) When all plugin references to this resource are gone or the
92  //          corresponding plugin instance is deleted, all pending callbacks
93  //          are abandoned.
94  //       2) It is *not* recommended to let |callback| hold any reference to
95  //          |this|, in which it will be stored. Otherwise, this object will
96  //          live forever if we fail to clean up the callback. It is safe to
97  //          use base::Unretained(this) or a weak pointer, because this object
98  //          will outlive the callback.
99  template<typename ReplyMsgClass, typename CallbackType>
100  int32_t Call(Destination dest,
101               const IPC::Message& msg,
102               const CallbackType& callback);
103
104  // Comparing with the previous Call() method, this method takes
105  // |reply_thread_hint| as a hint to determine which thread to handle the reply
106  // message.
107  //
108  // If |reply_thread_hint| is non-blocking, the reply message will be handled
109  // on the target thread of the callback; otherwise, it will be handled on the
110  // main thread.
111  //
112  // If handling a reply message will cause a TrackedCallback to be run, it is
113  // recommended to use this version of Call(). It eliminates unnecessary
114  // thread switching and therefore has better performance.
115  template<typename ReplyMsgClass, typename CallbackType>
116  int32_t Call(Destination dest,
117               const IPC::Message& msg,
118               const CallbackType& callback,
119               scoped_refptr<TrackedCallback> reply_thread_hint);
120
121  // Calls the browser/renderer with sync messages. Returns the pepper error
122  // code from the call.
123  // |ReplyMsgClass| is the type of the reply message that is expected. If it
124  // carries x parameters, then the method with x out parameters should be used.
125  // An example of usage:
126  //
127  // // Assuming the reply message carries a string and an integer.
128  // std::string param_1;
129  // int param_2 = 0;
130  // int32_t result = SyncCall<PpapiPluginMsg_MyResourceType_MyReplyMessage>(
131  //     RENDERER, PpapiHostMsg_MyResourceType_MyRequestMessage(),
132  //     &param_1, &param_2);
133  template <class ReplyMsgClass>
134  int32_t SyncCall(Destination dest, const IPC::Message& msg);
135  template <class ReplyMsgClass, class A>
136  int32_t SyncCall(Destination dest, const IPC::Message& msg, A* a);
137  template <class ReplyMsgClass, class A, class B>
138  int32_t SyncCall(Destination dest, const IPC::Message& msg, A* a, B* b);
139  template <class ReplyMsgClass, class A, class B, class C>
140  int32_t SyncCall(Destination dest, const IPC::Message& msg, A* a, B* b, C* c);
141  template <class ReplyMsgClass, class A, class B, class C, class D>
142  int32_t SyncCall(
143      Destination dest, const IPC::Message& msg, A* a, B* b, C* c, D* d);
144  template <class ReplyMsgClass, class A, class B, class C, class D, class E>
145  int32_t SyncCall(
146      Destination dest, const IPC::Message& msg, A* a, B* b, C* c, D* d, E* e);
147
148  int32_t GenericSyncCall(Destination dest,
149                          const IPC::Message& msg,
150                          IPC::Message* reply_msg,
151                          ResourceMessageReplyParams* reply_params);
152
153  const Connection& connection() { return connection_; }
154
155 private:
156  IPC::Sender* GetSender(Destination dest) {
157    return dest == RENDERER ? connection_.renderer_sender :
158                              connection_.browser_sender;
159  }
160
161  // Helper function to send a |PpapiHostMsg_ResourceCall| to the given
162  // destination with |nested_msg| and |call_params|.
163  bool SendResourceCall(Destination dest,
164                        const ResourceMessageCallParams& call_params,
165                        const IPC::Message& nested_msg);
166
167  int32_t GetNextSequence();
168
169  Connection connection_;
170
171  // Use GetNextSequence to retrieve the next value.
172  int32_t next_sequence_number_;
173
174  bool sent_create_to_browser_;
175  bool sent_create_to_renderer_;
176
177  typedef std::map<int32_t, scoped_refptr<PluginResourceCallbackBase> >
178      CallbackMap;
179  CallbackMap callbacks_;
180
181  scoped_refptr<ResourceReplyThreadRegistrar> resource_reply_thread_registrar_;
182
183  DISALLOW_COPY_AND_ASSIGN(PluginResource);
184};
185
186template<typename ReplyMsgClass, typename CallbackType>
187int32_t PluginResource::Call(Destination dest,
188                             const IPC::Message& msg,
189                             const CallbackType& callback) {
190  return Call<ReplyMsgClass>(dest, msg, callback, NULL);
191}
192
193template<typename ReplyMsgClass, typename CallbackType>
194int32_t PluginResource::Call(
195    Destination dest,
196    const IPC::Message& msg,
197    const CallbackType& callback,
198    scoped_refptr<TrackedCallback> reply_thread_hint) {
199  TRACE_EVENT2("ppapi proxy", "PluginResource::Call",
200               "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
201               "Line", IPC_MESSAGE_ID_LINE(msg.type()));
202  ResourceMessageCallParams params(pp_resource(), next_sequence_number_++);
203  // Stash the |callback| in |callbacks_| identified by the sequence number of
204  // the call.
205  scoped_refptr<PluginResourceCallbackBase> plugin_callback(
206      new PluginResourceCallback<ReplyMsgClass, CallbackType>(callback));
207  callbacks_.insert(std::make_pair(params.sequence(), plugin_callback));
208  params.set_has_callback();
209
210  if (resource_reply_thread_registrar_.get()) {
211    resource_reply_thread_registrar_->Register(
212        pp_resource(), params.sequence(), reply_thread_hint);
213  }
214  SendResourceCall(dest, params, msg);
215  return params.sequence();
216}
217
218template <class ReplyMsgClass>
219int32_t PluginResource::SyncCall(Destination dest, const IPC::Message& msg) {
220  IPC::Message reply;
221  ResourceMessageReplyParams reply_params;
222  return GenericSyncCall(dest, msg, &reply, &reply_params);
223}
224
225template <class ReplyMsgClass, class A>
226int32_t PluginResource::SyncCall(
227    Destination dest, const IPC::Message& msg, A* a) {
228  IPC::Message reply;
229  ResourceMessageReplyParams reply_params;
230  int32_t result = GenericSyncCall(dest, msg, &reply, &reply_params);
231
232  if (UnpackMessage<ReplyMsgClass>(reply, a))
233    return result;
234  return PP_ERROR_FAILED;
235}
236
237template <class ReplyMsgClass, class A, class B>
238int32_t PluginResource::SyncCall(
239    Destination dest, const IPC::Message& msg, A* a, B* b) {
240  IPC::Message reply;
241  ResourceMessageReplyParams reply_params;
242  int32_t result = GenericSyncCall(dest, msg, &reply, &reply_params);
243
244  if (UnpackMessage<ReplyMsgClass>(reply, a, b))
245    return result;
246  return PP_ERROR_FAILED;
247}
248
249template <class ReplyMsgClass, class A, class B, class C>
250int32_t PluginResource::SyncCall(
251    Destination dest, const IPC::Message& msg, A* a, B* b, C* c) {
252  IPC::Message reply;
253  ResourceMessageReplyParams reply_params;
254  int32_t result = GenericSyncCall(dest, msg, &reply, &reply_params);
255
256  if (UnpackMessage<ReplyMsgClass>(reply, a, b, c))
257    return result;
258  return PP_ERROR_FAILED;
259}
260
261template <class ReplyMsgClass, class A, class B, class C, class D>
262int32_t PluginResource::SyncCall(
263    Destination dest, const IPC::Message& msg, A* a, B* b, C* c, D* d) {
264  IPC::Message reply;
265  ResourceMessageReplyParams reply_params;
266  int32_t result = GenericSyncCall(dest, msg, &reply, &reply_params);
267
268  if (UnpackMessage<ReplyMsgClass>(reply, a, b, c, d))
269    return result;
270  return PP_ERROR_FAILED;
271}
272
273template <class ReplyMsgClass, class A, class B, class C, class D, class E>
274int32_t PluginResource::SyncCall(
275    Destination dest, const IPC::Message& msg, A* a, B* b, C* c, D* d, E* e) {
276  IPC::Message reply;
277  ResourceMessageReplyParams reply_params;
278  int32_t result = GenericSyncCall(dest, msg, &reply, &reply_params);
279
280  if (UnpackMessage<ReplyMsgClass>(reply, a, b, c, d, e))
281    return result;
282  return PP_ERROR_FAILED;
283}
284
285}  // namespace proxy
286}  // namespace ppapi
287
288#endif  // PPAPI_PROXY_PLUGIN_RESOURCE_H_
289