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();
32 void 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.
37 proxy_lock
->AssertAcquired();
40 EnterBase::EnterBase()
45 EnterBase::EnterBase(PP_Resource resource
)
46 : resource_(GetResource(resource
)),
50 EnterBase::EnterBase(PP_Instance instance
, SingletonResourceID resource_id
)
51 : resource_(GetSingletonResource(instance
, resource_id
)),
55 EnterBase::EnterBase(PP_Resource resource
,
56 const PP_CompletionCallback
& callback
)
57 : resource_(GetResource(resource
)),
59 callback_
= new TrackedCallback(resource_
, callback
);
62 EnterBase::EnterBase(PP_Instance instance
, SingletonResourceID resource_id
,
63 const PP_CompletionCallback
& callback
)
64 : resource_(GetSingletonResource(instance
, resource_id
)),
66 DCHECK(resource_
|| !instance
);
68 retval_
= PP_ERROR_BADARGUMENT
;
69 callback_
= new TrackedCallback(resource_
, callback
);
72 EnterBase::~EnterBase() {
73 // callback_ is cleared any time it is run, scheduled to be run, or once we
74 // know it will be completed asynchronously. So by this point it should be
76 DCHECK(!callback_
.get())
77 << "|callback_| is not NULL. Did you forget to call "
78 "|EnterBase::SetResult| in the interface's thunk?";
81 int32_t EnterBase::SetResult(int32_t result
) {
82 if (!callback_
.get()) {
83 // It doesn't make sense to call SetResult if there is no callback.
88 if (result
== PP_OK_COMPLETIONPENDING
) {
90 if (callback_
->is_blocking()) {
91 DCHECK(!IsMainThread()); // We should have returned an error before this.
92 retval_
= callback_
->BlockUntilComplete();
94 // The callback is not blocking and the operation will complete
95 // asynchronously, so there's nothing to do.
99 // The function completed synchronously.
100 if (callback_
->is_required()) {
101 // This is a required callback, so we must issue it asynchronously.
102 callback_
->PostRun(result
);
103 retval_
= PP_OK_COMPLETIONPENDING
;
105 // The callback is blocking or optional, so all we need to do is mark
106 // the callback as completed so that it won't be issued later.
107 callback_
->MarkAsCompleted();
116 Resource
* EnterBase::GetResource(PP_Resource resource
) {
117 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource
);
121 Resource
* EnterBase::GetSingletonResource(PP_Instance instance
,
122 SingletonResourceID resource_id
) {
123 PPB_Instance_API
* ppb_instance
=
124 PpapiGlobals::Get()->GetInstanceAPI(instance
);
128 return ppb_instance
->GetSingletonResource(instance
, resource_id
);
131 void EnterBase::SetStateForCallbackError(bool report_error
) {
132 if (PpapiGlobals::Get()->IsHostGlobals()) {
133 // In-process plugins can't make PPAPI calls off the main thread.
134 CHECK(IsMainThread());
136 if (callback_
.get()) {
137 if (callback_
->is_blocking() && IsMainThread()) {
138 // Blocking callbacks are never allowed on the main thread.
139 callback_
->MarkAsCompleted();
141 retval_
= PP_ERROR_BLOCKS_MAIN_THREAD
;
144 "Blocking callbacks are not allowed on the main thread.");
145 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR
,
146 std::string(), message
);
148 } else if (!IsMainThread() &&
149 callback_
->has_null_target_loop() &&
150 !callback_
->is_blocking()) {
151 // On a non-main thread, there must be a valid target loop for non-
152 // blocking callbacks, or we will have no place to run them.
154 // If the callback is required, there's no nice way to tell the plugin.
155 // We can't run their callback asynchronously without a message loop, and
156 // the plugin won't expect any return code other than
157 // PP_OK_COMPLETIONPENDING. So we crash to make the problem more obvious.
158 if (callback_
->is_required()) {
159 std::string
message("Attempted to use a required callback, but there "
160 "is no attached message loop on which to run the "
162 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR
,
163 std::string(), message
);
164 LOG(FATAL
) << message
;
167 callback_
->MarkAsCompleted();
169 retval_
= PP_ERROR_NO_MESSAGE_LOOP
;
172 "The calling thread must have a message loop attached.");
173 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR
,
174 std::string(), message
);
180 void EnterBase::ClearCallback() {
184 void EnterBase::SetStateForResourceError(PP_Resource pp_resource
,
185 Resource
* resource_base
,
188 // Check for callback errors. If we get any, SetStateForCallbackError will
189 // emit a log message. But we also want to check for resource errors. If there
190 // are both kinds of errors, we'll emit two log messages and return
191 // PP_ERROR_BADRESOURCE.
192 SetStateForCallbackError(report_error
);
195 return; // Everything worked.
197 if (callback_
.get() && callback_
->is_required()) {
198 callback_
->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE
));
200 retval_
= PP_OK_COMPLETIONPENDING
;
203 callback_
->MarkAsCompleted();
205 retval_
= PP_ERROR_BADRESOURCE
;
208 // We choose to silently ignore the error when the pp_resource is null
209 // because this is a pretty common case and we don't want to have lots
210 // of errors in the log. This should be an obvious case to debug.
211 if (report_error
&& pp_resource
) {
214 message
= base::StringPrintf(
215 "0x%X is not the correct type for this function.",
218 message
= base::StringPrintf(
219 "0x%X is not a valid resource ID.",
222 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR
,
223 std::string(), message
);
227 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance
,
230 // Check for callback errors. If we get any, SetStateForCallbackError will
231 // emit a log message. But we also want to check for instance errors. If there
232 // are both kinds of errors, we'll emit two log messages and return
233 // PP_ERROR_BADARGUMENT.
234 SetStateForCallbackError(report_error
);
237 return; // Everything worked.
239 if (callback_
.get() && callback_
->is_required()) {
240 callback_
->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT
));
242 retval_
= PP_OK_COMPLETIONPENDING
;
245 callback_
->MarkAsCompleted();
247 retval_
= PP_ERROR_BADARGUMENT
;
250 // We choose to silently ignore the error when the pp_instance is null as
251 // for PP_Resources above.
252 if (report_error
&& pp_instance
) {
254 message
= base::StringPrintf(
255 "0x%X is not a valid instance ID.",
257 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR
,
258 std::string(), message
);
262 } // namespace subtle
264 EnterInstance::EnterInstance(PP_Instance instance
)
266 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance
)) {
267 SetStateForFunctionError(instance
, functions_
, true);
270 EnterInstance::EnterInstance(PP_Instance instance
,
271 const PP_CompletionCallback
& callback
)
272 : EnterBase(0 /* resource */, callback
),
273 // TODO(dmichael): This means that the callback_ we get is not associated
274 // even with the instance, but we should handle that for
275 // MouseLock (maybe others?).
276 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance
)) {
277 SetStateForFunctionError(instance
, functions_
, true);
280 EnterInstance::~EnterInstance() {
283 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance
)
285 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance
)) {
286 SetStateForFunctionError(instance
, functions_
, true);
289 EnterInstanceNoLock::EnterInstanceNoLock(
290 PP_Instance instance
,
291 const PP_CompletionCallback
& callback
)
292 : EnterBase(0 /* resource */, callback
),
293 // TODO(dmichael): This means that the callback_ we get is not associated
294 // even with the instance, but we should handle that for
295 // MouseLock (maybe others?).
296 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance
)) {
297 SetStateForFunctionError(instance
, functions_
, true);
300 EnterInstanceNoLock::~EnterInstanceNoLock() {
303 EnterResourceCreation::EnterResourceCreation(PP_Instance instance
)
305 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance
)) {
306 SetStateForFunctionError(instance
, functions_
, true);
309 EnterResourceCreation::~EnterResourceCreation() {
312 EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance
)
314 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance
)) {
315 SetStateForFunctionError(instance
, functions_
, true);
318 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() {