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"
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"
23 PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread();
26 bool CurrentThreadHandlingBlockingMessage() {
27 ppapi::MessageLoopShared
* current
=
28 PpapiGlobals::Get()->GetCurrentMessageLoop();
29 return current
&& current
->CurrentlyHandlingBlockingMessage();
38 EnterBase::EnterBase()
41 PpapiGlobals::Get()->MarkPluginIsActive();
44 EnterBase::EnterBase(PP_Resource resource
)
45 : resource_(GetResource(resource
)),
47 PpapiGlobals::Get()->MarkPluginIsActive();
50 EnterBase::EnterBase(PP_Instance instance
, SingletonResourceID resource_id
)
51 : resource_(GetSingletonResource(instance
, resource_id
)),
53 PpapiGlobals::Get()->MarkPluginIsActive();
56 EnterBase::EnterBase(PP_Resource resource
,
57 const PP_CompletionCallback
& callback
)
58 : resource_(GetResource(resource
)),
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
)),
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
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.
90 if (result
== PP_OK_COMPLETIONPENDING
) {
92 if (callback_
->is_blocking()) {
93 DCHECK(!IsMainThread()); // We should have returned an error before this.
94 retval_
= callback_
->BlockUntilComplete();
96 // The callback is not blocking and the operation will complete
97 // asynchronously, so there's nothing to do.
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
;
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();
118 Resource
* EnterBase::GetResource(PP_Resource resource
) {
119 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource
);
123 Resource
* EnterBase::GetSingletonResource(PP_Instance instance
,
124 SingletonResourceID resource_id
) {
125 PPB_Instance_API
* ppb_instance
=
126 PpapiGlobals::Get()->GetInstanceAPI(instance
);
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();
143 retval_
= PP_ERROR_BLOCKS_MAIN_THREAD
;
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();
155 retval_
= PP_ERROR_WOULD_BLOCK_THREAD
;
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 "
176 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR
,
177 std::string(), message
);
178 LOG(FATAL
) << message
;
181 callback_
->MarkAsCompleted();
183 retval_
= PP_ERROR_NO_MESSAGE_LOOP
;
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() {
198 void EnterBase::SetStateForResourceError(PP_Resource pp_resource
,
199 Resource
* resource_base
,
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
);
209 return; // Everything worked.
211 if (callback_
.get() && callback_
->is_required()) {
212 callback_
->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE
));
214 retval_
= PP_OK_COMPLETIONPENDING
;
217 callback_
->MarkAsCompleted();
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
) {
228 message
= base::StringPrintf(
229 "0x%X is not the correct type for this function.",
232 message
= base::StringPrintf(
233 "0x%X is not a valid resource ID.",
236 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR
,
237 std::string(), message
);
241 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance
,
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
);
251 return; // Everything worked.
253 if (callback_
.get() && callback_
->is_required()) {
254 callback_
->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT
));
256 retval_
= PP_OK_COMPLETIONPENDING
;
259 callback_
->MarkAsCompleted();
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
) {
268 message
= base::StringPrintf(
269 "0x%X is not a valid instance ID.",
271 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR
,
272 std::string(), message
);
276 } // namespace subtle
278 EnterInstance::EnterInstance(PP_Instance instance
)
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
)
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
)
319 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance
)) {
320 SetStateForFunctionError(instance
, functions_
, true);
323 EnterResourceCreation::~EnterResourceCreation() {
326 EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance
)
328 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance
)) {
329 SetStateForFunctionError(instance
, functions_
, true);
332 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() {