| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/pepper/npapi_glue.h" |
| |
| #include "base/logging.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/strings/string_util.h" |
| #include "content/renderer/pepper/host_array_buffer_var.h" |
| #include "content/renderer/pepper/host_globals.h" |
| #include "content/renderer/pepper/host_var_tracker.h" |
| #include "content/renderer/pepper/npobject_var.h" |
| #include "content/renderer/pepper/pepper_plugin_instance_impl.h" |
| #include "content/renderer/pepper/plugin_module.h" |
| #include "content/renderer/pepper/plugin_object.h" |
| #include "ppapi/c/pp_var.h" |
| #include "third_party/npapi/bindings/npapi.h" |
| #include "third_party/npapi/bindings/npruntime.h" |
| #include "third_party/WebKit/public/web/WebBindings.h" |
| #include "third_party/WebKit/public/web/WebDocument.h" |
| #include "third_party/WebKit/public/web/WebElement.h" |
| #include "third_party/WebKit/public/web/WebFrame.h" |
| #include "third_party/WebKit/public/web/WebPluginContainer.h" |
| #include "v8/include/v8.h" |
| |
| using ppapi::NPObjectVar; |
| using ppapi::PpapiGlobals; |
| using ppapi::StringVar; |
| using ppapi::Var; |
| using blink::WebArrayBuffer; |
| using blink::WebBindings; |
| using blink::WebFrame; |
| using blink::WebPluginContainer; |
| |
| namespace content { |
| |
| namespace { |
| |
| const char kInvalidPluginValue[] = "Error: Plugin returned invalid value."; |
| |
| PP_Var NPObjectToPPVarImpl(PepperPluginInstanceImpl* instance, |
| NPObject* object, |
| v8::Local<v8::Context> context) { |
| DCHECK(object); |
| if (context.IsEmpty()) |
| return PP_MakeUndefined(); |
| v8::Context::Scope context_scope(context); |
| |
| WebArrayBuffer buffer; |
| // TODO(dmichael): Should I protect against duplicate Vars representing the |
| // same array buffer? It's probably not worth the trouble, since it will only |
| // affect in-process plugins. |
| if (WebBindings::getArrayBuffer(object, &buffer)) { |
| scoped_refptr<HostArrayBufferVar> buffer_var( |
| new HostArrayBufferVar(buffer)); |
| return buffer_var->GetPPVar(); |
| } |
| scoped_refptr<NPObjectVar> object_var( |
| HostGlobals::Get()->host_var_tracker()->NPObjectVarForNPObject( |
| instance->pp_instance(), object)); |
| if (!object_var.get()) { // No object for this module yet, make a new one. |
| object_var = new NPObjectVar(instance->pp_instance(), object); |
| } |
| return object_var->GetPPVar(); |
| } |
| |
| |
| } // namespace |
| |
| // Utilities ------------------------------------------------------------------- |
| |
| bool PPVarToNPVariant(PP_Var var, NPVariant* result) { |
| switch (var.type) { |
| case PP_VARTYPE_UNDEFINED: |
| VOID_TO_NPVARIANT(*result); |
| break; |
| case PP_VARTYPE_NULL: |
| NULL_TO_NPVARIANT(*result); |
| break; |
| case PP_VARTYPE_BOOL: |
| BOOLEAN_TO_NPVARIANT(var.value.as_bool, *result); |
| break; |
| case PP_VARTYPE_INT32: |
| INT32_TO_NPVARIANT(var.value.as_int, *result); |
| break; |
| case PP_VARTYPE_DOUBLE: |
| DOUBLE_TO_NPVARIANT(var.value.as_double, *result); |
| break; |
| case PP_VARTYPE_STRING: { |
| StringVar* string = StringVar::FromPPVar(var); |
| if (!string) { |
| VOID_TO_NPVARIANT(*result); |
| return false; |
| } |
| const std::string& value = string->value(); |
| char* c_string = static_cast<char*>(malloc(value.size())); |
| memcpy(c_string, value.data(), value.size()); |
| STRINGN_TO_NPVARIANT(c_string, value.size(), *result); |
| break; |
| } |
| case PP_VARTYPE_OBJECT: { |
| scoped_refptr<NPObjectVar> object(NPObjectVar::FromPPVar(var)); |
| if (!object.get()) { |
| VOID_TO_NPVARIANT(*result); |
| return false; |
| } |
| OBJECT_TO_NPVARIANT(WebBindings::retainObject(object->np_object()), |
| *result); |
| break; |
| } |
| // The following types are not supported for use with PPB_Var_Deprecated, |
| // because PPB_Var_Deprecated is only for trusted plugins, and the trusted |
| // plugins we have don't need these types. We can add support in the future |
| // if it becomes necessary. |
| case PP_VARTYPE_ARRAY: |
| case PP_VARTYPE_DICTIONARY: |
| case PP_VARTYPE_ARRAY_BUFFER: |
| case PP_VARTYPE_RESOURCE: |
| VOID_TO_NPVARIANT(*result); |
| break; |
| } |
| return true; |
| } |
| |
| PP_Var NPVariantToPPVar(PepperPluginInstanceImpl* instance, |
| const NPVariant* variant) { |
| switch (variant->type) { |
| case NPVariantType_Void: |
| return PP_MakeUndefined(); |
| case NPVariantType_Null: |
| return PP_MakeNull(); |
| case NPVariantType_Bool: |
| return PP_MakeBool(PP_FromBool(NPVARIANT_TO_BOOLEAN(*variant))); |
| case NPVariantType_Int32: |
| return PP_MakeInt32(NPVARIANT_TO_INT32(*variant)); |
| case NPVariantType_Double: |
| return PP_MakeDouble(NPVARIANT_TO_DOUBLE(*variant)); |
| case NPVariantType_String: |
| return StringVar::StringToPPVar( |
| NPVARIANT_TO_STRING(*variant).UTF8Characters, |
| NPVARIANT_TO_STRING(*variant).UTF8Length); |
| case NPVariantType_Object: |
| return NPObjectToPPVar(instance, NPVARIANT_TO_OBJECT(*variant)); |
| } |
| NOTREACHED(); |
| return PP_MakeUndefined(); |
| } |
| |
| NPIdentifier PPVarToNPIdentifier(PP_Var var) { |
| switch (var.type) { |
| case PP_VARTYPE_STRING: { |
| StringVar* string = StringVar::FromPPVar(var); |
| if (!string) |
| return NULL; |
| return WebBindings::getStringIdentifier(string->value().c_str()); |
| } |
| case PP_VARTYPE_INT32: |
| return WebBindings::getIntIdentifier(var.value.as_int); |
| default: |
| return NULL; |
| } |
| } |
| |
| PP_Var NPIdentifierToPPVar(NPIdentifier id) { |
| const NPUTF8* string_value = NULL; |
| int32_t int_value = 0; |
| bool is_string = false; |
| WebBindings::extractIdentifierData(id, string_value, int_value, is_string); |
| if (is_string) |
| return StringVar::StringToPPVar(string_value); |
| |
| return PP_MakeInt32(int_value); |
| } |
| |
| PP_Var NPObjectToPPVar(PepperPluginInstanceImpl* instance, NPObject* object) { |
| WebPluginContainer* container = instance->container(); |
| // It's possible that container() is NULL if the plugin has been removed from |
| // the DOM (but the PluginInstance is not destroyed yet). |
| if (!container) |
| return PP_MakeUndefined(); |
| WebFrame* frame = container->element().document().frame(); |
| if (!frame) |
| return PP_MakeUndefined(); |
| |
| v8::HandleScope scope(instance->GetIsolate()); |
| v8::Local<v8::Context> context = frame->mainWorldScriptContext(); |
| return NPObjectToPPVarImpl(instance, object, context); |
| } |
| |
| PP_Var NPObjectToPPVarForTest(PepperPluginInstanceImpl* instance, |
| NPObject* object) { |
| v8::Isolate* test_isolate = v8::Isolate::New(); |
| PP_Var result = PP_MakeUndefined(); |
| { |
| v8::HandleScope scope(test_isolate); |
| v8::Isolate::Scope isolate_scope(test_isolate); |
| v8::Local<v8::Context> context = v8::Context::New(test_isolate); |
| result = NPObjectToPPVarImpl(instance, object, context); |
| } |
| test_isolate->Dispose(); |
| return result; |
| } |
| |
| // PPResultAndExceptionToNPResult ---------------------------------------------- |
| |
| PPResultAndExceptionToNPResult::PPResultAndExceptionToNPResult( |
| NPObject* object_var, |
| NPVariant* np_result) |
| : object_var_(object_var), |
| np_result_(np_result), |
| exception_(PP_MakeUndefined()), |
| success_(false), |
| checked_exception_(false) { |
| } |
| |
| PPResultAndExceptionToNPResult::~PPResultAndExceptionToNPResult() { |
| // The user should have called SetResult or CheckExceptionForNoResult |
| // before letting this class go out of scope, or the exception will have |
| // been lost. |
| DCHECK(checked_exception_); |
| |
| PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); |
| } |
| |
| // Call this with the return value of the PPAPI function. It will convert |
| // the result to the NPVariant output parameter and pass any exception on to |
| // the JS engine. It will update the success flag and return it. |
| bool PPResultAndExceptionToNPResult::SetResult(PP_Var result) { |
| DCHECK(!checked_exception_); // Don't call more than once. |
| DCHECK(np_result_); // Should be expecting a result. |
| |
| checked_exception_ = true; |
| |
| if (has_exception()) { |
| ThrowException(); |
| success_ = false; |
| } else if (!PPVarToNPVariant(result, np_result_)) { |
| WebBindings::setException(object_var_, kInvalidPluginValue); |
| success_ = false; |
| } else { |
| success_ = true; |
| } |
| |
| // No matter what happened, we need to release the reference to the |
| // value passed in. On success, a reference to this value will be in |
| // the np_result_. |
| PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(result); |
| return success_; |
| } |
| |
| // Call this after calling a PPAPI function that could have set the |
| // exception. It will pass the exception on to the JS engine and update |
| // the success flag. |
| // |
| // The success flag will be returned. |
| bool PPResultAndExceptionToNPResult::CheckExceptionForNoResult() { |
| DCHECK(!checked_exception_); // Don't call more than once. |
| DCHECK(!np_result_); // Can't have a result when doing this. |
| |
| checked_exception_ = true; |
| |
| if (has_exception()) { |
| ThrowException(); |
| success_ = false; |
| return false; |
| } |
| success_ = true; |
| return true; |
| } |
| |
| // Call this to ignore any exception. This prevents the DCHECK from failing |
| // in the destructor. |
| void PPResultAndExceptionToNPResult::IgnoreException() { |
| checked_exception_ = true; |
| } |
| |
| // Throws the current exception to JS. The exception must be set. |
| void PPResultAndExceptionToNPResult::ThrowException() { |
| StringVar* string = StringVar::FromPPVar(exception_); |
| if (string) |
| WebBindings::setException(object_var_, string->value().c_str()); |
| } |
| |
| // PPVarArrayFromNPVariantArray ------------------------------------------------ |
| |
| PPVarArrayFromNPVariantArray::PPVarArrayFromNPVariantArray( |
| PepperPluginInstanceImpl* instance, |
| size_t size, |
| const NPVariant* variants) |
| : size_(size) { |
| if (size_ > 0) { |
| array_.reset(new PP_Var[size_]); |
| for (size_t i = 0; i < size_; i++) |
| array_[i] = NPVariantToPPVar(instance, &variants[i]); |
| } |
| } |
| |
| PPVarArrayFromNPVariantArray::~PPVarArrayFromNPVariantArray() { |
| ppapi::VarTracker* var_tracker = PpapiGlobals::Get()->GetVarTracker(); |
| for (size_t i = 0; i < size_; i++) |
| var_tracker->ReleaseVar(array_[i]); |
| } |
| |
| // PPVarFromNPObject ----------------------------------------------------------- |
| |
| PPVarFromNPObject::PPVarFromNPObject(PepperPluginInstanceImpl* instance, |
| NPObject* object) |
| : var_(NPObjectToPPVar(instance, object)) { |
| } |
| |
| PPVarFromNPObject::~PPVarFromNPObject() { |
| PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(var_); |
| } |
| |
| // NPObjectAccessorWithIdentifier ---------------------------------------------- |
| |
| NPObjectAccessorWithIdentifier::NPObjectAccessorWithIdentifier( |
| NPObject* object, |
| NPIdentifier identifier, |
| bool allow_integer_identifier) |
| : object_(PluginObject::FromNPObject(object)), |
| identifier_(PP_MakeUndefined()) { |
| if (object_) { |
| identifier_ = NPIdentifierToPPVar(identifier); |
| if (identifier_.type == PP_VARTYPE_INT32 && !allow_integer_identifier) |
| identifier_.type = PP_VARTYPE_UNDEFINED; // Mark it invalid. |
| } |
| } |
| |
| NPObjectAccessorWithIdentifier::~NPObjectAccessorWithIdentifier() { |
| PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(identifier_); |
| } |
| |
| // TryCatch -------------------------------------------------------------------- |
| |
| TryCatch::TryCatch(PP_Var* exception) |
| : has_exception_(exception && exception->type != PP_VARTYPE_UNDEFINED), |
| exception_(exception) { |
| WebBindings::pushExceptionHandler(&TryCatch::Catch, this); |
| } |
| |
| TryCatch::~TryCatch() { |
| WebBindings::popExceptionHandler(); |
| } |
| |
| void TryCatch::SetException(const char* message) { |
| if (!has_exception()) { |
| has_exception_ = true; |
| if (exception_) { |
| *exception_ = ppapi::StringVar::StringToPPVar(message, strlen(message)); |
| } |
| } |
| } |
| |
| // static |
| void TryCatch::Catch(void* self, const char* message) { |
| static_cast<TryCatch*>(self)->SetException(message); |
| } |
| |
| } // namespace content |