// 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