Home | History | Annotate | Download | only in pepper
      1 // Copyright (c) 2011 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 "content/renderer/pepper/npapi_glue.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/ref_counted.h"
      9 #include "base/strings/string_util.h"
     10 #include "content/renderer/pepper/host_array_buffer_var.h"
     11 #include "content/renderer/pepper/host_globals.h"
     12 #include "content/renderer/pepper/host_var_tracker.h"
     13 #include "content/renderer/pepper/npobject_var.h"
     14 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
     15 #include "content/renderer/pepper/plugin_module.h"
     16 #include "content/renderer/pepper/plugin_object.h"
     17 #include "ppapi/c/pp_var.h"
     18 #include "third_party/npapi/bindings/npapi.h"
     19 #include "third_party/npapi/bindings/npruntime.h"
     20 #include "third_party/WebKit/public/web/WebBindings.h"
     21 #include "third_party/WebKit/public/web/WebDocument.h"
     22 #include "third_party/WebKit/public/web/WebElement.h"
     23 #include "third_party/WebKit/public/web/WebFrame.h"
     24 #include "third_party/WebKit/public/web/WebPluginContainer.h"
     25 #include "v8/include/v8.h"
     26 
     27 using ppapi::NPObjectVar;
     28 using ppapi::PpapiGlobals;
     29 using ppapi::StringVar;
     30 using ppapi::Var;
     31 using blink::WebArrayBuffer;
     32 using blink::WebBindings;
     33 using blink::WebFrame;
     34 using blink::WebPluginContainer;
     35 
     36 namespace content {
     37 
     38 namespace {
     39 
     40 const char kInvalidPluginValue[] = "Error: Plugin returned invalid value.";
     41 
     42 PP_Var NPObjectToPPVarImpl(PepperPluginInstanceImpl* instance,
     43                            NPObject* object,
     44                            v8::Local<v8::Context> context) {
     45   DCHECK(object);
     46   if (context.IsEmpty())
     47     return PP_MakeUndefined();
     48   v8::Context::Scope context_scope(context);
     49 
     50   WebArrayBuffer buffer;
     51   // TODO(dmichael): Should I protect against duplicate Vars representing the
     52   // same array buffer? It's probably not worth the trouble, since it will only
     53   // affect in-process plugins.
     54   if (WebBindings::getArrayBuffer(object, &buffer)) {
     55     scoped_refptr<HostArrayBufferVar> buffer_var(
     56         new HostArrayBufferVar(buffer));
     57     return buffer_var->GetPPVar();
     58   }
     59   scoped_refptr<NPObjectVar> object_var(
     60       HostGlobals::Get()->host_var_tracker()->NPObjectVarForNPObject(
     61           instance->pp_instance(), object));
     62   if (!object_var.get()) {  // No object for this module yet, make a new one.
     63     object_var = new NPObjectVar(instance->pp_instance(), object);
     64   }
     65   return object_var->GetPPVar();
     66 }
     67 
     68 
     69 }  // namespace
     70 
     71 // Utilities -------------------------------------------------------------------
     72 
     73 bool PPVarToNPVariant(PP_Var var, NPVariant* result) {
     74   switch (var.type) {
     75     case PP_VARTYPE_UNDEFINED:
     76       VOID_TO_NPVARIANT(*result);
     77       break;
     78     case PP_VARTYPE_NULL:
     79       NULL_TO_NPVARIANT(*result);
     80       break;
     81     case PP_VARTYPE_BOOL:
     82       BOOLEAN_TO_NPVARIANT(var.value.as_bool, *result);
     83       break;
     84     case PP_VARTYPE_INT32:
     85       INT32_TO_NPVARIANT(var.value.as_int, *result);
     86       break;
     87     case PP_VARTYPE_DOUBLE:
     88       DOUBLE_TO_NPVARIANT(var.value.as_double, *result);
     89       break;
     90     case PP_VARTYPE_STRING: {
     91       StringVar* string = StringVar::FromPPVar(var);
     92       if (!string) {
     93         VOID_TO_NPVARIANT(*result);
     94         return false;
     95       }
     96       const std::string& value = string->value();
     97       char* c_string = static_cast<char*>(malloc(value.size()));
     98       memcpy(c_string, value.data(), value.size());
     99       STRINGN_TO_NPVARIANT(c_string, value.size(), *result);
    100       break;
    101     }
    102     case PP_VARTYPE_OBJECT: {
    103       scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(var));
    104       if (!object.get()) {
    105         VOID_TO_NPVARIANT(*result);
    106         return false;
    107       }
    108       OBJECT_TO_NPVARIANT(WebBindings::retainObject(object->np_object()),
    109                           *result);
    110       break;
    111     }
    112     // The following types are not supported for use with PPB_Var_Deprecated,
    113     // because PPB_Var_Deprecated is only for trusted plugins, and the trusted
    114     // plugins we have don't need these types. We can add support in the future
    115     // if it becomes necessary.
    116     case PP_VARTYPE_ARRAY:
    117     case PP_VARTYPE_DICTIONARY:
    118     case PP_VARTYPE_ARRAY_BUFFER:
    119     case PP_VARTYPE_RESOURCE:
    120       VOID_TO_NPVARIANT(*result);
    121       break;
    122   }
    123   return true;
    124 }
    125 
    126 PP_Var NPVariantToPPVar(PepperPluginInstanceImpl* instance,
    127                         const NPVariant* variant) {
    128   switch (variant->type) {
    129     case NPVariantType_Void:
    130       return PP_MakeUndefined();
    131     case NPVariantType_Null:
    132       return PP_MakeNull();
    133     case NPVariantType_Bool:
    134       return PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant)));
    135     case NPVariantType_Int32:
    136       return PP_MakeInt32(NPVARIANT_TO_INT32(*variant));
    137     case NPVariantType_Double:
    138       return PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant));
    139     case NPVariantType_String:
    140       return StringVar::StringToPPVar(
    141           NPVARIANT_TO_STRING(*variant).UTF8Characters,
    142           NPVARIANT_TO_STRING(*variant).UTF8Length);
    143     case NPVariantType_Object:
    144       return NPObjectToPPVar(instance, NPVARIANT_TO_OBJECT(*variant));
    145   }
    146   NOTREACHED();
    147   return PP_MakeUndefined();
    148 }
    149 
    150 NPIdentifier PPVarToNPIdentifier(PP_Var var) {
    151   switch (var.type) {
    152     case PP_VARTYPE_STRING: {
    153       StringVar* string = StringVar::FromPPVar(var);
    154       if (!string)
    155         return NULL;
    156       return WebBindings::getStringIdentifier(string->value().c_str());
    157     }
    158     case PP_VARTYPE_INT32:
    159       return WebBindings::getIntIdentifier(var.value.as_int);
    160     default:
    161       return NULL;
    162   }
    163 }
    164 
    165 PP_Var NPIdentifierToPPVar(NPIdentifier id) {
    166   const NPUTF8* string_value = NULL;
    167   int32_t int_value = 0;
    168   bool is_string = false;
    169   WebBindings::extractIdentifierData(id, string_value, int_value, is_string);
    170   if (is_string)
    171     return StringVar::StringToPPVar(string_value);
    172 
    173   return PP_MakeInt32(int_value);
    174 }
    175 
    176 PP_Var NPObjectToPPVar(PepperPluginInstanceImpl* instance, NPObject* object) {
    177   WebPluginContainer* container = instance->container();
    178   // It's possible that container() is NULL if the plugin has been removed from
    179   // the DOM (but the PluginInstance is not destroyed yet).
    180   if (!container)
    181     return PP_MakeUndefined();
    182   WebFrame* frame = container->element().document().frame();
    183   if (!frame)
    184     return PP_MakeUndefined();
    185 
    186   v8::HandleScope scope(instance->GetIsolate());
    187   v8::Local<v8::Context> context = frame->mainWorldScriptContext();
    188   return NPObjectToPPVarImpl(instance, object, context);
    189 }
    190 
    191 PP_Var NPObjectToPPVarForTest(PepperPluginInstanceImpl* instance,
    192                               NPObject* object) {
    193   v8::Isolate* test_isolate = v8::Isolate::New();
    194   PP_Var result = PP_MakeUndefined();
    195   {
    196     v8::HandleScope scope(test_isolate);
    197     v8::Isolate::Scope isolate_scope(test_isolate);
    198     v8::Local<v8::Context> context = v8::Context::New(test_isolate);
    199     result = NPObjectToPPVarImpl(instance, object, context);
    200   }
    201   test_isolate->Dispose();
    202   return result;
    203 }
    204 
    205 // PPResultAndExceptionToNPResult ----------------------------------------------
    206 
    207 PPResultAndExceptionToNPResult::PPResultAndExceptionToNPResult(
    208     NPObject* object_var,
    209     NPVariant* np_result)
    210     : object_var_(object_var),
    211       np_result_(np_result),
    212       exception_(PP_MakeUndefined()),
    213       success_(false),
    214       checked_exception_(false) {
    215 }
    216 
    217 PPResultAndExceptionToNPResult::~PPResultAndExceptionToNPResult() {
    218   // The user should have called SetResult or CheckExceptionForNoResult
    219   // before letting this class go out of scope, or the exception will have
    220   // been lost.
    221   DCHECK(checked_exception_);
    222 
    223   PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_);
    224 }
    225 
    226 // Call this with the return value of the PPAPI function. It will convert
    227 // the result to the NPVariant output parameter and pass any exception on to
    228 // the JS engine. It will update the success flag and return it.
    229 bool PPResultAndExceptionToNPResult::SetResult(PP_Var result) {
    230   DCHECK(!checked_exception_);  // Don't call more than once.
    231   DCHECK(np_result_);  // Should be expecting a result.
    232 
    233   checked_exception_ = true;
    234 
    235   if (has_exception()) {
    236     ThrowException();
    237     success_ = false;
    238   } else if (!PPVarToNPVariant(result, np_result_)) {
    239     WebBindings::setException(object_var_, kInvalidPluginValue);
    240     success_ = false;
    241   } else {
    242     success_ = true;
    243   }
    244 
    245   // No matter what happened, we need to release the reference to the
    246   // value passed in. On success, a reference to this value will be in
    247   // the np_result_.
    248   PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(result);
    249   return success_;
    250 }
    251 
    252 // Call this after calling a PPAPI function that could have set the
    253 // exception. It will pass the exception on to the JS engine and update
    254 // the success flag.
    255 //
    256 // The success flag will be returned.
    257 bool PPResultAndExceptionToNPResult::CheckExceptionForNoResult() {
    258   DCHECK(!checked_exception_);  // Don't call more than once.
    259   DCHECK(!np_result_);  // Can't have a result when doing this.
    260 
    261   checked_exception_ = true;
    262 
    263   if (has_exception()) {
    264     ThrowException();
    265     success_ = false;
    266     return false;
    267   }
    268   success_ = true;
    269   return true;
    270 }
    271 
    272 // Call this to ignore any exception. This prevents the DCHECK from failing
    273 // in the destructor.
    274 void PPResultAndExceptionToNPResult::IgnoreException() {
    275   checked_exception_ = true;
    276 }
    277 
    278 // Throws the current exception to JS. The exception must be set.
    279 void PPResultAndExceptionToNPResult::ThrowException() {
    280   StringVar* string = StringVar::FromPPVar(exception_);
    281   if (string)
    282     WebBindings::setException(object_var_, string->value().c_str());
    283 }
    284 
    285 // PPVarArrayFromNPVariantArray ------------------------------------------------
    286 
    287 PPVarArrayFromNPVariantArray::PPVarArrayFromNPVariantArray(
    288     PepperPluginInstanceImpl* instance,
    289     size_t size,
    290     const NPVariant* variants)
    291     : size_(size) {
    292   if (size_ > 0) {
    293     array_.reset(new PP_Var[size_]);
    294     for (size_t i = 0; i < size_; i++)
    295       array_[i] = NPVariantToPPVar(instance, &variants[i]);
    296   }
    297 }
    298 
    299 PPVarArrayFromNPVariantArray::~PPVarArrayFromNPVariantArray() {
    300   ppapi::VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker();
    301   for (size_t i = 0; i < size_; i++)
    302     var_tracker->ReleaseVar(array_[i]);
    303 }
    304 
    305 // PPVarFromNPObject -----------------------------------------------------------
    306 
    307 PPVarFromNPObject::PPVarFromNPObject(PepperPluginInstanceImpl* instance,
    308                                      NPObject* object)
    309     : var_(NPObjectToPPVar(instance, object)) {
    310 }
    311 
    312 PPVarFromNPObject::~PPVarFromNPObject() {
    313   PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(var_);
    314 }
    315 
    316 // NPObjectAccessorWithIdentifier ----------------------------------------------
    317 
    318 NPObjectAccessorWithIdentifier::NPObjectAccessorWithIdentifier(
    319     NPObject* object,
    320     NPIdentifier identifier,
    321     bool allow_integer_identifier)
    322     : object_(PluginObject::FromNPObject(object)),
    323       identifier_(PP_MakeUndefined()) {
    324   if (object_) {
    325     identifier_ = NPIdentifierToPPVar(identifier);
    326     if (identifier_.type == PP_VARTYPE_INT32 && !allow_integer_identifier)
    327       identifier_.type = PP_VARTYPE_UNDEFINED;  // Mark it invalid.
    328   }
    329 }
    330 
    331 NPObjectAccessorWithIdentifier::~NPObjectAccessorWithIdentifier() {
    332   PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(identifier_);
    333 }
    334 
    335 // TryCatch --------------------------------------------------------------------
    336 
    337 TryCatch::TryCatch(PP_Var* exception)
    338     : has_exception_(exception && exception->type != PP_VARTYPE_UNDEFINED),
    339       exception_(exception) {
    340   WebBindings::pushExceptionHandler(&TryCatch::Catch, this);
    341 }
    342 
    343 TryCatch::~TryCatch() {
    344   WebBindings::popExceptionHandler();
    345 }
    346 
    347 void TryCatch::SetException(const char* message) {
    348   if (!has_exception()) {
    349     has_exception_ = true;
    350     if (exception_) {
    351       *exception_ = ppapi::StringVar::StringToPPVar(message, strlen(message));
    352     }
    353   }
    354 }
    355 
    356 // static
    357 void TryCatch::Catch(void* self, const char* message) {
    358   static_cast<TryCatch*>(self)->SetException(message);
    359 }
    360 
    361 }  // namespace content
    362