| // 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/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 kDeprecatedTypes[] = { |
| "fullscreen", "mouselock", |
| }; |
| |
| bool IsDeprecated(base::StringPiece type) { |
| return std::find(std::begin(kDeprecatedTypes), std::end(kDeprecatedTypes), |
| type) != std::end(kDeprecatedTypes); |
| } |
| |
| } // namespace |
| |
| v8::Local<v8::Object> 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<ContentSetting> handle = gin::CreateHandle( |
| isolate, new ContentSetting(request_handler, type_refs, access_checker, |
| pref_name, *value_spec)); |
| return handle.ToV8().As<v8::Object>(); |
| } |
| |
| 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: <t> }, ... }, where <t> |
| // is the custom set() argument specified above by value_spec. |
| ArgumentSpec::PropertiesMap properties; |
| properties["primaryPattern"] = |
| std::make_unique<ArgumentSpec>(ArgumentType::STRING); |
| { |
| auto secondary_pattern_spec = |
| std::make_unique<ArgumentSpec>(ArgumentType::STRING); |
| secondary_pattern_spec->set_optional(true); |
| properties["secondaryPattern"] = std::move(secondary_pattern_spec); |
| } |
| { |
| auto resource_identifier_spec = |
| std::make_unique<ArgumentSpec>(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<ArgumentSpec>(ArgumentType::REF); |
| scope_spec->set_ref("contentSettings.Scope"); |
| scope_spec->set_optional(true); |
| properties["scope"] = std::move(scope_spec); |
| } |
| properties["setting"] = std::make_unique<ArgumentSpec>(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<ContentSetting>::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<v8::Context> context = arguments->GetHolderCreationContext(); |
| |
| if (!binding::IsContextValidOrThrowError(context)) |
| return; |
| |
| std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll(); |
| |
| std::string full_name = "contentSettings.ContentSetting." + method_name; |
| |
| if (!access_checker_->HasAccessOrThrowError(context, full_name)) |
| return; |
| |
| std::unique_ptr<base::ListValue> converted_arguments; |
| v8::Local<v8::Function> callback; |
| std::string error; |
| const APISignature* signature = type_refs_->GetTypeMethodSignature(full_name); |
| if (!signature->ParseArgumentsToJSON(context, argument_list, *type_refs_, |
| &converted_arguments, &callback, |
| &error)) { |
| arguments->ThrowTypeError(api_errors::InvocationError( |
| full_name, signature->GetExpectedSignature(), 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 (!callback.IsEmpty()) { |
| std::vector<v8::Local<v8::Value>> args; |
| if (method_name == "get") { |
| // Deprecated settings are always set to "allow". Populate the result to |
| // avoid breaking extensions. |
| v8::Local<v8::Object> object = v8::Object::New(isolate); |
| v8::Maybe<bool> result = object->CreateDataProperty( |
| context, gin::StringToSymbol(isolate, "setting"), |
| gin::StringToSymbol(isolate, "allow")); |
| // Since we just defined this object, CreateDataProperty() should never |
| // fail. |
| CHECK(result.ToChecked()); |
| args.push_back(object); |
| } |
| JSRunner::Get(context)->RunJSFunction(callback, context, args.size(), |
| args.data()); |
| } |
| return; |
| } |
| |
| if (method_name == "set") { |
| v8::Local<v8::Value> 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; |
| } |
| } |
| |
| converted_arguments->Insert(0u, std::make_unique<base::Value>(pref_name_)); |
| request_handler_->StartRequest( |
| context, "contentSettings." + method_name, std::move(converted_arguments), |
| callback, v8::Local<v8::Function>(), binding::RequestThread::UI); |
| } |
| |
| } // namespace extensions |