SW: Move TRACE_EVENT_ASYNC_END to avoid early return and fix typo
[chromium-blink-merge.git] / ppapi / thunk / enter.cc
bloba98a3ec3102e07130639f8f26f6d22c4467b59d4
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.
5 #include "ppapi/thunk/enter.h"
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/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"
18 namespace ppapi {
19 namespace {
21 bool IsMainThread() {
22 return
23 PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread();
26 bool CurrentThreadHandlingBlockingMessage() {
27 ppapi::MessageLoopShared* current =
28 PpapiGlobals::Get()->GetCurrentMessageLoop();
29 return current && current->CurrentlyHandlingBlockingMessage();
32 } // namespace
34 namespace thunk {
36 namespace subtle {
38 EnterBase::EnterBase()
39 : resource_(NULL),
40 retval_(PP_OK) {
41 PpapiGlobals::Get()->MarkPluginIsActive();
44 EnterBase::EnterBase(PP_Resource resource)
45 : resource_(GetResource(resource)),
46 retval_(PP_OK) {
47 PpapiGlobals::Get()->MarkPluginIsActive();
50 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id)
51 : resource_(GetSingletonResource(instance, resource_id)),
52 retval_(PP_OK) {
53 PpapiGlobals::Get()->MarkPluginIsActive();
56 EnterBase::EnterBase(PP_Resource resource,
57 const PP_CompletionCallback& callback)
58 : resource_(GetResource(resource)),
59 retval_(PP_OK) {
60 callback_ = new TrackedCallback(resource_, callback);
61 PpapiGlobals::Get()->MarkPluginIsActive();
64 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id,
65 const PP_CompletionCallback& callback)
66 : resource_(GetSingletonResource(instance, resource_id)),
67 retval_(PP_OK) {
68 if (!resource_)
69 retval_ = PP_ERROR_BADARGUMENT;
70 callback_ = new TrackedCallback(resource_, callback);
71 PpapiGlobals::Get()->MarkPluginIsActive();
74 EnterBase::~EnterBase() {
75 // callback_ is cleared any time it is run, scheduled to be run, or once we
76 // know it will be completed asynchronously. So by this point it should be
77 // NULL.
78 DCHECK(!callback_.get())
79 << "|callback_| is not NULL. Did you forget to call "
80 "|EnterBase::SetResult| in the interface's thunk?";
83 int32_t EnterBase::SetResult(int32_t result) {
84 if (!callback_.get()) {
85 // It doesn't make sense to call SetResult if there is no callback.
86 NOTREACHED();
87 retval_ = result;
88 return result;
90 if (result == PP_OK_COMPLETIONPENDING) {
91 retval_ = result;
92 if (callback_->is_blocking()) {
93 DCHECK(!IsMainThread()); // We should have returned an error before this.
94 retval_ = callback_->BlockUntilComplete();
95 } else {
96 // The callback is not blocking and the operation will complete
97 // asynchronously, so there's nothing to do.
98 retval_ = result;
100 } else {
101 // The function completed synchronously.
102 if (callback_->is_required()) {
103 // This is a required callback, so we must issue it asynchronously.
104 callback_->PostRun(result);
105 retval_ = PP_OK_COMPLETIONPENDING;
106 } else {
107 // The callback is blocking or optional, so all we need to do is mark
108 // the callback as completed so that it won't be issued later.
109 callback_->MarkAsCompleted();
110 retval_ = result;
113 callback_ = NULL;
114 return retval_;
117 // static
118 Resource* EnterBase::GetResource(PP_Resource resource) {
119 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource);
122 // static
123 Resource* EnterBase::GetSingletonResource(PP_Instance instance,
124 SingletonResourceID resource_id) {
125 PPB_Instance_API* ppb_instance =
126 PpapiGlobals::Get()->GetInstanceAPI(instance);
127 if (!ppb_instance)
128 return NULL;
130 return ppb_instance->GetSingletonResource(instance, resource_id);
133 void EnterBase::SetStateForCallbackError(bool report_error) {
134 if (PpapiGlobals::Get()->IsHostGlobals()) {
135 // In-process plugins can't make PPAPI calls off the main thread.
136 CHECK(IsMainThread());
138 if (callback_.get()) {
139 if (callback_->is_blocking() && IsMainThread()) {
140 // Blocking callbacks are never allowed on the main thread.
141 callback_->MarkAsCompleted();
142 callback_ = NULL;
143 retval_ = PP_ERROR_BLOCKS_MAIN_THREAD;
144 if (report_error) {
145 std::string message(
146 "Blocking callbacks are not allowed on the main thread.");
147 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
148 std::string(), message);
150 } else if (callback_->is_blocking() &&
151 CurrentThreadHandlingBlockingMessage()) {
152 // Blocking callbacks are not allowed while handling a blocking message.
153 callback_->MarkAsCompleted();
154 callback_ = NULL;
155 retval_ = PP_ERROR_WOULD_BLOCK_THREAD;
156 if (report_error) {
157 std::string message("Blocking callbacks are not allowed while handling "
158 "a blocking message from JavaScript.");
159 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
160 std::string(), message);
162 } else if (!IsMainThread() &&
163 callback_->has_null_target_loop() &&
164 !callback_->is_blocking()) {
165 // On a non-main thread, there must be a valid target loop for non-
166 // blocking callbacks, or we will have no place to run them.
168 // If the callback is required, there's no nice way to tell the plugin.
169 // We can't run their callback asynchronously without a message loop, and
170 // the plugin won't expect any return code other than
171 // PP_OK_COMPLETIONPENDING. So we crash to make the problem more obvious.
172 if (callback_->is_required()) {
173 std::string message("Attempted to use a required callback, but there "
174 "is no attached message loop on which to run the "
175 "callback.");
176 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
177 std::string(), message);
178 LOG(FATAL) << message;
181 callback_->MarkAsCompleted();
182 callback_ = NULL;
183 retval_ = PP_ERROR_NO_MESSAGE_LOOP;
184 if (report_error) {
185 std::string message(
186 "The calling thread must have a message loop attached.");
187 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
188 std::string(), message);
194 void EnterBase::ClearCallback() {
195 callback_ = NULL;
198 void EnterBase::SetStateForResourceError(PP_Resource pp_resource,
199 Resource* resource_base,
200 void* object,
201 bool report_error) {
202 // Check for callback errors. If we get any, SetStateForCallbackError will
203 // emit a log message. But we also want to check for resource errors. If there
204 // are both kinds of errors, we'll emit two log messages and return
205 // PP_ERROR_BADRESOURCE.
206 SetStateForCallbackError(report_error);
208 if (object)
209 return; // Everything worked.
211 if (callback_.get() && callback_->is_required()) {
212 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE));
213 callback_ = NULL;
214 retval_ = PP_OK_COMPLETIONPENDING;
215 } else {
216 if (callback_.get())
217 callback_->MarkAsCompleted();
218 callback_ = NULL;
219 retval_ = PP_ERROR_BADRESOURCE;
222 // We choose to silently ignore the error when the pp_resource is null
223 // because this is a pretty common case and we don't want to have lots
224 // of errors in the log. This should be an obvious case to debug.
225 if (report_error && pp_resource) {
226 std::string message;
227 if (resource_base) {
228 message = base::StringPrintf(
229 "0x%X is not the correct type for this function.",
230 pp_resource);
231 } else {
232 message = base::StringPrintf(
233 "0x%X is not a valid resource ID.",
234 pp_resource);
236 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
237 std::string(), message);
241 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance,
242 void* object,
243 bool report_error) {
244 // Check for callback errors. If we get any, SetStateForCallbackError will
245 // emit a log message. But we also want to check for instance errors. If there
246 // are both kinds of errors, we'll emit two log messages and return
247 // PP_ERROR_BADARGUMENT.
248 SetStateForCallbackError(report_error);
250 if (object)
251 return; // Everything worked.
253 if (callback_.get() && callback_->is_required()) {
254 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT));
255 callback_ = NULL;
256 retval_ = PP_OK_COMPLETIONPENDING;
257 } else {
258 if (callback_.get())
259 callback_->MarkAsCompleted();
260 callback_ = NULL;
261 retval_ = PP_ERROR_BADARGUMENT;
264 // We choose to silently ignore the error when the pp_instance is null as
265 // for PP_Resources above.
266 if (report_error && pp_instance) {
267 std::string message;
268 message = base::StringPrintf(
269 "0x%X is not a valid instance ID.",
270 pp_instance);
271 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
272 std::string(), message);
276 } // namespace subtle
278 EnterInstance::EnterInstance(PP_Instance instance)
279 : EnterBase(),
280 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
281 SetStateForFunctionError(instance, functions_, true);
284 EnterInstance::EnterInstance(PP_Instance instance,
285 const PP_CompletionCallback& callback)
286 : EnterBase(0 /* resource */, callback),
287 // TODO(dmichael): This means that the callback_ we get is not associated
288 // even with the instance, but we should handle that for
289 // MouseLock (maybe others?).
290 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
291 SetStateForFunctionError(instance, functions_, true);
294 EnterInstance::~EnterInstance() {
297 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance)
298 : EnterBase(),
299 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
300 SetStateForFunctionError(instance, functions_, true);
303 EnterInstanceNoLock::EnterInstanceNoLock(
304 PP_Instance instance,
305 const PP_CompletionCallback& callback)
306 : EnterBase(0 /* resource */, callback),
307 // TODO(dmichael): This means that the callback_ we get is not associated
308 // even with the instance, but we should handle that for
309 // MouseLock (maybe others?).
310 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
311 SetStateForFunctionError(instance, functions_, true);
314 EnterInstanceNoLock::~EnterInstanceNoLock() {
317 EnterResourceCreation::EnterResourceCreation(PP_Instance instance)
318 : EnterBase(),
319 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
320 SetStateForFunctionError(instance, functions_, true);
323 EnterResourceCreation::~EnterResourceCreation() {
326 EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance)
327 : EnterBase(),
328 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
329 SetStateForFunctionError(instance, functions_, true);
332 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() {
335 } // namespace thunk
336 } // namespace ppapi