summaryrefslogtreecommitdiffstats
path: root/ppapi/proxy/enter_proxy.h
blob: 4ad67b136c39dff211f25f152d24170b2be8bb86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef PPAPI_PROXY_ENTER_PROXY_H_
#define PPAPI_PROXY_ENTER_PROXY_H_

#include "base/logging.h"
#include "ppapi/cpp/completion_callback.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/plugin_globals.h"
#include "ppapi/proxy/plugin_resource_tracker.h"
#include "ppapi/thunk/enter.h"

namespace ppapi {

namespace proxy {

// Wrapper around EnterResourceNoLock that takes a host resource. This is used
// when handling messages in the plugin from the host and we need to convert to
// an object in the plugin side corresponding to that.
//
// This never locks since we assume the host Resource is coming from IPC, and
// never logs errors since we assume the host is doing reasonable things.
template<typename ResourceT>
class EnterPluginFromHostResource
    : public thunk::EnterResourceNoLock<ResourceT> {
 public:
  explicit EnterPluginFromHostResource(const HostResource& host_resource)
      : thunk::EnterResourceNoLock<ResourceT>(
            PluginGlobals::Get()->plugin_resource_tracker()->
                PluginResourceForHostResource(host_resource),
            false) {
    // Validate that we're in the plugin rather than the host. Otherwise this
    // object will do the wrong thing. In the plugin, the instance should have
    // a corresponding plugin dispatcher (assuming the resource is valid).
    DCHECK(this->failed() ||
           PluginDispatcher::GetForInstance(host_resource.instance()));
  }
};

template<typename ResourceT>
class EnterHostFromHostResource
    : public thunk::EnterResourceNoLock<ResourceT> {
 public:
  explicit EnterHostFromHostResource(const HostResource& host_resource)
      : thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
                                              false) {
    // Validate that we're in the host rather than the plugin. Otherwise this
    // object will do the wrong thing. In the host, the instance should have
    // a corresponding host disptacher (assuming the resource is valid).
    DCHECK(this->failed() ||
           HostDispatcher::GetForInstance(host_resource.instance()));
  }

  EnterHostFromHostResource(const HostResource& host_resource,
                            const pp::CompletionCallback& callback)
      : thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
                                              callback.pp_completion_callback(),
                                              false) {
    // Validate that we're in the host rather than the plugin. Otherwise this
    // object will do the wrong thing. In the host, the instance should have
    // a corresponding host disptacher (assuming the resource is valid).
    DCHECK(this->failed() ||
           HostDispatcher::GetForInstance(host_resource.instance()));
  }
};

// Enters a resource and forces a completion callback to be issued.
//
// This is used when implementing the host (renderer) side of a resource
// function that issues a completion callback. In all cases, we need to issue
// the callback to avoid hanging the plugin.
//
// This class automatically constructs a callback with the given factory
// calling the given method. The method will generally be the one that sends
// the message to trigger the completion callback in the plugin process.
//
// It will automatically issue the callback with PP_ERROR_NOINTERFACE if the
// host resource is invalid (i.e. failed() is set). In all other cases you
// should call SetResult(), which will issue the callback immediately if the
// result value isn't PP_OK_COMPLETIONPENDING. In the "completion pending"
// case, it's assumed the function the proxy is calling will take responsibility
// of executing the callback (returned by callback()).
//
// Example:
//   EnterHostFromHostResourceForceCallback<PPB_Foo_API> enter(
//       resource, callback_factory_, &MyClass::SendResult, resource);
//   if (enter.failed())
//     return;  // SendResult automatically called with PP_ERROR_BADRESOURCE.
//   enter.SetResult(enter.object()->DoFoo(enter.callback()));
//
// Where DoFoo's signature looks like this:
//   int32_t DoFoo(PP_CompletionCallback callback);
// And SendResult's implementation looks like this:
//   void MyClass::SendResult(int32_t result, const HostResource& res) {
//     Send(new FooMsg_FooComplete(..., result, res));
//   }
template<typename ResourceT>
class EnterHostFromHostResourceForceCallback
    : public EnterHostFromHostResource<ResourceT> {
 public:
  EnterHostFromHostResourceForceCallback(
      const HostResource& host_resource,
      const pp::CompletionCallback& callback)
      : EnterHostFromHostResource<ResourceT>(host_resource, callback),
        needs_running_(true) {
  }

  // For callbacks that take no parameters except the "int32_t result". Most
  // implementations will use the 1-extra-argument constructor below.
  template<class CallbackFactory, typename Method>
  EnterHostFromHostResourceForceCallback(
      const HostResource& host_resource,
      CallbackFactory& factory,
      Method method)
      : EnterHostFromHostResource<ResourceT>(host_resource,
            factory.NewOptionalCallback(method)),
        needs_running_(true) {
    if (this->failed())
      RunCallback(PP_ERROR_BADRESOURCE);
  }

  // For callbacks that take an extra parameter as a closure.
  template<class CallbackFactory, typename Method, typename A>
  EnterHostFromHostResourceForceCallback(
      const HostResource& host_resource,
      CallbackFactory& factory,
      Method method,
      const A& a)
      : EnterHostFromHostResource<ResourceT>(host_resource,
            factory.NewOptionalCallback(method, a)),
        needs_running_(true) {
    if (this->failed())
      RunCallback(PP_ERROR_BADRESOURCE);
  }

  // For callbacks that take two extra parameters as a closure.
  template<class CallbackFactory, typename Method, typename A, typename B>
  EnterHostFromHostResourceForceCallback(
      const HostResource& host_resource,
      CallbackFactory& factory,
      Method method,
      const A& a,
      const B& b)
      : EnterHostFromHostResource<ResourceT>(host_resource,
            factory.NewOptionalCallback(method, a, b)),
        needs_running_(true) {
    if (this->failed())
      RunCallback(PP_ERROR_BADRESOURCE);
  }

  // For callbacks that take three extra parameters as a closure.
  template<class CallbackFactory, typename Method, typename A, typename B,
           typename C>
  EnterHostFromHostResourceForceCallback(
      const HostResource& host_resource,
      CallbackFactory& factory,
      Method method,
      const A& a,
      const B& b,
      const C& c)
      : EnterHostFromHostResource<ResourceT>(host_resource,
            factory.NewOptionalCallback(method, a, b, c)),
        needs_running_(true) {
    if (this->failed())
      RunCallback(PP_ERROR_BADRESOURCE);
  }

  ~EnterHostFromHostResourceForceCallback() {
    if (needs_running_) {
      NOTREACHED() << "Should always call SetResult except in the "
                      "initialization failed case.";
      RunCallback(PP_ERROR_FAILED);
    }
  }

  void SetResult(int32_t result) {
    DCHECK(needs_running_) << "Don't call SetResult when there already is one.";
    if (result != PP_OK_COMPLETIONPENDING)
      RunCallback(result);
    needs_running_ = false;
    // Either we already ran the callback, or it will be run asynchronously. We
    // clear the callback so it isn't accidentally run again (and because
    // EnterBase checks that the callback has been cleared).
    this->ClearCallback();
  }

 private:
  void RunCallback(int32_t result) {
    DCHECK(needs_running_);
    needs_running_ = false;
    this->callback()->Run(result);
    this->ClearCallback();
  }

  bool needs_running_;
};

}  // namespace proxy
}  // namespace ppapi

#endif  // PPAPI_PROXY_ENTER_PROXY_H_