// 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/content_setting.h" #include "base/containers/contains.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "extensions/renderer/bindings/api_binding_types.h" #include "extensions/renderer/bindings/api_binding_util.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 "extensions/renderer/bindings/js_runner.h" #include "extensions/renderer/console.h" #include "extensions/renderer/script_context_set.h" #include "gin/arguments.h" #include "gin/converter.h" #include "gin/handle.h" #include "gin/object_template_builder.h" #include "third_party/blink/public/mojom/devtools/console_message.mojom.h" namespace extensions { namespace { // Content settings that are deprecated. const char* const kDeprecatedTypesToAllow[] = { "fullscreen", "mouselock", }; const char* const kDeprecatedTypesToBlock[] = { "plugins", }; const char* GetForcedValueForDeprecatedSetting(base::StringPiece type) { if (base::Contains(kDeprecatedTypesToAllow, type)) return "allow"; DCHECK(base::Contains(kDeprecatedTypesToBlock, type)); return "block"; } bool IsDeprecated(base::StringPiece type) { return base::Contains(kDeprecatedTypesToAllow, type) || base::Contains(kDeprecatedTypesToBlock, type); } } // namespace v8::Local ContentSetting::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 ContentSetting(request_handler, type_refs, access_checker, pref_name, *value_spec)); return handle.ToV8().As(); } ContentSetting::ContentSetting(APIRequestHandler* request_handler, const APITypeReferenceMap* type_refs, const BindingAccessChecker* access_checker, const std::string& pref_name, const base::DictionaryValue& set_value_spec) : request_handler_(request_handler), type_refs_(type_refs), access_checker_(access_checker), pref_name_(pref_name), argument_spec_(ArgumentType::OBJECT) { // The set() call takes an object { setting: { type: }, ... }, where // is the custom set() argument specified above by value_spec. ArgumentSpec::PropertiesMap properties; properties["primaryPattern"] = std::make_unique(ArgumentType::STRING); { auto secondary_pattern_spec = std::make_unique(ArgumentType::STRING); secondary_pattern_spec->set_optional(true); properties["secondaryPattern"] = std::move(secondary_pattern_spec); } { auto resource_identifier_spec = std::make_unique(ArgumentType::REF); resource_identifier_spec->set_ref("contentSettings.ResourceIdentifier"); resource_identifier_spec->set_optional(true); properties["resourceIdentifier"] = std::move(resource_identifier_spec); } { auto scope_spec = std::make_unique(ArgumentType::REF); scope_spec->set_ref("contentSettings.Scope"); scope_spec->set_optional(true); properties["scope"] = std::move(scope_spec); } properties["setting"] = std::make_unique(set_value_spec); argument_spec_.set_properties(std::move(properties)); } ContentSetting::~ContentSetting() = default; gin::WrapperInfo ContentSetting::kWrapperInfo = {gin::kEmbedderNativeGin}; gin::ObjectTemplateBuilder ContentSetting::GetObjectTemplateBuilder( v8::Isolate* isolate) { return Wrappable::GetObjectTemplateBuilder(isolate) .SetMethod("get", &ContentSetting::Get) .SetMethod("set", &ContentSetting::Set) .SetMethod("clear", &ContentSetting::Clear) .SetMethod("getResourceIdentifiers", &ContentSetting::GetResourceIdentifiers); } const char* ContentSetting::GetTypeName() { return "ContentSetting"; } void ContentSetting::Get(gin::Arguments* arguments) { HandleFunction("get", arguments); } void ContentSetting::Set(gin::Arguments* arguments) { HandleFunction("set", arguments); } void ContentSetting::Clear(gin::Arguments* arguments) { HandleFunction("clear", arguments); } void ContentSetting::GetResourceIdentifiers(gin::Arguments* arguments) { HandleFunction("getResourceIdentifiers", arguments); } void ContentSetting::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 = "contentSettings.ContentSetting." + method_name; if (!access_checker_->HasAccessOrThrowError(context, full_name)) return; 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; } if (IsDeprecated(pref_name_)) { console::AddMessage(ScriptContextSet::GetContextByV8Context(context), blink::mojom::ConsoleMessageLevel::kWarning, base::StringPrintf("contentSettings.%s is deprecated.", pref_name_.c_str())); // If a callback was provided, call it immediately. if (!parse_result.callback.IsEmpty()) { std::vector> args; if (method_name == "get") { // Populate the result to avoid breaking extensions. v8::Local object = v8::Object::New(isolate); v8::Maybe result = object->CreateDataProperty( context, gin::StringToSymbol(isolate, "setting"), gin::StringToSymbol( isolate, GetForcedValueForDeprecatedSetting(pref_name_))); // Since we just defined this object, CreateDataProperty() should never // fail. CHECK(result.ToChecked()); args.push_back(object); } JSRunner::Get(context)->RunJSFunction(parse_result.callback, context, args.size(), args.data()); } return; } if (method_name == "set") { v8::Local value = argument_list[0]; // 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. // Note: we do this *after* checking if the setting is deprecated, since // this validation will fail for deprecated settings. std::string error; if (!value.IsEmpty() && !argument_spec_.ParseArgument(context, value, *type_refs_, nullptr, nullptr, &error)) { arguments->ThrowTypeError("Invalid invocation: " + error); return; } } parse_result.arguments_list->Insert( parse_result.arguments_list->GetList().begin(), base::Value(pref_name_)); request_handler_->StartRequest(context, "contentSettings." + method_name, std::move(parse_result.arguments_list), parse_result.callback, v8::Local()); } } // namespace extensions