• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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"
17 
18 namespace ppapi {
19 namespace {
20 
IsMainThread()21 bool IsMainThread() {
22   return
23       PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread();
24 }
25 
CurrentThreadHandlingBlockingMessage()26 bool CurrentThreadHandlingBlockingMessage() {
27   ppapi::MessageLoopShared* current =
28       PpapiGlobals::Get()->GetCurrentMessageLoop();
29   return current && current->CurrentlyHandlingBlockingMessage();
30 }
31 
32 }  // namespace
33 
34 namespace thunk {
35 
36 namespace subtle {
37 
EnterBase()38 EnterBase::EnterBase()
39     : resource_(NULL),
40       retval_(PP_OK) {
41   PpapiGlobals::Get()->MarkPluginIsActive();
42 }
43 
EnterBase(PP_Resource resource)44 EnterBase::EnterBase(PP_Resource resource)
45     : resource_(GetResource(resource)),
46       retval_(PP_OK) {
47   PpapiGlobals::Get()->MarkPluginIsActive();
48 }
49 
EnterBase(PP_Instance instance,SingletonResourceID resource_id)50 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id)
51     : resource_(GetSingletonResource(instance, resource_id)),
52       retval_(PP_OK) {
53   PpapiGlobals::Get()->MarkPluginIsActive();
54 }
55 
EnterBase(PP_Resource resource,const PP_CompletionCallback & callback)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();
62 }
63 
EnterBase(PP_Instance instance,SingletonResourceID resource_id,const PP_CompletionCallback & callback)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();
72 }
73 
~EnterBase()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?";
81 }
82 
SetResult(int32_t result)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;
89   }
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;
99     }
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;
111     }
112   }
113   callback_ = NULL;
114   return retval_;
115 }
116 
117 // static
GetResource(PP_Resource resource)118 Resource* EnterBase::GetResource(PP_Resource resource) {
119   return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource);
120 }
121 
122 // static
GetSingletonResource(PP_Instance instance,SingletonResourceID resource_id)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;
129 
130   return ppb_instance->GetSingletonResource(instance, resource_id);
131 }
132 
SetStateForCallbackError(bool report_error)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());
137   }
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);
149       }
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);
161       }
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.
167 
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;
179       }
180 
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);
189       }
190     }
191   }
192 }
193 
ClearCallback()194 void EnterBase::ClearCallback() {
195   callback_ = NULL;
196 }
197 
SetStateForResourceError(PP_Resource pp_resource,Resource * resource_base,void * object,bool report_error)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);
207 
208   if (object)
209     return;  // Everything worked.
210 
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;
220   }
221 
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);
235     }
236     PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
237                                                 std::string(), message);
238   }
239 }
240 
SetStateForFunctionError(PP_Instance pp_instance,void * object,bool report_error)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);
249 
250   if (object)
251     return;  // Everything worked.
252 
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;
262   }
263 
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);
273   }
274 }
275 
276 }  // namespace subtle
277 
EnterInstance(PP_Instance instance)278 EnterInstance::EnterInstance(PP_Instance instance)
279     : EnterBase(),
280       functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
281   SetStateForFunctionError(instance, functions_, true);
282 }
283 
EnterInstance(PP_Instance instance,const PP_CompletionCallback & callback)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);
292 }
293 
~EnterInstance()294 EnterInstance::~EnterInstance() {
295 }
296 
EnterInstanceNoLock(PP_Instance instance)297 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance)
298     : EnterBase(),
299       functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
300   SetStateForFunctionError(instance, functions_, true);
301 }
302 
EnterInstanceNoLock(PP_Instance instance,const PP_CompletionCallback & callback)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);
312 }
313 
~EnterInstanceNoLock()314 EnterInstanceNoLock::~EnterInstanceNoLock() {
315 }
316 
EnterResourceCreation(PP_Instance instance)317 EnterResourceCreation::EnterResourceCreation(PP_Instance instance)
318     : EnterBase(),
319       functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
320   SetStateForFunctionError(instance, functions_, true);
321 }
322 
~EnterResourceCreation()323 EnterResourceCreation::~EnterResourceCreation() {
324 }
325 
EnterResourceCreationNoLock(PP_Instance instance)326 EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance)
327     : EnterBase(),
328       functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
329   SetStateForFunctionError(instance, functions_, true);
330 }
331 
~EnterResourceCreationNoLock()332 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() {
333 }
334 
335 }  // namespace thunk
336 }  // namespace ppapi
337