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 EnterBase::EnterBase()
37 EnterBase::EnterBase(PP_Resource resource)
38 : resource_(GetResource(resource)),
42 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id)
43 : resource_(GetSingletonResource(instance, resource_id)),
47 EnterBase::EnterBase(PP_Resource resource,
48 const PP_CompletionCallback& callback)
49 : resource_(GetResource(resource)),
51 callback_ = new TrackedCallback(resource_, callback);
54 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id,
55 const PP_CompletionCallback& callback)
56 : resource_(GetSingletonResource(instance, resource_id)),
58 DCHECK(resource_ || !instance);
60 retval_ = PP_ERROR_BADARGUMENT;
61 callback_ = new TrackedCallback(resource_, callback);
64 EnterBase::~EnterBase() {
65 // callback_ is cleared any time it is run, scheduled to be run, or once we
66 // know it will be completed asynchronously. So by this point it should be
68 DCHECK(!callback_.get())
69 << "|callback_| is not NULL. Did you forget to call "
70 "|EnterBase::SetResult| in the interface's thunk?";
73 int32_t EnterBase::SetResult(int32_t result) {
74 if (!callback_.get()) {
75 // It doesn't make sense to call SetResult if there is no callback.
80 if (result == PP_OK_COMPLETIONPENDING) {
82 if (callback_->is_blocking()) {
83 DCHECK(!IsMainThread()); // We should have returned an error before this.
84 retval_ = callback_->BlockUntilComplete();
86 // The callback is not blocking and the operation will complete
87 // asynchronously, so there's nothing to do.
91 // The function completed synchronously.
92 if (callback_->is_required()) {
93 // This is a required callback, so we must issue it asynchronously.
94 callback_->PostRun(result);
95 retval_ = PP_OK_COMPLETIONPENDING;
97 // The callback is blocking or optional, so all we need to do is mark
98 // the callback as completed so that it won't be issued later.
99 callback_->MarkAsCompleted();
108 Resource* EnterBase::GetResource(PP_Resource resource) {
109 return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource);
113 Resource* EnterBase::GetSingletonResource(PP_Instance instance,
114 SingletonResourceID resource_id) {
115 PPB_Instance_API* ppb_instance =
116 PpapiGlobals::Get()->GetInstanceAPI(instance);
120 return ppb_instance->GetSingletonResource(instance, resource_id);
123 void EnterBase::SetStateForCallbackError(bool report_error) {
124 if (PpapiGlobals::Get()->IsHostGlobals()) {
125 // In-process plugins can't make PPAPI calls off the main thread.
126 CHECK(IsMainThread());
128 if (callback_.get()) {
129 if (callback_->is_blocking() && IsMainThread()) {
130 // Blocking callbacks are never allowed on the main thread.
131 callback_->MarkAsCompleted();
133 retval_ = PP_ERROR_BLOCKS_MAIN_THREAD;
136 "Blocking callbacks are not allowed on the main thread.");
137 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
138 std::string(), message);
140 } else if (!IsMainThread() &&
141 callback_->has_null_target_loop() &&
142 !callback_->is_blocking()) {
143 // On a non-main thread, there must be a valid target loop for non-
144 // blocking callbacks, or we will have no place to run them.
146 // If the callback is required, there's no nice way to tell the plugin.
147 // We can't run their callback asynchronously without a message loop, and
148 // the plugin won't expect any return code other than
149 // PP_OK_COMPLETIONPENDING. So we crash to make the problem more obvious.
150 if (callback_->is_required()) {
151 std::string message("Attempted to use a required callback, but there "
152 "is no attached message loop on which to run the "
154 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
155 std::string(), message);
156 LOG(FATAL) << message;
159 callback_->MarkAsCompleted();
161 retval_ = PP_ERROR_NO_MESSAGE_LOOP;
164 "The calling thread must have a message loop attached.");
165 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
166 std::string(), message);
172 void EnterBase::ClearCallback() {
176 void EnterBase::SetStateForResourceError(PP_Resource pp_resource,
177 Resource* resource_base,
180 // Check for callback errors. If we get any, SetStateForCallbackError will
181 // emit a log message. But we also want to check for resource errors. If there
182 // are both kinds of errors, we'll emit two log messages and return
183 // PP_ERROR_BADRESOURCE.
184 SetStateForCallbackError(report_error);
187 return; // Everything worked.
189 if (callback_.get() && callback_->is_required()) {
190 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE));
192 retval_ = PP_OK_COMPLETIONPENDING;
195 callback_->MarkAsCompleted();
197 retval_ = PP_ERROR_BADRESOURCE;
200 // We choose to silently ignore the error when the pp_resource is null
201 // because this is a pretty common case and we don't want to have lots
202 // of errors in the log. This should be an obvious case to debug.
203 if (report_error && pp_resource) {
206 message = base::StringPrintf(
207 "0x%X is not the correct type for this function.",
210 message = base::StringPrintf(
211 "0x%X is not a valid resource ID.",
214 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
215 std::string(), message);
219 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance,
222 // Check for callback errors. If we get any, SetStateForCallbackError will
223 // emit a log message. But we also want to check for instance errors. If there
224 // are both kinds of errors, we'll emit two log messages and return
225 // PP_ERROR_BADARGUMENT.
226 SetStateForCallbackError(report_error);
229 return; // Everything worked.
231 if (callback_.get() && callback_->is_required()) {
232 callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT));
234 retval_ = PP_OK_COMPLETIONPENDING;
237 callback_->MarkAsCompleted();
239 retval_ = PP_ERROR_BADARGUMENT;
242 // We choose to silently ignore the error when the pp_instance is null as
243 // for PP_Resources above.
244 if (report_error && pp_instance) {
246 message = base::StringPrintf(
247 "0x%X is not a valid instance ID.",
249 PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
250 std::string(), message);
254 } // namespace subtle
256 EnterInstance::EnterInstance(PP_Instance instance)
258 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
259 SetStateForFunctionError(instance, functions_, true);
262 EnterInstance::EnterInstance(PP_Instance instance,
263 const PP_CompletionCallback& callback)
264 : EnterBase(0 /* resource */, callback),
265 // TODO(dmichael): This means that the callback_ we get is not associated
266 // even with the instance, but we should handle that for
267 // MouseLock (maybe others?).
268 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
269 SetStateForFunctionError(instance, functions_, true);
272 EnterInstance::~EnterInstance() {
275 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance)
277 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
278 SetStateForFunctionError(instance, functions_, true);
281 EnterInstanceNoLock::EnterInstanceNoLock(
282 PP_Instance instance,
283 const PP_CompletionCallback& callback)
284 : EnterBase(0 /* resource */, callback),
285 // TODO(dmichael): This means that the callback_ we get is not associated
286 // even with the instance, but we should handle that for
287 // MouseLock (maybe others?).
288 functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
289 SetStateForFunctionError(instance, functions_, true);
292 EnterInstanceNoLock::~EnterInstanceNoLock() {
295 EnterResourceCreation::EnterResourceCreation(PP_Instance instance)
297 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
298 SetStateForFunctionError(instance, functions_, true);
301 EnterResourceCreation::~EnterResourceCreation() {
304 EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance)
306 functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
307 SetStateForFunctionError(instance, functions_, true);
310 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() {