enter.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/thunk/enter.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/message_loop.h"
10#include "base/stringprintf.h"
11#include "base/synchronization/lock.h"
12#include "ppapi/c/pp_errors.h"
13#include "ppapi/shared_impl/ppapi_globals.h"
14#include "ppapi/shared_impl/tracked_callback.h"
15#include "ppapi/thunk/ppb_instance_api.h"
16#include "ppapi/thunk/resource_creation_api.h"
17
18namespace ppapi {
19namespace {
20
21bool IsMainThread() {
22  return
23      PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread();
24}
25
26}  // namespace
27
28namespace thunk {
29
30namespace subtle {
31
32void AssertLockHeld() {
33  base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock();
34  // The lock is only valid in the plugin side of the proxy, so it only makes
35  // sense to assert there. Otherwise, silently succeed.
36  if (proxy_lock)
37    proxy_lock->AssertAcquired();
38}
39
40EnterBase::EnterBase()
41    : resource_(NULL),
42      retval_(PP_OK) {
43  // TODO(dmichael) validate that threads have an associated message loop.
44}
45
46EnterBase::EnterBase(PP_Resource resource)
47    : resource_(GetResource(resource)),
48      retval_(PP_OK) {
49  // TODO(dmichael) validate that threads have an associated message loop.
50}
51
52EnterBase::EnterBase(PP_Resource resource,
53                     const PP_CompletionCallback& callback)
54    : resource_(GetResource(resource)),
55      retval_(PP_OK) {
56  callback_ = new TrackedCallback(resource_, callback);
57
58  // TODO(dmichael) validate that threads have an associated message loop.
59}
60
61EnterBase::~EnterBase() {
62  // callback_ is cleared any time it is run, scheduled to be run, or once we
63  // know it will be completed asynchronously. So by this point it should be
64  // NULL.
65  DCHECK(!callback_) << "|callback_| is not NULL. Did you forget to call "
66      "|EnterBase::SetResult| in the interface's thunk?";
67}
68
69int32_t EnterBase::SetResult(int32_t result) {
70  if (!callback_) {
71    // It doesn't make sense to call SetResult if there is no callback.
72    NOTREACHED();
73    retval_ = result;
74    return result;
75  }
76  if (result == PP_OK_COMPLETIONPENDING) {
77    retval_ = result;
78    if (callback_->is_blocking()) {
79      DCHECK(!IsMainThread());  // We should have returned an error before this.
80      retval_ = callback_->BlockUntilComplete();
81    } else {
82      // The callback is not blocking and the operation will complete
83      // asynchronously, so there's nothing to do.
84      retval_ = result;
85    }
86  } else {
87    // The function completed synchronously.
88    if (callback_->is_required()) {
89      // This is a required callback, so we must issue it asynchronously.
90      callback_->PostRun(result);
91      retval_ = PP_OK_COMPLETIONPENDING;
92    } else {
93      // The callback is blocking or optional, so all we need to do is mark
94      // the callback as completed so that it won't be issued later.
95      callback_->MarkAsCompleted();
96      retval_ = result;
97    }
98  }
99  callback_ = NULL;
100  return retval_;
101}
102
103// static
104Resource* EnterBase::GetResource(PP_Resource resource) {
105  return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource);
106}
107
108void EnterBase::SetStateForCallbackError(bool report_error) {
109  if (PpapiGlobals::Get()->IsHostGlobals()) {
110    // In-process plugins can't make PPAPI calls off the main thread.
111    CHECK(IsMainThread());
112  }
113  if (callback_) {
114    if (callback_->is_blocking() && IsMainThread()) {
115      // Blocking callbacks are never allowed on the main thread.
116      callback_->MarkAsCompleted();
117      callback_ = NULL;
118      retval_ = PP_ERROR_BLOCKS_MAIN_THREAD;
119      if (report_error) {
120        std::string message(
121            "Blocking callbacks are not allowed on the main thread.");
122        PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
123                                                    std::string(), message);
124      }
125    } else if (!IsMainThread() &&
126               callback_->has_null_target_loop() &&
127               !callback_->is_blocking()) {
128      // On a non-main thread, there must be a valid target loop for non-
129      // blocking callbacks, or we will have no place to run them.
130
131      // If the callback is required, there's no nice way to tell the plugin.
132      // We can't run their callback asynchronously without a message loop, and
133      // the plugin won't expect any return code other than
134      // PP_OK_COMPLETIONPENDING. So we crash to make the problem more obvious.
135      if (callback_->is_required()) {
136        std::string message("Attempted to use a required callback, but there"
137                            "is no attached message loop on which to run the"
138                            "callback.");
139        PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
140                                                    std::string(), message);
141        LOG(FATAL) << message;
142      }
143
144      callback_->MarkAsCompleted();
145      callback_ = NULL;
146      retval_ = PP_ERROR_NO_MESSAGE_LOOP;
147      if (report_error) {
148        std::string message(
149            "The calling thread must have a message loop attached.");
150        PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
151                                                    std::string(), message);
152      }
153    }
154  }
155}
156
157void EnterBase::ClearCallback() {
158  callback_ = NULL;
159}
160
161void EnterBase::SetStateForResourceError(PP_Resource pp_resource,
162                                         Resource* resource_base,
163                                         void* object,
164                                         bool report_error) {
165  // Check for callback errors. If we get any, SetStateForCallbackError will
166  // emit a log message. But we also want to check for resource errors. If there
167  // are both kinds of errors, we'll emit two log messages and return
168  // PP_ERROR_BADRESOURCE.
169  SetStateForCallbackError(report_error);
170
171  if (object)
172    return;  // Everything worked.
173
174  if (callback_ && callback_->is_required()) {
175    callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE));
176    callback_ = NULL;
177    retval_ = PP_OK_COMPLETIONPENDING;
178  } else {
179    if (callback_)
180      callback_->MarkAsCompleted();
181    callback_ = NULL;
182    retval_ = PP_ERROR_BADRESOURCE;
183  }
184
185  // We choose to silently ignore the error when the pp_resource is null
186  // because this is a pretty common case and we don't want to have lots
187  // of errors in the log. This should be an obvious case to debug.
188  if (report_error && pp_resource) {
189    std::string message;
190    if (resource_base) {
191      message = base::StringPrintf(
192          "0x%X is not the correct type for this function.",
193          pp_resource);
194    } else {
195      message = base::StringPrintf(
196          "0x%X is not a valid resource ID.",
197          pp_resource);
198    }
199    PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
200                                                std::string(), message);
201  }
202}
203
204void EnterBase::SetStateForFunctionError(PP_Instance pp_instance,
205                                         void* object,
206                                         bool report_error) {
207  // Check for callback errors. If we get any, SetStateForCallbackError will
208  // emit a log message. But we also want to check for instance errors. If there
209  // are both kinds of errors, we'll emit two log messages and return
210  // PP_ERROR_BADARGUMENT.
211  SetStateForCallbackError(report_error);
212
213  if (object)
214    return;  // Everything worked.
215
216  if (callback_ && callback_->is_required()) {
217    callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT));
218    callback_ = NULL;
219    retval_ = PP_OK_COMPLETIONPENDING;
220  } else {
221    if (callback_)
222      callback_->MarkAsCompleted();
223    callback_ = NULL;
224    retval_ = PP_ERROR_BADARGUMENT;
225  }
226
227  // We choose to silently ignore the error when the pp_instance is null as
228  // for PP_Resources above.
229  if (report_error && pp_instance) {
230    std::string message;
231    message = base::StringPrintf(
232        "0x%X is not a valid instance ID.",
233        pp_instance);
234    PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
235                                                std::string(), message);
236  }
237}
238
239}  // namespace subtle
240
241EnterInstance::EnterInstance(PP_Instance instance)
242    : EnterBase(),
243      functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
244  SetStateForFunctionError(instance, functions_, true);
245}
246
247EnterInstance::EnterInstance(PP_Instance instance,
248                             const PP_CompletionCallback& callback)
249    : EnterBase(0 /* resource */, callback),
250      // TODO(dmichael): This means that the callback_ we get is not associated
251      //                 even with the instance, but we should handle that for
252      //                 MouseLock (maybe others?).
253      functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
254  SetStateForFunctionError(instance, functions_, true);
255}
256
257EnterInstance::~EnterInstance() {
258}
259
260EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance)
261    : EnterBase(),
262      functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
263  SetStateForFunctionError(instance, functions_, true);
264}
265
266EnterInstanceNoLock::EnterInstanceNoLock(
267    PP_Instance instance,
268    const PP_CompletionCallback& callback)
269    : EnterBase(0 /* resource */, callback),
270      // TODO(dmichael): This means that the callback_ we get is not associated
271      //                 even with the instance, but we should handle that for
272      //                 MouseLock (maybe others?).
273      functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
274  SetStateForFunctionError(instance, functions_, true);
275}
276
277EnterInstanceNoLock::~EnterInstanceNoLock() {
278}
279
280EnterResourceCreation::EnterResourceCreation(PP_Instance instance)
281    : EnterBase(),
282      functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
283  SetStateForFunctionError(instance, functions_, true);
284}
285
286EnterResourceCreation::~EnterResourceCreation() {
287}
288
289EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance)
290    : EnterBase(),
291      functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
292  SetStateForFunctionError(instance, functions_, true);
293}
294
295EnterResourceCreationNoLock::~EnterResourceCreationNoLock() {
296}
297
298}  // namespace thunk
299}  // namespace ppapi
300