// 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/plugin_object.h" #include #include #include "base/bind.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "content/renderer/pepper/pepper_plugin_instance_impl.h" #include "content/renderer/pepper/pepper_try_catch.h" #include "content/renderer/pepper/plugin_module.h" #include "content/renderer/pepper/v8_var_converter.h" #include "gin/arguments.h" #include "gin/converter.h" #include "gin/function_template.h" #include "gin/handle.h" #include "gin/interceptor.h" #include "gin/object_template_builder.h" #include "gin/public/gin_embedders.h" #include "ppapi/c/dev/ppb_var_deprecated.h" #include "ppapi/c/dev/ppp_class_deprecated.h" #include "ppapi/c/pp_resource.h" #include "ppapi/c/pp_var.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/resource_tracker.h" #include "ppapi/shared_impl/var.h" #include "ppapi/shared_impl/var_tracker.h" using ppapi::PpapiGlobals; using ppapi::ScopedPPVar; using ppapi::ScopedPPVarArray; using ppapi::StringVar; using ppapi::Var; namespace content { namespace { const char kInvalidValueException[] = "Error: Invalid value"; } // namespace // PluginObject ---------------------------------------------------------------- PluginObject::~PluginObject() { if (instance_) { ppp_class_->Deallocate(ppp_class_data_); instance_->RemovePluginObject(this); } } // static gin::WrapperInfo PluginObject::kWrapperInfo = {gin::kEmbedderNativeGin}; // static PluginObject* PluginObject::FromV8Object(v8::Isolate* isolate, v8::Local v8_object) { PluginObject* plugin_object; if (!v8_object.IsEmpty() && gin::ConvertFromV8(isolate, v8_object, &plugin_object)) { return plugin_object; } return nullptr; } // static PP_Var PluginObject::Create(PepperPluginInstanceImpl* instance, const PPP_Class_Deprecated* ppp_class, void* ppp_class_data) { V8VarConverter var_converter(instance->pp_instance(), V8VarConverter::kAllowObjectVars); PepperTryCatchVar try_catch(instance, &var_converter, nullptr); // If the V8 context is empty, we may be in the process of tearing down the // frame and may not have a valid isolate (in particular due to re-entrancy). // We shouldn't try to call gin::CreateHandle. if (try_catch.GetContext().IsEmpty()) { ppp_class->Deallocate(ppp_class_data); return PP_MakeUndefined(); } gin::Handle object = gin::CreateHandle(instance->GetIsolate(), new PluginObject(instance, ppp_class, ppp_class_data)); ScopedPPVar result = try_catch.FromV8(object.ToV8()); DCHECK(!try_catch.HasException()); return result.Release(); } v8::Local PluginObject::GetNamedProperty( v8::Isolate* isolate, const std::string& identifier) { if (!instance_) { std::string error = "Property " + identifier + " does not exist."; isolate->ThrowException( v8::Exception::ReferenceError(gin::StringToV8(isolate, error))); return v8::Local(); } ScopedPPVar identifier_var(ScopedPPVar::PassRef(), StringVar::StringToPPVar(identifier)); return GetPropertyOrMethod(instance_->GetIsolate(), identifier_var.get()); } bool PluginObject::SetNamedProperty(v8::Isolate* isolate, const std::string& identifier, v8::Local value) { if (!instance_) { std::string error = "Property " + identifier + " does not exist."; isolate->ThrowException( v8::Exception::ReferenceError(gin::StringToV8(isolate, error))); return false; } ScopedPPVar identifier_var(ScopedPPVar::PassRef(), StringVar::StringToPPVar(identifier)); V8VarConverter var_converter(instance_->pp_instance(), V8VarConverter::kAllowObjectVars); PepperTryCatchV8 try_catch(instance_, &var_converter, isolate); bool has_property = ppp_class_->HasProperty(ppp_class_data_, identifier_var.get(), try_catch.exception()); if (try_catch.ThrowException()) return false; if (!has_property) return false; ScopedPPVar var = try_catch.FromV8(value); if (try_catch.ThrowException()) return false; ppp_class_->SetProperty(ppp_class_data_, identifier_var.get(), var.get(), try_catch.exception()); // If the plugin threw an exception, then throw a V8 version of it to // JavaScript. Either way, return true, because we successfully dispatched // the call to the plugin. try_catch.ThrowException(); return true; } std::vector PluginObject::EnumerateNamedProperties( v8::Isolate* isolate) { std::vector result; if (!instance_) { std::string error = "Plugin object deleted"; isolate->ThrowException( v8::Exception::ReferenceError(gin::StringToV8(isolate, error))); return result; } V8VarConverter var_converter(instance_->pp_instance(), V8VarConverter::kAllowObjectVars); PepperTryCatchV8 try_catch(instance_, &var_converter, isolate); PP_Var* name_vars; uint32_t count = 0; ppp_class_->GetAllPropertyNames(ppp_class_data_, &count, &name_vars, try_catch.exception()); ScopedPPVarArray scoped_name_vars( ScopedPPVarArray::PassPPBMemoryAllocatedArray(), name_vars, count); if (try_catch.ThrowException()) return result; for (uint32_t i = 0; i < count; ++i) { StringVar* string_var = StringVar::FromPPVar(name_vars[i]); if (string_var) { result.push_back(string_var->value()); } else { try_catch.ThrowException(kInvalidValueException); result.clear(); return result; } } return result; } void PluginObject::InstanceDeleted() { instance_ = nullptr; } PluginObject::PluginObject(PepperPluginInstanceImpl* instance, const PPP_Class_Deprecated* ppp_class, void* ppp_class_data) : gin::NamedPropertyInterceptor(instance->GetIsolate(), this), instance_(instance), ppp_class_(ppp_class), ppp_class_data_(ppp_class_data), template_cache_(instance->GetIsolate()), weak_factory_(this) { instance_->AddPluginObject(this); } gin::ObjectTemplateBuilder PluginObject::GetObjectTemplateBuilder( v8::Isolate* isolate) { return Wrappable::GetObjectTemplateBuilder(isolate) .AddNamedPropertyInterceptor(); } v8::Local PluginObject::GetPropertyOrMethod(v8::Isolate* isolate, PP_Var identifier_var) { if (!instance_) return v8::Local(); V8VarConverter var_converter(instance_->pp_instance(), V8VarConverter::kAllowObjectVars); PepperTryCatchV8 try_catch(instance_, &var_converter, isolate); bool has_property = ppp_class_->HasProperty(ppp_class_data_, identifier_var, try_catch.exception()); if (try_catch.ThrowException()) return v8::Local(); if (has_property) { ScopedPPVar result_var(ScopedPPVar::PassRef(), ppp_class_->GetProperty(ppp_class_data_, identifier_var, try_catch.exception())); if (try_catch.ThrowException()) return v8::Local(); v8::Local result = try_catch.ToV8(result_var.get()); if (try_catch.ThrowException()) return v8::Local(); return result; } bool has_method = identifier_var.type == PP_VARTYPE_STRING && ppp_class_->HasMethod(ppp_class_data_, identifier_var, try_catch.exception()); if (try_catch.ThrowException()) return v8::Local(); if (has_method) { const std::string& identifier = StringVar::FromPPVar(identifier_var)->value(); return GetFunctionTemplate(isolate, identifier)->GetFunction(); } return v8::Local(); } void PluginObject::Call(const std::string& identifier, gin::Arguments* args) { if (!instance_) return; V8VarConverter var_converter(instance_->pp_instance(), V8VarConverter::kAllowObjectVars); PepperTryCatchV8 try_catch(instance_, &var_converter, args->isolate()); ScopedPPVar identifier_var(ScopedPPVar::PassRef(), StringVar::StringToPPVar(identifier)); ScopedPPVarArray argument_vars(args->Length()); for (uint32_t i = 0; i < argument_vars.size(); ++i) { v8::Local arg; if (!args->GetNext(&arg)) { NOTREACHED(); } argument_vars.Set(i, try_catch.FromV8(arg)); if (try_catch.ThrowException()) return; } // For the OOP plugin case we need to grab a reference on the plugin module // object to ensure that it is not destroyed courtesy an incoming // ExecuteScript call which destroys the plugin module and in turn the // dispatcher. scoped_refptr ref(instance_->module()); ScopedPPVar result_var(ScopedPPVar::PassRef(), ppp_class_->Call(ppp_class_data_, identifier_var.get(), argument_vars.size(), argument_vars.get(), try_catch.exception())); if (try_catch.ThrowException()) return; v8::Local result = try_catch.ToV8(result_var.get()); if (try_catch.ThrowException()) return; args->Return(result); } v8::Local PluginObject::GetFunctionTemplate( v8::Isolate* isolate, const std::string& name) { v8::Local function_template = template_cache_.Get(name); if (!function_template.IsEmpty()) return function_template; function_template = gin::CreateFunctionTemplate( isolate, base::Bind(&PluginObject::Call, weak_factory_.GetWeakPtr(), name)); template_cache_.Set(name, function_template); return function_template; } } // namespace content