Home | History | Annotate | Download | only in thunk
      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 
     21 bool IsMainThread() {
     22   return
     23       PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread();
     24 }
     25 
     26 }  // namespace
     27 
     28 namespace thunk {
     29 
     30 namespace subtle {
     31 
     32 EnterBase::EnterBase()
     33     : resource_(NULL),
     34       retval_(PP_OK) {
     35 }
     36 
     37 EnterBase::EnterBase(PP_Resource resource)
     38     : resource_(GetResource(resource)),
     39       retval_(PP_OK) {
     40 }
     41 
     42 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id)
     43     : resource_(GetSingletonResource(instance, resource_id)),
     44       retval_(PP_OK) {
     45 }
     46 
     47 EnterBase::EnterBase(PP_Resource resource,
     48                      const PP_CompletionCallback& callback)
     49     : resource_(GetResource(resource)),
     50       retval_(PP_OK) {
     51   callback_ = new TrackedCallback(resource_, callback);
     52 }
     53 
     54 EnterBase::EnterBase(PP_Instance instance, SingletonResourceID resource_id,
     55                      const PP_CompletionCallback& callback)
     56     : resource_(GetSingletonResource(instance, resource_id)),
     57       retval_(PP_OK) {
     58   DCHECK(resource_ || !instance);
     59   if (!resource_)
     60     retval_ = PP_ERROR_BADARGUMENT;
     61   callback_ = new TrackedCallback(resource_, callback);
     62 }
     63 
     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
     67   // NULL.
     68   DCHECK(!callback_.get())
     69       << "|callback_| is not NULL. Did you forget to call "
     70          "|EnterBase::SetResult| in the interface's thunk?";
     71 }
     72 
     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.
     76     NOTREACHED();
     77     retval_ = result;
     78     return result;
     79   }
     80   if (result == PP_OK_COMPLETIONPENDING) {
     81     retval_ = result;
     82     if (callback_->is_blocking()) {
     83       DCHECK(!IsMainThread());  // We should have returned an error before this.
     84       retval_ = callback_->BlockUntilComplete();
     85     } else {
     86       // The callback is not blocking and the operation will complete
     87       // asynchronously, so there's nothing to do.
     88       retval_ = result;
     89     }
     90   } else {
     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;
     96     } else {
     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();
    100       retval_ = result;
    101     }
    102   }
    103   callback_ = NULL;
    104   return retval_;
    105 }
    106 
    107 // static
    108 Resource* EnterBase::GetResource(PP_Resource resource) {
    109   return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource);
    110 }
    111 
    112 // static
    113 Resource* EnterBase::GetSingletonResource(PP_Instance instance,
    114                                           SingletonResourceID resource_id) {
    115   PPB_Instance_API* ppb_instance =
    116       PpapiGlobals::Get()->GetInstanceAPI(instance);
    117   if (!ppb_instance)
    118     return NULL;
    119 
    120   return ppb_instance->GetSingletonResource(instance, resource_id);
    121 }
    122 
    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());
    127   }
    128   if (callback_.get()) {
    129     if (callback_->is_blocking() && IsMainThread()) {
    130       // Blocking callbacks are never allowed on the main thread.
    131       callback_->MarkAsCompleted();
    132       callback_ = NULL;
    133       retval_ = PP_ERROR_BLOCKS_MAIN_THREAD;
    134       if (report_error) {
    135         std::string message(
    136             "Blocking callbacks are not allowed on the main thread.");
    137         PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
    138                                                     std::string(), message);
    139       }
    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.
    145 
    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 "
    153                             "callback.");
    154         PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
    155                                                     std::string(), message);
    156         LOG(FATAL) << message;
    157       }
    158 
    159       callback_->MarkAsCompleted();
    160       callback_ = NULL;
    161       retval_ = PP_ERROR_NO_MESSAGE_LOOP;
    162       if (report_error) {
    163         std::string message(
    164             "The calling thread must have a message loop attached.");
    165         PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
    166                                                     std::string(), message);
    167       }
    168     }
    169   }
    170 }
    171 
    172 void EnterBase::ClearCallback() {
    173   callback_ = NULL;
    174 }
    175 
    176 void EnterBase::SetStateForResourceError(PP_Resource pp_resource,
    177                                          Resource* resource_base,
    178                                          void* object,
    179                                          bool report_error) {
    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);
    185 
    186   if (object)
    187     return;  // Everything worked.
    188 
    189   if (callback_.get() && callback_->is_required()) {
    190     callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE));
    191     callback_ = NULL;
    192     retval_ = PP_OK_COMPLETIONPENDING;
    193   } else {
    194     if (callback_.get())
    195       callback_->MarkAsCompleted();
    196     callback_ = NULL;
    197     retval_ = PP_ERROR_BADRESOURCE;
    198   }
    199 
    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) {
    204     std::string message;
    205     if (resource_base) {
    206       message = base::StringPrintf(
    207           "0x%X is not the correct type for this function.",
    208           pp_resource);
    209     } else {
    210       message = base::StringPrintf(
    211           "0x%X is not a valid resource ID.",
    212           pp_resource);
    213     }
    214     PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
    215                                                 std::string(), message);
    216   }
    217 }
    218 
    219 void EnterBase::SetStateForFunctionError(PP_Instance pp_instance,
    220                                          void* object,
    221                                          bool report_error) {
    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);
    227 
    228   if (object)
    229     return;  // Everything worked.
    230 
    231   if (callback_.get() && callback_->is_required()) {
    232     callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT));
    233     callback_ = NULL;
    234     retval_ = PP_OK_COMPLETIONPENDING;
    235   } else {
    236     if (callback_.get())
    237       callback_->MarkAsCompleted();
    238     callback_ = NULL;
    239     retval_ = PP_ERROR_BADARGUMENT;
    240   }
    241 
    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) {
    245     std::string message;
    246     message = base::StringPrintf(
    247         "0x%X is not a valid instance ID.",
    248         pp_instance);
    249     PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
    250                                                 std::string(), message);
    251   }
    252 }
    253 
    254 }  // namespace subtle
    255 
    256 EnterInstance::EnterInstance(PP_Instance instance)
    257     : EnterBase(),
    258       functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
    259   SetStateForFunctionError(instance, functions_, true);
    260 }
    261 
    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);
    270 }
    271 
    272 EnterInstance::~EnterInstance() {
    273 }
    274 
    275 EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance)
    276     : EnterBase(),
    277       functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) {
    278   SetStateForFunctionError(instance, functions_, true);
    279 }
    280 
    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);
    290 }
    291 
    292 EnterInstanceNoLock::~EnterInstanceNoLock() {
    293 }
    294 
    295 EnterResourceCreation::EnterResourceCreation(PP_Instance instance)
    296     : EnterBase(),
    297       functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
    298   SetStateForFunctionError(instance, functions_, true);
    299 }
    300 
    301 EnterResourceCreation::~EnterResourceCreation() {
    302 }
    303 
    304 EnterResourceCreationNoLock::EnterResourceCreationNoLock(PP_Instance instance)
    305     : EnterBase(),
    306       functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
    307   SetStateForFunctionError(instance, functions_, true);
    308 }
    309 
    310 EnterResourceCreationNoLock::~EnterResourceCreationNoLock() {
    311 }
    312 
    313 }  // namespace thunk
    314 }  // namespace ppapi
    315