| // Copyright (c) 2012 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/ppb_var_deprecated_impl.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <limits> |
| |
| #include "content/renderer/pepper/host_globals.h" |
| #include "content/renderer/pepper/message_channel.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/plugin_object.h" |
| #include "content/renderer/pepper/v8object_var.h" |
| #include "ppapi/c/dev/ppb_var_deprecated.h" |
| #include "ppapi/c/ppb_var.h" |
| #include "ppapi/shared_impl/ppb_var_shared.h" |
| #include "third_party/blink/public/web/web_document.h" |
| #include "third_party/blink/public/web/web_local_frame.h" |
| #include "third_party/blink/public/web/web_plugin_container.h" |
| #include "third_party/blink/public/web/web_plugin_script_forbidden_scope.h" |
| #include "third_party/blink/public/web/web_scoped_user_gesture.h" |
| |
| using ppapi::V8ObjectVar; |
| using ppapi::PpapiGlobals; |
| using ppapi::ScopedPPVar; |
| using ppapi::ScopedPPVarArray; |
| using ppapi::StringVar; |
| using ppapi::Var; |
| |
| namespace content { |
| |
| namespace { |
| |
| const char kInvalidIdentifierException[] = "Error: Invalid identifier."; |
| const char kInvalidObjectException[] = "Error: Invalid object"; |
| const char kUnableToCallMethodException[] = "Error: Unable to call method"; |
| |
| class ObjectAccessor { |
| public: |
| ObjectAccessor(PP_Var var) |
| : object_var_(V8ObjectVar::FromPPVar(var).get()), |
| instance_(object_var_ ? object_var_->instance() : nullptr) { |
| if (instance_) { |
| converter_.reset(new V8VarConverter(instance_->pp_instance(), |
| V8VarConverter::kAllowObjectVars)); |
| } |
| } |
| |
| // Check if the object is valid. If it isn't, set an exception and return |
| // false. |
| bool IsValid(PP_Var* exception) { |
| // If we already have an exception, then the call is invalid according to |
| // the unittests. |
| if (exception && exception->type != PP_VARTYPE_UNDEFINED) |
| return false; |
| if (instance_) |
| return !instance_->is_deleted() || |
| !blink::WebPluginScriptForbiddenScope::IsForbidden(); |
| if (exception) |
| *exception = ppapi::StringVar::StringToPPVar(kInvalidObjectException); |
| return false; |
| } |
| // Lazily grab the object so that the handle is created in the current handle |
| // scope. |
| v8::Local<v8::Object> GetObject() { return object_var_->GetHandle(); } |
| PepperPluginInstanceImpl* instance() { return instance_; } |
| V8VarConverter* converter() { return converter_.get(); } |
| |
| private: |
| V8ObjectVar* object_var_; |
| PepperPluginInstanceImpl* instance_; |
| std::unique_ptr<V8VarConverter> converter_; |
| }; |
| |
| bool IsValidIdentifer(PP_Var identifier, PP_Var* exception) { |
| if (identifier.type == PP_VARTYPE_INT32 || |
| identifier.type == PP_VARTYPE_STRING) { |
| return true; |
| } |
| if (exception) |
| *exception = ppapi::StringVar::StringToPPVar(kInvalidIdentifierException); |
| return false; |
| } |
| |
| bool HasPropertyDeprecated(PP_Var var, PP_Var name, PP_Var* exception) { |
| ObjectAccessor accessor(var); |
| if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception)) |
| return false; |
| |
| PepperTryCatchVar try_catch(accessor.instance(), accessor.converter(), |
| exception); |
| v8::Local<v8::Value> v8_name = try_catch.ToV8(name); |
| if (try_catch.HasException()) |
| return false; |
| |
| v8::Local<v8::Context> context = try_catch.GetContext(); |
| if (try_catch.HasException()) |
| return false; |
| |
| bool result = false; |
| if (!accessor.GetObject()->Has(context, v8_name).To(&result)) { |
| try_catch.HasException(); |
| return false; |
| } |
| |
| return result; |
| } |
| |
| bool HasMethodDeprecated(PP_Var var, PP_Var name, PP_Var* exception) { |
| ObjectAccessor accessor(var); |
| if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception)) |
| return false; |
| |
| PepperTryCatchVar try_catch(accessor.instance(), accessor.converter(), |
| exception); |
| v8::Local<v8::Value> v8_name = try_catch.ToV8(name); |
| if (try_catch.HasException()) |
| return false; |
| |
| v8::Local<v8::Context> context = try_catch.GetContext(); |
| if (try_catch.HasException()) |
| return false; |
| |
| bool has_name = false; |
| if (!accessor.GetObject()->Has(context, v8_name).To(&has_name)) { |
| try_catch.HasException(); |
| return false; |
| } |
| |
| if (!has_name) |
| return false; |
| |
| v8::Local<v8::Value> function; |
| return accessor.GetObject()->Get(context, v8_name).ToLocal(&function) && |
| function->IsFunction(); |
| } |
| |
| PP_Var GetProperty(PP_Var var, PP_Var name, PP_Var* exception) { |
| ObjectAccessor accessor(var); |
| if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception)) |
| return PP_MakeUndefined(); |
| |
| PepperTryCatchVar try_catch(accessor.instance(), accessor.converter(), |
| exception); |
| v8::Local<v8::Value> v8_name = try_catch.ToV8(name); |
| if (try_catch.HasException()) |
| return PP_MakeUndefined(); |
| |
| v8::Local<v8::Value> result; |
| ScopedPPVar result_var = try_catch.FromV8Maybe( |
| accessor.GetObject()->Get(try_catch.GetContext(), v8_name)); |
| if (try_catch.HasException()) |
| return PP_MakeUndefined(); |
| |
| return result_var.Release(); |
| } |
| |
| void EnumerateProperties(PP_Var var, |
| uint32_t* property_count, |
| PP_Var** properties, |
| PP_Var* exception) { |
| ObjectAccessor accessor(var); |
| if (!accessor.IsValid(exception)) |
| return; |
| |
| PepperTryCatchVar try_catch(accessor.instance(), accessor.converter(), |
| exception); |
| |
| *properties = nullptr; |
| *property_count = 0; |
| |
| v8::Local<v8::Context> context = try_catch.GetContext(); |
| v8::Local<v8::Array> identifiers; |
| if (!accessor.GetObject()->GetPropertyNames(context).ToLocal(&identifiers)) |
| return; |
| ScopedPPVarArray identifier_vars(identifiers->Length()); |
| for (uint32_t i = 0; i < identifiers->Length(); ++i) { |
| ScopedPPVar identifier = |
| try_catch.FromV8Maybe(identifiers->Get(context, i)); |
| if (try_catch.HasException()) |
| return; |
| identifier_vars.Set(i, identifier); |
| } |
| |
| size_t size = identifier_vars.size(); |
| *properties = identifier_vars.Release( |
| ScopedPPVarArray::PassPPBMemoryAllocatedArray()); |
| *property_count = size; |
| } |
| |
| void SetPropertyDeprecated(PP_Var var, |
| PP_Var name, |
| PP_Var value, |
| PP_Var* exception) { |
| ObjectAccessor accessor(var); |
| if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception)) |
| return; |
| |
| PepperTryCatchVar try_catch(accessor.instance(), accessor.converter(), |
| exception); |
| v8::Local<v8::Value> v8_name = try_catch.ToV8(name); |
| v8::Local<v8::Value> v8_value = try_catch.ToV8(value); |
| |
| if (try_catch.HasException()) |
| return; |
| |
| if (accessor.GetObject() |
| ->Set(try_catch.GetContext(), v8_name, v8_value) |
| .IsNothing()) { |
| try_catch.HasException(); // Ensure an exception gets set. |
| } |
| } |
| |
| void DeletePropertyDeprecated(PP_Var var, PP_Var name, PP_Var* exception) { |
| ObjectAccessor accessor(var); |
| if (!accessor.IsValid(exception) || !IsValidIdentifer(name, exception)) |
| return; |
| |
| PepperTryCatchVar try_catch(accessor.instance(), accessor.converter(), |
| exception); |
| v8::Local<v8::Value> v8_name = try_catch.ToV8(name); |
| |
| if (try_catch.HasException()) |
| return; |
| |
| v8::Local<v8::Context> context = try_catch.GetContext(); |
| if (try_catch.HasException()) |
| return; |
| |
| if (accessor.GetObject()->Delete(context, v8_name).IsNothing()) { |
| // Ensure exception object is created if V8 has thrown. |
| try_catch.HasException(); |
| return; |
| } |
| return; |
| } |
| |
| PP_Var CallDeprecatedInternal(PP_Var var, |
| PP_Var method_name, |
| uint32_t argc, |
| PP_Var* argv, |
| PP_Var* exception) { |
| ObjectAccessor accessor(var); |
| if (!accessor.IsValid(exception)) |
| return PP_MakeUndefined(); |
| |
| // If the method name is undefined, set it to the empty string to trigger |
| // calling |var| as a function. |
| ScopedPPVar scoped_name(method_name); |
| if (method_name.type == PP_VARTYPE_UNDEFINED) { |
| scoped_name = ScopedPPVar(ScopedPPVar::PassRef(), |
| StringVar::StringToPPVar("")); |
| } |
| |
| PepperTryCatchVar try_catch(accessor.instance(), accessor.converter(), |
| exception); |
| v8::Local<v8::Value> v8_method_name = try_catch.ToV8(scoped_name.get()); |
| if (try_catch.HasException()) |
| return PP_MakeUndefined(); |
| |
| if (!v8_method_name->IsString()) { |
| try_catch.SetException(kUnableToCallMethodException); |
| return PP_MakeUndefined(); |
| } |
| |
| v8::Local<v8::Object> function = accessor.GetObject(); |
| v8::Local<v8::Context> context = accessor.instance()->GetMainWorldContext(); |
| v8::Local<v8::Object> recv = context->Global(); |
| if (v8_method_name.As<v8::String>()->Length() != 0) { |
| v8::Local<v8::Value> value; |
| if (!function->Get(context, v8_method_name).ToLocal(&value) || |
| !value->ToObject(context).ToLocal(&function)) { |
| try_catch.SetException(kUnableToCallMethodException); |
| return PP_MakeUndefined(); |
| } |
| recv = accessor.GetObject(); |
| } |
| |
| if (try_catch.HasException()) |
| return PP_MakeUndefined(); |
| |
| if (!function->IsFunction()) { |
| try_catch.SetException(kUnableToCallMethodException); |
| return PP_MakeUndefined(); |
| } |
| |
| std::unique_ptr<v8::Local<v8::Value>[]> converted_args( |
| new v8::Local<v8::Value>[argc]); |
| for (uint32_t i = 0; i < argc; ++i) { |
| converted_args[i] = try_catch.ToV8(argv[i]); |
| if (try_catch.HasException()) |
| return PP_MakeUndefined(); |
| } |
| |
| blink::WebPluginContainer* container = accessor.instance()->container(); |
| blink::WebLocalFrame* frame = nullptr; |
| if (container) |
| frame = container->GetDocument().GetFrame(); |
| |
| if (!frame) { |
| try_catch.SetException("No frame to execute script in."); |
| return PP_MakeUndefined(); |
| } |
| |
| ScopedPPVar result_var; |
| v8::Local<v8::Value> result; |
| if (frame |
| ->CallFunctionEvenIfScriptDisabled(function.As<v8::Function>(), recv, |
| argc, converted_args.get()) |
| .ToLocal(&result)) { |
| result_var = try_catch.FromV8(result); |
| } |
| |
| if (try_catch.HasException()) |
| return PP_MakeUndefined(); |
| |
| return result_var.Release(); |
| } |
| |
| PP_Var CallDeprecated(PP_Var var, |
| PP_Var method_name, |
| uint32_t argc, |
| PP_Var* argv, |
| PP_Var* exception) { |
| ObjectAccessor accessor(var); |
| if (accessor.instance() && accessor.instance()->IsProcessingUserGesture()) { |
| blink::WebScopedUserGesture user_gesture( |
| accessor.instance()->CurrentUserGestureToken()); |
| return CallDeprecatedInternal(var, method_name, argc, argv, exception); |
| } |
| return CallDeprecatedInternal(var, method_name, argc, argv, exception); |
| } |
| |
| PP_Var Construct(PP_Var var, uint32_t argc, PP_Var* argv, PP_Var* exception) { |
| // Deprecated. |
| NOTREACHED(); |
| return PP_MakeUndefined(); |
| } |
| |
| bool IsInstanceOfDeprecated(PP_Var var, |
| const PPP_Class_Deprecated* ppp_class, |
| void** ppp_class_data) { |
| scoped_refptr<V8ObjectVar> object(V8ObjectVar::FromPPVar(var)); |
| if (!object.get()) |
| return false; // Not an object at all. |
| |
| v8::HandleScope handle_scope(object->instance()->GetIsolate()); |
| v8::Local<v8::Context> context = object->instance()->GetMainWorldContext(); |
| if (context.IsEmpty()) |
| return false; |
| v8::Context::Scope context_scope(context); |
| PluginObject* plugin_object = PluginObject::FromV8Object( |
| object->instance()->GetIsolate(), object->GetHandle()); |
| if (plugin_object && plugin_object->ppp_class() == ppp_class) { |
| if (ppp_class_data) |
| *ppp_class_data = plugin_object->ppp_class_data(); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| PP_Var CreateObjectDeprecated(PP_Instance pp_instance, |
| const PPP_Class_Deprecated* ppp_class, |
| void* ppp_class_data) { |
| PepperPluginInstanceImpl* instance = |
| HostGlobals::Get()->GetInstance(pp_instance); |
| if (!instance) { |
| DLOG(ERROR) << "Create object passed an invalid instance."; |
| return PP_MakeNull(); |
| } |
| return PluginObject::Create(instance, ppp_class, ppp_class_data); |
| } |
| |
| PP_Var CreateObjectWithModuleDeprecated(PP_Module pp_module, |
| const PPP_Class_Deprecated* ppp_class, |
| void* ppp_class_data) { |
| PluginModule* module = HostGlobals::Get()->GetModule(pp_module); |
| if (!module) |
| return PP_MakeNull(); |
| return PluginObject::Create( |
| module->GetSomeInstance(), ppp_class, ppp_class_data); |
| } |
| |
| } // namespace |
| |
| // static |
| const PPB_Var_Deprecated* PPB_Var_Deprecated_Impl::GetVarDeprecatedInterface() { |
| static const PPB_Var_Deprecated var_deprecated_interface = { |
| ppapi::PPB_Var_Shared::GetVarInterface1_0()->AddRef, |
| ppapi::PPB_Var_Shared::GetVarInterface1_0()->Release, |
| ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarFromUtf8, |
| ppapi::PPB_Var_Shared::GetVarInterface1_0()->VarToUtf8, |
| &HasPropertyDeprecated, |
| &HasMethodDeprecated, |
| &GetProperty, |
| &EnumerateProperties, |
| &SetPropertyDeprecated, |
| &DeletePropertyDeprecated, |
| &CallDeprecated, |
| &Construct, |
| &IsInstanceOfDeprecated, |
| &CreateObjectDeprecated, |
| &CreateObjectWithModuleDeprecated, }; |
| |
| return &var_deprecated_interface; |
| } |
| |
| } // namespace content |