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_ENTER_PROXY_H_
6#define PPAPI_PROXY_ENTER_PROXY_H_
7
8#include "base/logging.h"
9#include "ppapi/cpp/completion_callback.h"
10#include "ppapi/proxy/host_dispatcher.h"
11#include "ppapi/proxy/plugin_dispatcher.h"
12#include "ppapi/proxy/plugin_globals.h"
13#include "ppapi/proxy/plugin_resource_tracker.h"
14#include "ppapi/thunk/enter.h"
15
16namespace ppapi {
17
18namespace proxy {
19
20// Wrapper around EnterResourceNoLock that takes a host resource. This is used
21// when handling messages in the plugin from the host and we need to convert to
22// an object in the plugin side corresponding to that.
23//
24// This never locks since we assume the host Resource is coming from IPC, and
25// never logs errors since we assume the host is doing reasonable things.
26template<typename ResourceT>
27class EnterPluginFromHostResource
28    : public thunk::EnterResourceNoLock<ResourceT> {
29 public:
30  explicit EnterPluginFromHostResource(const HostResource& host_resource)
31      : thunk::EnterResourceNoLock<ResourceT>(
32            PluginGlobals::Get()->plugin_resource_tracker()->
33                PluginResourceForHostResource(host_resource),
34            false) {
35    // Validate that we're in the plugin rather than the host. Otherwise this
36    // object will do the wrong thing. In the plugin, the instance should have
37    // a corresponding plugin dispatcher (assuming the resource is valid).
38    DCHECK(this->failed() ||
39           PluginDispatcher::GetForInstance(host_resource.instance()));
40  }
41};
42
43template<typename ResourceT>
44class EnterHostFromHostResource
45    : public thunk::EnterResourceNoLock<ResourceT> {
46 public:
47  explicit EnterHostFromHostResource(const HostResource& host_resource)
48      : thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
49                                              false) {
50    // Validate that we're in the host rather than the plugin. Otherwise this
51    // object will do the wrong thing. In the host, the instance should have
52    // a corresponding host disptacher (assuming the resource is valid).
53    DCHECK(this->failed() ||
54           HostDispatcher::GetForInstance(host_resource.instance()));
55  }
56
57  EnterHostFromHostResource(const HostResource& host_resource,
58                            const pp::CompletionCallback& callback)
59      : thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
60                                              callback.pp_completion_callback(),
61                                              false) {
62    // Validate that we're in the host rather than the plugin. Otherwise this
63    // object will do the wrong thing. In the host, the instance should have
64    // a corresponding host disptacher (assuming the resource is valid).
65    DCHECK(this->failed() ||
66           HostDispatcher::GetForInstance(host_resource.instance()));
67  }
68};
69
70// Enters a resource and forces a completion callback to be issued.
71//
72// This is used when implementing the host (renderer) side of a resource
73// function that issues a completion callback. In all cases, we need to issue
74// the callback to avoid hanging the plugin.
75//
76// This class automatically constructs a callback with the given factory
77// calling the given method. The method will generally be the one that sends
78// the message to trigger the completion callback in the plugin process.
79//
80// It will automatically issue the callback with PP_ERROR_NOINTERFACE if the
81// host resource is invalid (i.e. failed() is set). In all other cases you
82// should call SetResult(), which will issue the callback immediately if the
83// result value isn't PP_OK_COMPLETIONPENDING. In the "completion pending"
84// case, it's assumed the function the proxy is calling will take responsibility
85// of executing the callback (returned by callback()).
86//
87// Example:
88//   EnterHostFromHostResourceForceCallback<PPB_Foo_API> enter(
89//       resource, callback_factory_, &MyClass::SendResult, resource);
90//   if (enter.failed())
91//     return;  // SendResult automatically called with PP_ERROR_BADRESOURCE.
92//   enter.SetResult(enter.object()->DoFoo(enter.callback()));
93//
94// Where DoFoo's signature looks like this:
95//   int32_t DoFoo(PP_CompletionCallback callback);
96// And SendResult's implementation looks like this:
97//   void MyClass::SendResult(int32_t result, const HostResource& res) {
98//     Send(new FooMsg_FooComplete(..., result, res));
99//   }
100template<typename ResourceT>
101class EnterHostFromHostResourceForceCallback
102    : public EnterHostFromHostResource<ResourceT> {
103 public:
104  EnterHostFromHostResourceForceCallback(
105      const HostResource& host_resource,
106      const pp::CompletionCallback& callback)
107      : EnterHostFromHostResource<ResourceT>(host_resource, callback),
108        needs_running_(true) {
109  }
110
111  // For callbacks that take no parameters except the "int32_t result". Most
112  // implementations will use the 1-extra-argument constructor below.
113  template<class CallbackFactory, typename Method>
114  EnterHostFromHostResourceForceCallback(
115      const HostResource& host_resource,
116      CallbackFactory& factory,
117      Method method)
118      : EnterHostFromHostResource<ResourceT>(host_resource,
119            factory.NewOptionalCallback(method)),
120        needs_running_(true) {
121    if (this->failed())
122      RunCallback(PP_ERROR_BADRESOURCE);
123  }
124
125  // For callbacks that take an extra parameter as a closure.
126  template<class CallbackFactory, typename Method, typename A>
127  EnterHostFromHostResourceForceCallback(
128      const HostResource& host_resource,
129      CallbackFactory& factory,
130      Method method,
131      const A& a)
132      : EnterHostFromHostResource<ResourceT>(host_resource,
133            factory.NewOptionalCallback(method, a)),
134        needs_running_(true) {
135    if (this->failed())
136      RunCallback(PP_ERROR_BADRESOURCE);
137  }
138
139  // For callbacks that take two extra parameters as a closure.
140  template<class CallbackFactory, typename Method, typename A, typename B>
141  EnterHostFromHostResourceForceCallback(
142      const HostResource& host_resource,
143      CallbackFactory& factory,
144      Method method,
145      const A& a,
146      const B& b)
147      : EnterHostFromHostResource<ResourceT>(host_resource,
148            factory.NewOptionalCallback(method, a, b)),
149        needs_running_(true) {
150    if (this->failed())
151      RunCallback(PP_ERROR_BADRESOURCE);
152  }
153
154  // For callbacks that take three extra parameters as a closure.
155  template<class CallbackFactory, typename Method, typename A, typename B,
156           typename C>
157  EnterHostFromHostResourceForceCallback(
158      const HostResource& host_resource,
159      CallbackFactory& factory,
160      Method method,
161      const A& a,
162      const B& b,
163      const C& c)
164      : EnterHostFromHostResource<ResourceT>(host_resource,
165            factory.NewOptionalCallback(method, a, b, c)),
166        needs_running_(true) {
167    if (this->failed())
168      RunCallback(PP_ERROR_BADRESOURCE);
169  }
170
171  ~EnterHostFromHostResourceForceCallback() {
172    if (needs_running_) {
173      NOTREACHED() << "Should always call SetResult except in the "
174                      "initialization failed case.";
175      RunCallback(PP_ERROR_FAILED);
176    }
177  }
178
179  void SetResult(int32_t result) {
180    DCHECK(needs_running_) << "Don't call SetResult when there already is one.";
181    if (result != PP_OK_COMPLETIONPENDING)
182      RunCallback(result);
183    needs_running_ = false;
184    // Either we already ran the callback, or it will be run asynchronously. We
185    // clear the callback so it isn't accidentally run again (and because
186    // EnterBase checks that the callback has been cleared).
187    this->ClearCallback();
188  }
189
190 private:
191  void RunCallback(int32_t result) {
192    DCHECK(needs_running_);
193    needs_running_ = false;
194    this->callback()->Run(result);
195    this->ClearCallback();
196  }
197
198  bool needs_running_;
199};
200
201}  // namespace proxy
202}  // namespace ppapi
203
204#endif  // PPAPI_PROXY_ENTER_PROXY_H_
205