// Copyright 2017 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 "extensions/renderer/chrome_setting.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "extensions/renderer/bindings/api_binding_util.h" #include "extensions/renderer/bindings/api_event_handler.h" #include "extensions/renderer/bindings/api_invocation_errors.h" #include "extensions/renderer/bindings/api_request_handler.h" #include "extensions/renderer/bindings/api_signature.h" #include "extensions/renderer/bindings/api_type_reference_map.h" #include "extensions/renderer/bindings/binding_access_checker.h" #include "gin/arguments.h" #include "gin/handle.h" #include "gin/object_template_builder.h" namespace extensions { v8::Local ChromeSetting::Create( v8::Isolate* isolate, const std::string& property_name, const base::ListValue* property_values, APIRequestHandler* request_handler, APIEventHandler* event_handler, APITypeReferenceMap* type_refs, const BindingAccessChecker* access_checker) { std::string pref_name; CHECK(property_values->GetString(0u, &pref_name)); const base::DictionaryValue* value_spec = nullptr; CHECK(property_values->GetDictionary(1u, &value_spec)); gin::Handle handle = gin::CreateHandle( isolate, new ChromeSetting(request_handler, event_handler, type_refs, access_checker, pref_name, *value_spec)); return handle.ToV8().As(); } ChromeSetting::ChromeSetting(APIRequestHandler* request_handler, APIEventHandler* event_handler, const APITypeReferenceMap* type_refs, const BindingAccessChecker* access_checker, const std::string& pref_name, const base::DictionaryValue& set_value_spec) : request_handler_(request_handler), event_handler_(event_handler), type_refs_(type_refs), access_checker_(access_checker), pref_name_(pref_name), argument_spec_(ArgumentType::OBJECT) { // The set() call takes an object { value: { type: }, ... }, where // is the custom set() argument specified above by value_spec. ArgumentSpec::PropertiesMap properties; { auto scope_spec = std::make_unique(ArgumentType::REF); scope_spec->set_ref("types.ChromeSettingScope"); scope_spec->set_optional(true); properties["scope"] = std::move(scope_spec); } properties["value"] = std::make_unique(set_value_spec); argument_spec_.set_properties(std::move(properties)); } ChromeSetting::~ChromeSetting() = default; gin::WrapperInfo ChromeSetting::kWrapperInfo = {gin::kEmbedderNativeGin}; gin::ObjectTemplateBuilder ChromeSetting::GetObjectTemplateBuilder( v8::Isolate* isolate) { return Wrappable::GetObjectTemplateBuilder(isolate) .SetMethod("get", &ChromeSetting::Get) .SetMethod("set", &ChromeSetting::Set) .SetMethod("clear", &ChromeSetting::Clear) .SetProperty("onChange", &ChromeSetting::GetOnChangeEvent); } const char* ChromeSetting::GetTypeName() { return "ChromeSetting"; } void ChromeSetting::Get(gin::Arguments* arguments) { HandleFunction("get", arguments); } void ChromeSetting::Set(gin::Arguments* arguments) { v8::Isolate* isolate = arguments->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = arguments->GetHolderCreationContext(); if (!binding::IsContextValidOrThrowError(context)) return; v8::Local value = arguments->PeekNext(); // The set schema included in the Schema object is generic, since it varies // per-setting. However, this is only ever for a single setting, so we can // enforce the types more thoroughly. std::string error; if (!value.IsEmpty() && !argument_spec_.ParseArgument(context, value, *type_refs_, nullptr, nullptr, &error)) { arguments->ThrowTypeError("Invalid invocation"); return; } HandleFunction("set", arguments); } void ChromeSetting::Clear(gin::Arguments* arguments) { HandleFunction("clear", arguments); } v8::Local ChromeSetting::GetOnChangeEvent( gin::Arguments* arguments) { v8::Isolate* isolate = arguments->isolate(); v8::Local context = arguments->GetHolderCreationContext(); v8::Local wrapper = GetWrapper(isolate).ToLocalChecked(); if (!binding::IsContextValidOrThrowError(context)) return v8::Undefined(isolate); v8::Local key = v8::Private::ForApi( isolate, gin::StringToSymbol(isolate, "onChangeEvent")); v8::Local event; if (!wrapper->GetPrivate(context, key).ToLocal(&event)) { NOTREACHED(); return v8::Local(); } DCHECK(!event.IsEmpty()); if (event->IsUndefined()) { bool supports_filters = false; bool supports_lazy_listeners = true; event = event_handler_->CreateEventInstance( base::StringPrintf("types.ChromeSetting.%s.onChange", pref_name_.c_str()), supports_filters, supports_lazy_listeners, binding::kNoListenerMax, true, context); v8::Maybe set_result = wrapper->SetPrivate(context, key, event); if (!set_result.IsJust() || !set_result.FromJust()) { NOTREACHED(); return v8::Local(); } } return event; } void ChromeSetting::HandleFunction(const std::string& method_name, gin::Arguments* arguments) { v8::Isolate* isolate = arguments->isolate(); v8::HandleScope handle_scope(isolate); v8::Local context = arguments->GetHolderCreationContext(); if (!binding::IsContextValidOrThrowError(context)) return; std::vector> argument_list = arguments->GetAll(); std::string full_name = "types.ChromeSetting." + method_name; if (!access_checker_->HasAccessOrThrowError(context, full_name)) return; std::unique_ptr converted_arguments; v8::Local callback; std::string error; const APISignature* signature = type_refs_->GetTypeMethodSignature(full_name); APISignature::JSONParseResult parse_result = signature->ParseArgumentsToJSON(context, argument_list, *type_refs_); if (!parse_result.succeeded()) { arguments->ThrowTypeError(api_errors::InvocationError( full_name, signature->GetExpectedSignature(), *parse_result.error)); return; } parse_result.arguments_list->Insert( parse_result.arguments_list->GetList().begin(), base::Value(pref_name_)); request_handler_->StartRequest( context, full_name, std::move(parse_result.arguments_list), parse_result.callback, v8::Local()); } } // namespace extensions