diff --git a/browser/extensions/BUILD.gn b/browser/extensions/BUILD.gn index 44c12270f7b6..81663f8f7e9a 100644 --- a/browser/extensions/BUILD.gn +++ b/browser/extensions/BUILD.gn @@ -4,8 +4,6 @@ source_set("extensions") { sources = [ "api/brave_shields_api.cc", "api/brave_shields_api.h", - "api/content_settings/brave_content_settings_store.cc", - "api/content_settings/brave_content_settings_store.h", "api/settings_private/brave_prefs_util.cc", "api/settings_private/brave_prefs_util.h", "brave_component_extension.cc", 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/browser/extensions/api/content_settings/brave_content_settings_store.cc b/browser/extensions/api/content_settings/brave_content_settings_store.cc deleted file mode 100644 index 98b60200a3a4..000000000000 --- a/browser/extensions/api/content_settings/brave_content_settings_store.cc +++ /dev/null @@ -1,46 +0,0 @@ -/* 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/browser/extensions/api/content_settings/brave_content_settings_store.h" - -namespace extensions { - -BraveContentSettingsStore::BraveContentSettingsStore() - : suppress_notifications_(false) { -} - -BraveContentSettingsStore::~BraveContentSettingsStore() { -} - -void BraveContentSettingsStore::SetExtensionContentSettingFromList( - const std::string& extension_id, - const base::ListValue* list, - ExtensionPrefsScope scope) { - if (list->GetList().empty()) return; - - // For SetExtensionContentSettingFromList use case, we delay the notification - // after the values in the list from extension_prefs are all stored in the - // content setting store. The delay of notification is needed because the - // values saved in extension_prefs will be overwritten by the one saved in - // content setting store in PreferenceAPI::OnContentSettingChanged. Without - // the delay, only the first entry in the list could be saved into content - // setting store. - suppress_notifications_ = true; - ContentSettingsStore::SetExtensionContentSettingFromList( - extension_id, list, scope); - suppress_notifications_ = false; - - // Send a single notification for the entire list. - NotifyOfContentSettingChanged(extension_id, - scope != kExtensionPrefsScopeRegular); -} - -void BraveContentSettingsStore::NotifyOfContentSettingChanged( - const std::string& extension_id, - bool incognito) { - if (suppress_notifications_) return; - ContentSettingsStore::NotifyOfContentSettingChanged(extension_id, incognito); -} - -} // namespace extensions diff --git a/browser/extensions/api/content_settings/brave_content_settings_store.h b/browser/extensions/api/content_settings/brave_content_settings_store.h deleted file mode 100644 index ecfab54711b8..000000000000 --- a/browser/extensions/api/content_settings/brave_content_settings_store.h +++ /dev/null @@ -1,39 +0,0 @@ -/* 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_BROWSER_EXTENSIONS_API_BREVE_CONTENT_SETTINGS_CONTENT_SETTINGS_STORE_H_ -#define BRAVE_BROWSER_EXTENSIONS_API_BREVE_CONTENT_SETTINGS_CONTENT_SETTINGS_STORE_H_ - -#include "chrome/browser/extensions/api/content_settings/content_settings_store.h" - -namespace extensions { - -// This class is the backend for extension-defined content settings. It is used -// by the content_settings::CustomExtensionProvider to integrate its settings -// into the HostContentSettingsMap and by the content settings extension API to -// provide extensions with access to content settings. -class BraveContentSettingsStore : public ContentSettingsStore { - public: - BraveContentSettingsStore(); - - // Deserializes content settings rules from |list| and applies them as set by - // the extension with ID |extension_id|. - void SetExtensionContentSettingFromList(const std::string& extension_id, - const base::ListValue* list, - ExtensionPrefsScope scope) override; - - void NotifyOfContentSettingChanged(const std::string& extension_id, - bool incognito) override; - private: - friend class base::RefCountedThreadSafe; - ~BraveContentSettingsStore() override; - - bool suppress_notifications_; - - DISALLOW_COPY_AND_ASSIGN(BraveContentSettingsStore); -}; - -} // namespace extensions - -#endif // BRAVE_BROWSER_EXTENSIONS_API_BREVE_CONTENT_SETTINGS_CONTENT_SETTINGS_STORE_H_ diff --git a/browser/ui/webui/settings/default_brave_shields_handler.cc b/browser/ui/webui/settings/default_brave_shields_handler.cc index f9de423bf595..bdd7bf8c11b6 100644 --- a/browser/ui/webui/settings/default_brave_shields_handler.cc +++ b/browser/ui/webui/settings/default_brave_shields_handler.cc @@ -79,6 +79,13 @@ void DefaultBraveShieldsHandler::SetAdControlType(const base::ListValue* args) { CONTENT_SETTINGS_TYPE_PLUGINS, brave_shields::kAds, value == "allow" ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK); + HostContentSettingsMapFactory::GetForProfile(profile_)-> + SetContentSettingCustomScope( + ContentSettingsPattern::Wildcard(), + ContentSettingsPattern::Wildcard(), + CONTENT_SETTINGS_TYPE_PLUGINS, + brave_shields::kTrackers, + value == "allow" ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK); } void DefaultBraveShieldsHandler::GetCookieControlType(const base::ListValue* args) { diff --git a/chromium_src/chrome/browser/extensions/api/content_settings/content_settings_service.cc b/chromium_src/chrome/browser/extensions/api/content_settings/content_settings_service.cc deleted file mode 100644 index 615d145195bf..000000000000 --- a/chromium_src/chrome/browser/extensions/api/content_settings/content_settings_service.cc +++ /dev/null @@ -1,9 +0,0 @@ -/* 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/browser/extensions/api/content_settings/brave_content_settings_store.h" - -#define ContentSettingsStore BraveContentSettingsStore -#include "../../../../../../../chrome/browser/extensions/api/content_settings/content_settings_service.cc" -#undef ContentSettingsStore 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/chrome-browser-extensions-api-content_settings-content_settings_store.h.patch b/patches/chrome-browser-extensions-api-content_settings-content_settings_store.h.patch deleted file mode 100644 index d308bc3bb1af..000000000000 --- a/patches/chrome-browser-extensions-api-content_settings-content_settings_store.h.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/chrome/browser/extensions/api/content_settings/content_settings_store.h b/chrome/browser/extensions/api/content_settings/content_settings_store.h -index ea311c4b725153133b79e653339b3a822857fff0..6930c297b296893b97ea09713db3f1238bafebeb 100644 ---- a/chrome/browser/extensions/api/content_settings/content_settings_store.h -+++ b/chrome/browser/extensions/api/content_settings/content_settings_store.h -@@ -86,7 +86,7 @@ class ContentSettingsStore - - // Deserializes content settings rules from |list| and applies them as set by - // the extension with ID |extension_id|. -- void SetExtensionContentSettingFromList(const std::string& extension_id, -+ virtual void SetExtensionContentSettingFromList(const std::string& extension_id, - const base::ListValue* list, - ExtensionPrefsScope scope); - -@@ -111,6 +111,7 @@ class ContentSettingsStore - void RemoveObserver(Observer* observer); - - private: -+ friend class BraveContentSettingsStore; - friend class base::RefCountedThreadSafe; - - struct ExtensionEntry; -@@ -129,7 +130,7 @@ class ContentSettingsStore - const std::string& ext_id, - ExtensionPrefsScope scope) const; - -- void NotifyOfContentSettingChanged(const std::string& extension_id, -+ virtual void NotifyOfContentSettingChanged(const std::string& extension_id, - bool incognito); - - bool OnCorrectThread(); 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",