From 869776949226d8a6df3bdf4df09f11ab9788f894 Mon Sep 17 00:00:00 2001 From: Simon Hong Date: Wed, 12 Sep 2018 18:14:48 +0900 Subject: [PATCH] Add Get/Set contentSettings api to BraveShields Newly addied apis set content settings to user preference instead of ContentSettingsStore. Also, previously set value in extension's ContentSettingsStore is deleted when user changes current setting. --- browser/extensions/api/brave_shields_api.cc | 208 ++++++++++++++++++ browser/extensions/api/brave_shields_api.h | 24 ++ .../api/brave_shields_api_browsertest.cc | 135 +++++++++++- .../extensions/renderer/dispatcher.cc | 9 + common/extensions/api/brave_shields.json | 197 +++++++++++++++-- extensions/renderer/BUILD.gn | 14 ++ .../brave_native_extension_bindings_system.cc | 20 ++ .../brave_native_extension_bindings_system.h | 24 ++ .../renderer/brave_shields_content_setting.cc | 157 +++++++++++++ .../renderer/brave_shields_content_setting.h | 86 ++++++++ patches/extensions-renderer-BUILD.gn.patch | 13 ++ 11 files changed, 860 insertions(+), 27 deletions(-) create mode 100644 chromium_src/extensions/renderer/dispatcher.cc create mode 100644 extensions/renderer/BUILD.gn create mode 100644 extensions/renderer/brave_native_extension_bindings_system.cc create mode 100644 extensions/renderer/brave_native_extension_bindings_system.h create mode 100644 extensions/renderer/brave_shields_content_setting.cc create mode 100644 extensions/renderer/brave_shields_content_setting.h create mode 100644 patches/extensions-renderer-BUILD.gn.patch diff --git a/browser/extensions/api/brave_shields_api.cc b/browser/extensions/api/brave_shields_api.cc index 1e508a8c64b0..f268be2286b9 100644 --- a/browser/extensions/api/brave_shields_api.cc +++ b/browser/extensions/api/brave_shields_api.cc @@ -5,14 +5,47 @@ #include "brave/browser/extensions/api/brave_shields_api.h" #include "brave/common/extensions/api/brave_shields.h" +#include "brave/common/extensions/extension_constants.h" #include "brave/components/brave_shields/browser/brave_shields_web_contents_observer.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_api_constants.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_helpers.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_service.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_store.h" +#include "chrome/browser/extensions/api/preference/preference_api_constants.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/profiles/profile.h" +#include "components/content_settings/core/browser/content_settings_registry.h" +#include "components/content_settings/core/browser/content_settings_utils.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" #include "content/public/browser/web_contents.h" +#include "extensions/browser/extension_util.h" using brave_shields::BraveShieldsWebContentsObserver; +namespace Get = extensions::api::brave_shields::ContentSetting::Get; +namespace Set = extensions::api::brave_shields::ContentSetting::Set; +namespace pref_keys = extensions::preference_api_constants; + +namespace { + +bool RemoveContentType(base::ListValue* args, + ContentSettingsType* content_type) { + std::string content_type_str; + if (!args->GetString(0, &content_type_str)) + return false; + // We remove the ContentSettingsType parameter since this is added by the + // renderer, and is not part of the JSON schema. + args->Remove(0, nullptr); + *content_type = + extensions::content_settings_helpers::StringToContentSettingsType( + content_type_str); + return *content_type != CONTENT_SETTINGS_TYPE_DEFAULT; +} + +} // namespace + namespace extensions { namespace api { @@ -43,5 +76,180 @@ ExtensionFunction::ResponseAction BraveShieldsAllowScriptsOnceFunction::Run() { return RespondNow(NoArguments()); } +ExtensionFunction::ResponseAction +BraveShieldsContentSettingGetFunction::Run() { + ContentSettingsType content_type; + EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type)); + + std::unique_ptr params(Get::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + GURL primary_url(params->details.primary_url); + if (!primary_url.is_valid()) { + return RespondNow(Error(content_settings_api_constants::kInvalidUrlError, + params->details.primary_url)); + } + + GURL secondary_url(primary_url); + if (params->details.secondary_url.get()) { + secondary_url = GURL(*params->details.secondary_url); + if (!secondary_url.is_valid()) { + return RespondNow(Error(content_settings_api_constants::kInvalidUrlError, + *params->details.secondary_url)); + } + } + + std::string resource_identifier; + if (params->details.resource_identifier.get()) + resource_identifier = params->details.resource_identifier->id; + + bool incognito = false; + if (params->details.incognito.get()) + incognito = *params->details.incognito; + if (incognito && !include_incognito_information()) + return RespondNow(Error(pref_keys::kIncognitoErrorMessage)); + + HostContentSettingsMap* map; + Profile* profile = Profile::FromBrowserContext(browser_context()); + if (incognito) { + if (!profile->HasOffTheRecordProfile()) { + // TODO(bauerb): Allow reading incognito content settings + // outside of an incognito session. + return RespondNow( + Error(content_settings_api_constants::kIncognitoSessionOnlyError)); + } + map = HostContentSettingsMapFactory::GetForProfile( + profile->GetOffTheRecordProfile()); + } else { + map = HostContentSettingsMapFactory::GetForProfile(profile); + } + + ContentSetting setting = map->GetContentSetting( + primary_url, secondary_url, content_type, resource_identifier); + + std::unique_ptr result(new base::DictionaryValue()); + std::string setting_string = + content_settings::ContentSettingToString(setting); + DCHECK(!setting_string.empty()); + result->SetString(content_settings_api_constants::kContentSettingKey, + setting_string); + + return RespondNow(OneArgument(std::move(result))); +} + +ExtensionFunction::ResponseAction +BraveShieldsContentSettingSetFunction::Run() { + ContentSettingsType content_type; + EXTENSION_FUNCTION_VALIDATE(RemoveContentType(args_.get(), &content_type)); + + std::unique_ptr params(Set::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + + std::string resource_identifier; + if (params->details.resource_identifier.get()) + resource_identifier = params->details.resource_identifier->id; + + std::string setting_str; + EXTENSION_FUNCTION_VALIDATE( + params->details.setting->GetAsString(&setting_str)); + ContentSetting setting; + EXTENSION_FUNCTION_VALIDATE( + content_settings::ContentSettingFromString(setting_str, &setting)); + EXTENSION_FUNCTION_VALIDATE(CONTENT_SETTING_DEFAULT != setting); + EXTENSION_FUNCTION_VALIDATE( + content_settings::ContentSettingsRegistry::GetInstance() + ->Get(content_type) + ->IsSettingValid(setting)); + + std::string primary_error; + ContentSettingsPattern primary_pattern = + content_settings_helpers::ParseExtensionPattern( + params->details.primary_pattern, &primary_error); + if (!primary_pattern.IsValid()) + return RespondNow(Error(primary_error)); + ContentSettingsPattern secondary_pattern = + ContentSettingsPattern::Wildcard(); + if (params->details.secondary_pattern.get()) { + std::string secondary_error; + secondary_pattern = content_settings_helpers::ParseExtensionPattern( + *params->details.secondary_pattern, &secondary_error); + if (!secondary_pattern.IsValid()) + return RespondNow(Error(secondary_error)); + } + + ExtensionPrefsScope scope = kExtensionPrefsScopeRegular; + bool incognito = false; + if (params->details.scope == + brave_shields::SCOPE_INCOGNITO_SESSION_ONLY) { + scope = kExtensionPrefsScopeIncognitoSessionOnly; + incognito = true; + } + + if (incognito) { + // Regular profiles can't access incognito unless the extension is allowed + // to run in incognito contexts. + if (!browser_context()->IsOffTheRecord() && + !extensions::util::IsIncognitoEnabled(brave_extension_id, + browser_context())) { + return RespondNow(Error(pref_keys::kIncognitoErrorMessage)); + } + } else { + // Incognito profiles can't access regular mode ever, they only exist in + // split mode. + if (browser_context()->IsOffTheRecord()) + return RespondNow( + Error(content_settings_api_constants::kIncognitoContextError)); + } + + if (scope == kExtensionPrefsScopeIncognitoSessionOnly && + !Profile::FromBrowserContext(browser_context()) + ->HasOffTheRecordProfile()) { + return RespondNow(Error(pref_keys::kIncognitoSessionOnlyErrorMessage)); + } + + HostContentSettingsMap* map; + Profile* profile = Profile::FromBrowserContext(browser_context()); + if (incognito) { + if (!profile->HasOffTheRecordProfile()) { + // TODO(bauerb): Allow reading incognito content settings + // outside of an incognito session. + return RespondNow( + Error(content_settings_api_constants::kIncognitoSessionOnlyError)); + } + map = HostContentSettingsMapFactory::GetForProfile( + profile->GetOffTheRecordProfile()); + } else { + map = HostContentSettingsMapFactory::GetForProfile(profile); + } + + if (content_type == CONTENT_SETTINGS_TYPE_JAVASCRIPT) { + // TODO(simonhong): Need to check why generating pattern with + // content_settings_helpers::ParseExtensionPattern() causes javascript + // set fail. + const GURL primary_url(params->details.primary_pattern); + if (!primary_url.is_valid()) + return RespondNow(Error("Invalid url")); + + map->SetContentSettingDefaultScope( + primary_url, primary_url, content_type, resource_identifier, setting); + } else { + map->SetContentSettingCustomScope( + primary_pattern, secondary_pattern, + content_type, resource_identifier, setting); + } + + // Delete previous settings set by brave extension in extension's + // ContentSettingsStore. Setting default means delete. + scoped_refptr store = + ContentSettingsService::Get(browser_context())->content_settings_store(); + store->SetExtensionContentSetting(brave_extension_id, + primary_pattern, secondary_pattern, + content_type, + resource_identifier, + CONTENT_SETTING_DEFAULT, + scope); + return RespondNow(NoArguments()); +} + } // namespace api } // namespace extensions diff --git a/browser/extensions/api/brave_shields_api.h b/browser/extensions/api/brave_shields_api.h index 71028fc0b238..d74dc2be14f2 100644 --- a/browser/extensions/api/brave_shields_api.h +++ b/browser/extensions/api/brave_shields_api.h @@ -20,6 +20,30 @@ class BraveShieldsAllowScriptsOnceFunction : public UIThreadExtensionFunction { ResponseAction Run() override; }; +class BraveShieldsContentSettingGetFunction + : public UIThreadExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveShields.get", UNKNOWN) + + protected: + ~BraveShieldsContentSettingGetFunction() override {} + + // ExtensionFunction: + ResponseAction Run() override; +}; + +class BraveShieldsContentSettingSetFunction + : public UIThreadExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("braveShields.set", UNKNOWN) + + protected: + ~BraveShieldsContentSettingSetFunction() override {} + + // ExtensionFunction: + ResponseAction Run() override; +}; + } // namespace api } // namespace extensions diff --git a/browser/extensions/api/brave_shields_api_browsertest.cc b/browser/extensions/api/brave_shields_api_browsertest.cc index 5df17fbb3d55..8314fea569fd 100644 --- a/browser/extensions/api/brave_shields_api_browsertest.cc +++ b/browser/extensions/api/brave_shields_api_browsertest.cc @@ -5,9 +5,16 @@ #include "base/path_service.h" #include "brave/browser/extensions/api/brave_shields_api.h" #include "brave/common/brave_paths.h" +#include "brave/common/extensions/extension_constants.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_api.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_api_constants.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_helpers.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_service.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_store.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" @@ -18,6 +25,10 @@ #include "extensions/common/extension_builder.h" #include "net/dns/mock_host_resolver.h" +namespace extensions { + +using api::BraveShieldsContentSettingGetFunction; +using api::BraveShieldsContentSettingSetFunction; using extensions::api::BraveShieldsAllowScriptsOnceFunction; using extension_function_test_utils::RunFunctionAndReturnError; using extension_function_test_utils::RunFunctionAndReturnSingleResult; @@ -36,6 +47,8 @@ class BraveShieldsAPIBrowserTest : public InProcessBrowserTest { ASSERT_TRUE(embedded_test_server()->Start()); extension_ = extensions::ExtensionBuilder("Test").Build(); + content_settings_ = + HostContentSettingsMapFactory::GetForProfile(browser()->profile()); } content::WebContents* active_contents() { @@ -46,10 +59,12 @@ class BraveShieldsAPIBrowserTest : public InProcessBrowserTest { return extension_; } + HostContentSettingsMap* content_settings() const { + return content_settings_; + } + void BlockScripts() { - HostContentSettingsMap* content_settings = - HostContentSettingsMapFactory::GetForProfile(browser()->profile()); - content_settings->SetContentSettingCustomScope( + content_settings_->SetContentSettingCustomScope( ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_JAVASCRIPT, "", CONTENT_SETTING_BLOCK); } @@ -85,6 +100,7 @@ class BraveShieldsAPIBrowserTest : public InProcessBrowserTest { } private: + HostContentSettingsMap* content_settings_; scoped_refptr extension_; }; @@ -140,3 +156,116 @@ IN_PROC_BROWSER_TEST_F(BraveShieldsAPIBrowserTest, AllowScriptsOnceIframe) { EXPECT_EQ(active_contents()->GetAllFrames().size(), 3u) << "Scripts from b.com should be temporarily allowed."; } + +constexpr char kJavascriptSetParams[] = + "[\"javascript\", {\"primaryPattern\": \"https://www.brave.com/*\"," + "\"setting\": \"block\"}]"; +constexpr char kJavascriptGetParams[] = + "[\"javascript\", {\"primaryUrl\": \"https://www.brave.com/*\"}]"; +constexpr char kBraveURLPattern[] = "https://www.brave.com/*"; +const GURL kBraveURL("https://www.brave.com"); + +// Test javascript content setting works properly via braveShields api. +IN_PROC_BROWSER_TEST_F(BraveShieldsAPIBrowserTest, + ContentSettingJavascriptAPI) { + // Default content settings for javascript is allow. + scoped_refptr get_function( + new BraveShieldsContentSettingGetFunction()); + get_function->set_extension(extension().get()); + std::unique_ptr value; + value.reset(RunFunctionAndReturnSingleResult(get_function.get(), + kJavascriptGetParams, + browser())); + EXPECT_EQ(value->FindKey( + content_settings_api_constants::kContentSettingKey)->GetString(), + std::string("allow")); + + // Block javascript. + scoped_refptr set_function( + new BraveShieldsContentSettingSetFunction()); + set_function->set_extension(extension().get()); + RunFunctionAndReturnSingleResult(set_function.get(), + kJavascriptSetParams, + browser()); + + // Check Block is set. + get_function = base::MakeRefCounted(); + get_function->set_extension(extension().get()); + value.reset(RunFunctionAndReturnSingleResult(get_function.get(), + kJavascriptGetParams, + browser())); + EXPECT_EQ(value->FindKey( + content_settings_api_constants::kContentSettingKey)->GetString(), + std::string("block")); +} + +// Test previous settings set by extension is deleted when setting is newly +// modifed. +IN_PROC_BROWSER_TEST_F(BraveShieldsAPIBrowserTest, + ContentSettingValueFromExtensionDelete) { + // Set javascript content setting via ContentSettingsStore and check this + // settings comes from extension. chrome.contentSettings.javascript.set() + // sets settings into ContentSettingsStore. + std::string primary_error; + ContentSettingsPattern primary_pattern = + content_settings_helpers::ParseExtensionPattern(kBraveURLPattern, + &primary_error); + scoped_refptr store = + ContentSettingsService::Get(browser()->profile())-> + content_settings_store(); + store->SetExtensionContentSetting(brave_extension_id, + primary_pattern, + ContentSettingsPattern::Wildcard(), + CONTENT_SETTINGS_TYPE_JAVASCRIPT, + std::string(), + CONTENT_SETTING_ALLOW, + kExtensionPrefsScopeRegular); + DCHECK(primary_pattern.IsValid()); + + content_settings::SettingInfo info; + content_settings()->GetWebsiteSetting( + kBraveURL, kBraveURL, + CONTENT_SETTINGS_TYPE_JAVASCRIPT, std::string(), &info); + // Check source is extension. + EXPECT_EQ(info.source, + content_settings::SettingSource::SETTING_SOURCE_EXTENSION); + + // Check this value via braveShields api. + scoped_refptr get_function( + new BraveShieldsContentSettingGetFunction()); + get_function->set_extension(extension().get()); + std::unique_ptr value; + value.reset(RunFunctionAndReturnSingleResult(get_function.get(), + kJavascriptGetParams, + browser())); + EXPECT_EQ(value->FindKey( + content_settings_api_constants::kContentSettingKey)->GetString(), + std::string("allow")); + + // Block via shields api. + scoped_refptr set_function( + new BraveShieldsContentSettingSetFunction()); + set_function->set_extension(extension().get()); + RunFunctionAndReturnSingleResult(set_function.get(), + kJavascriptSetParams, + browser()); + + // Check Block is set. + get_function = base::MakeRefCounted(); + get_function->set_extension(extension().get()); + value.reset(RunFunctionAndReturnSingleResult(get_function.get(), + kJavascriptGetParams, + browser())); + EXPECT_EQ(value->FindKey( + content_settings_api_constants::kContentSettingKey)->GetString(), + std::string("block")); + + content_settings()->GetWebsiteSetting( + kBraveURL, kBraveURL, + CONTENT_SETTINGS_TYPE_JAVASCRIPT, std::string(), &info); + // Check source is user. + EXPECT_EQ(info.source, + content_settings::SettingSource::SETTING_SOURCE_USER); +} + +} // namespace extensions diff --git a/chromium_src/extensions/renderer/dispatcher.cc b/chromium_src/extensions/renderer/dispatcher.cc new file mode 100644 index 000000000000..37ba40d24579 --- /dev/null +++ b/chromium_src/extensions/renderer/dispatcher.cc @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/extensions/renderer/brave_native_extension_bindings_system.h" + +#define NativeExtensionBindingsSystem BraveNativeExtensionBindingsSystem +#include "../../../../extensions/renderer/dispatcher.cc" +#undef NativeExtensionBindingsSystem diff --git a/common/extensions/api/brave_shields.json b/common/extensions/api/brave_shields.json index bc50fb090199..b2c0a27964ca 100644 --- a/common/extensions/api/brave_shields.json +++ b/common/extensions/api/brave_shields.json @@ -27,29 +27,178 @@ ] } ], - "functions": [ - { - "name": "allowScriptsOnce", - "type": "function", - "description": "Allow scripts from a list of origins until next reload", - "parameters": [ - { - "name": "origins", - "type": "array", - "items": {"type": "string"} - }, - { - "name": "tabID", - "type": "integer" - }, - { - "type": "function", - "name": "callback", - "optional": true, - "parameters": [] - } - ] - } - ] + "functions": [ + { + "name": "allowScriptsOnce", + "type": "function", + "description": "Allow scripts from a list of origins until next reload", + "parameters": [ + { + "name": "origins", + "type": "array", + "items": {"type": "string"} + }, + { + "name": "tabID", + "type": "integer" + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [] + } + ] + } + ], + "types": [ + { + "id": "ResourceIdentifier", + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The resource identifier for the given content type." + }, + "description": { + "type": "string", + "optional": true, + "description": "A human readable description of the resource." + } + }, + "description": "The only content type using resource identifiers is $(ref:contentSettings.plugins). For more information, see Resource Identifiers." + }, + { + "id": "Scope", + "type": "string", + "enum": ["regular", "incognito_session_only"], + "description": "The scope of the ContentSetting. One of
regular: setting for regular profile (which is inherited by the incognito profile if not overridden elsewhere),
incognito_session_only: setting for incognito profile that can only be set during an incognito session and is deleted when the incognito session ends (overrides regular settings)." + }, + { + "id": "ContentSetting", + "js_module": "ContentSetting", + "type": "object", + "functions": [ + { + "name": "get", + "type": "function", + "description": "Gets the current content setting for a given pair of URLs.", + "parameters": [ + { + "name": "details", + "type": "object", + "properties": { + "primaryUrl": { + "type": "string", + "description": "The primary URL for which the content setting should be retrieved. Note that the meaning of a primary URL depends on the content type." + }, + "secondaryUrl": { + "type": "string", + "description": "The secondary URL for which the content setting should be retrieved. Defaults to the primary URL. Note that the meaning of a secondary URL depends on the content type, and not all content types use secondary URLs.", + "optional": true + }, + "resourceIdentifier": { + "$ref": "ResourceIdentifier", + "optional": true, + "description": "A more specific identifier of the type of content for which the settings should be retrieved." + }, + "incognito": { + "type": "boolean", + "optional": true, + "description": "Whether to check the content settings for an incognito session. (default false)" + } + } + }, + { + "type": "function", + "name": "callback", + "parameters": [ + { + "name": "details", + "type": "object", + "properties": { + "setting": { + "type": "any", + "description": "The content setting. See the description of the individual ContentSetting objects for the possible values." + } + } + } + ] + } + ] + }, + { + "name": "set", + "type": "function", + "description": "Applies a new content setting rule.", + "parameters": [ + { + "name": "details", + "type": "object", + "properties": { + "primaryPattern": { + "type": "string", + "description": "The pattern for the primary URL. For details on the format of a pattern, see Content Setting Patterns." + }, + "secondaryPattern": { + "type": "string", + "description": "The pattern for the secondary URL. Defaults to matching all URLs. For details on the format of a pattern, see Content Setting Patterns.", + "optional": true + }, + "resourceIdentifier": { + "$ref": "ResourceIdentifier", + "optional": true, + "description": "The resource identifier for the content type." + }, + "setting": { + "type": "any", + "description": "The setting applied by this rule. See the description of the individual ContentSetting objects for the possible values." + }, + "scope": { + "$ref": "Scope", + "optional": true, + "description": "Where to set the setting (default: regular)." + } + } + }, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [] + } + ] + } + ] + }, + { + "id": "JavascriptContentSetting", + "type": "string", + "enum": ["allow", "block"] + }, + { + "id": "PluginsContentSetting", + "type": "string", + "enum": ["allow", "block", "detect_important_content"] + } + ], + "properties": { + "javascript": { + "$ref": "ContentSetting", + "description": "Whether to run JavaScript. One of
allow: Run JavaScript,
block: Don't run JavaScript.
Default is allow.
The primary URL is the URL of the top-level frame. The secondary URL is not used.", + "value": [ + "javascript", + {"$ref":"JavascriptContentSetting"} + ] + }, + "plugins": { + "$ref": "ContentSetting", + "description": "Whether to run plugins. One of
allow: Run plugins automatically,
block: Don't run plugins automatically,
detect_important_content: Only run automatically those plugins that are detected as the website's main content.
The primary URL is the URL of the top-level frame. The secondary URL is not used.", + "value": [ + "plugins", + {"$ref":"PluginsContentSetting"} + ] + } + } } ] diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn new file mode 100644 index 000000000000..68ed64083b8b --- /dev/null +++ b/extensions/renderer/BUILD.gn @@ -0,0 +1,14 @@ +source_set("renderer") { + visibility = [ "//extensions/renderer" ] + + sources = [ + "brave_native_extension_bindings_system.cc", + "brave_native_extension_bindings_system.h", + "brave_shields_content_setting.cc", + "brave_shields_content_setting.h", + ] + + # Didn't add any dependencies because sources of this target + # will compiled together with //extensions/renderer target and these files + # doesn't add additional dependencies. +} diff --git a/extensions/renderer/brave_native_extension_bindings_system.cc b/extensions/renderer/brave_native_extension_bindings_system.cc new file mode 100644 index 000000000000..51296f6ec5d3 --- /dev/null +++ b/extensions/renderer/brave_native_extension_bindings_system.cc @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/extensions/renderer/brave_native_extension_bindings_system.h" + +#include "base/bind.h" +#include "brave/extensions/renderer/brave_shields_content_setting.h" +#include "extensions/renderer/ipc_message_sender.h" + +namespace extensions { + +BraveNativeExtensionBindingsSystem::BraveNativeExtensionBindingsSystem( + std::unique_ptr ipc_message_sender) + : NativeExtensionBindingsSystem(std::move(ipc_message_sender)) { + api_system()->RegisterCustomType("braveShields.ContentSetting", + base::Bind(&BraveShieldsContentSetting::Create)); +} + +} // namespace extensions diff --git a/extensions/renderer/brave_native_extension_bindings_system.h b/extensions/renderer/brave_native_extension_bindings_system.h new file mode 100644 index 000000000000..96a319a219db --- /dev/null +++ b/extensions/renderer/brave_native_extension_bindings_system.h @@ -0,0 +1,24 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_EXTENSIONS_RENDERER_BRAVE_NATIVE_EXTENSION_BINDINGS_SYSTEM_H_ +#define BRAVE_EXTENSIONS_RENDERER_BRAVE_NATIVE_EXTENSION_BINDINGS_SYSTEM_H_ + +#include "extensions/renderer/native_extension_bindings_system.h" + +namespace extensions { + +class BraveNativeExtensionBindingsSystem : public NativeExtensionBindingsSystem { + public: + explicit BraveNativeExtensionBindingsSystem( + std::unique_ptr ipc_message_sender); + ~BraveNativeExtensionBindingsSystem() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(BraveNativeExtensionBindingsSystem); +}; + +} // namespace extensions + +#endif // BRAVE_EXTENSIONS_RENDERER_BRAVE_NATIVE_EXTENSION_BINDINGS_SYSTEM_H_ diff --git a/extensions/renderer/brave_shields_content_setting.cc b/extensions/renderer/brave_shields_content_setting.cc new file mode 100644 index 000000000000..1118f30aef44 --- /dev/null +++ b/extensions/renderer/brave_shields_content_setting.cc @@ -0,0 +1,157 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/extensions/renderer/brave_shields_content_setting.h" + +#include "base/strings/stringprintf.h" +#include "base/values.h" +#include "content/public/common/console_message_level.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" + +namespace extensions { + +v8::Local BraveShieldsContentSetting::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 BraveShieldsContentSetting(request_handler, type_refs, access_checker, + pref_name, *value_spec)); + return handle.ToV8().As(); +} + +BraveShieldsContentSetting::BraveShieldsContentSetting(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("braveShields.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("braveShields.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)); +} + +BraveShieldsContentSetting::~BraveShieldsContentSetting() = default; + +gin::WrapperInfo BraveShieldsContentSetting::kWrapperInfo = {gin::kEmbedderNativeGin}; + +gin::ObjectTemplateBuilder BraveShieldsContentSetting::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return Wrappable::GetObjectTemplateBuilder(isolate) + .SetMethod("get", &BraveShieldsContentSetting::Get) + .SetMethod("set", &BraveShieldsContentSetting::Set); +} + +const char* BraveShieldsContentSetting::GetTypeName() { + return "ContentSetting"; +} + +void BraveShieldsContentSetting::Get(gin::Arguments* arguments) { + HandleFunction("get", arguments); +} + +void BraveShieldsContentSetting::Set(gin::Arguments* arguments) { + HandleFunction("set", arguments); +} + +void BraveShieldsContentSetting::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 = "braveShields.ContentSetting." + 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); + if (!signature->ParseArgumentsToJSON(context, argument_list, *type_refs_, + &converted_arguments, &callback, + &error)) { + arguments->ThrowTypeError(api_errors::InvocationError( + full_name, signature->GetExpectedSignature(), error)); + 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; + } + } + + converted_arguments->Insert(0u, std::make_unique(pref_name_)); + request_handler_->StartRequest( + context, "braveShields." + method_name, std::move(converted_arguments), + callback, v8::Local(), binding::RequestThread::UI); +} + +} // namespace extensions diff --git a/extensions/renderer/brave_shields_content_setting.h b/extensions/renderer/brave_shields_content_setting.h new file mode 100644 index 000000000000..d866fcc3e599 --- /dev/null +++ b/extensions/renderer/brave_shields_content_setting.h @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_EXTENSIONS_RENDERER_BRAVE_SHIELDS_CONTENT_SETTING_H_ +#define BRAVE_EXTENSIONS_RENDERER_BRAVE_SHIELDS_CONTENT_SETTING_H_ + +#include + +#include "base/macros.h" +#include "extensions/renderer/bindings/argument_spec.h" +#include "gin/wrappable.h" +#include "v8/include/v8.h" + +namespace base { +class DictionaryValue; +class ListValue; +} + +namespace gin { +class Arguments; +} + +namespace extensions { +class APIEventHandler; +class APIRequestHandler; +class BindingAccessChecker; + +// The custom implementation of the contentSettings.ContentSetting type exposed +// to APIs. +class BraveShieldsContentSetting final + : public gin::Wrappable { + public: + ~BraveShieldsContentSetting() override; + + // Creates a ContentSetting object for the given property. + static v8::Local 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); + + static gin::WrapperInfo kWrapperInfo; + + gin::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override; + const char* GetTypeName() override; + + private: + BraveShieldsContentSetting(APIRequestHandler* request_handler, + const APITypeReferenceMap* type_refs, + const BindingAccessChecker* access_checker, + const std::string& pref_name, + const base::DictionaryValue& argument_spec); + + // JS function handlers: + void Get(gin::Arguments* arguments); + void Set(gin::Arguments* arguments); + + // Common function handling endpoint. + void HandleFunction(const std::string& function_name, + gin::Arguments* arguments); + + APIRequestHandler* request_handler_; + + const APITypeReferenceMap* type_refs_; + + const BindingAccessChecker* const access_checker_; + + // The name of the preference this ContentSetting is managing. + std::string pref_name_; + + // The type of argument that calling set() on the ContentSetting expects + // (since different settings can take a different type of argument depending + // on the preference it manages). + ArgumentSpec argument_spec_; + + DISALLOW_COPY_AND_ASSIGN(BraveShieldsContentSetting); +}; + +} // namespace extensions + +#endif // BRAVE_EXTENSIONS_RENDERER_BRAVE_SHIELDS_CONTENT_SETTING_H_ diff --git a/patches/extensions-renderer-BUILD.gn.patch b/patches/extensions-renderer-BUILD.gn.patch new file mode 100644 index 000000000000..c51ece31745d --- /dev/null +++ b/patches/extensions-renderer-BUILD.gn.patch @@ -0,0 +1,13 @@ +diff --git a/extensions/renderer/BUILD.gn b/extensions/renderer/BUILD.gn +index 1a861507893444036e4f0a9133ff753a9537a23b..e3131669249663f18fc62f2fe4467da550e43edd 100644 +--- a/extensions/renderer/BUILD.gn ++++ b/extensions/renderer/BUILD.gn +@@ -329,6 +329,8 @@ jumbo_source_set("renderer") { + "//third_party/cld_3/src/src:cld_3", + ] + ++ if (brave_chromium_build) { deps += [ "//brave/extensions/renderer"] } ++ + if (proprietary_codecs && enable_wifi_display) { + sources += [ + "api/display_source/wifi_display/wifi_display_audio_encoder.cc",