From b94d4b0df2a6a27349f7c2dd8d207c9e3df46ae5 Mon Sep 17 00:00:00 2001 From: boocmp Date: Tue, 15 Oct 2024 21:36:21 +0700 Subject: [PATCH 01/21] Added Custom Scriptlet section in the settings page. --- app/brave_settings_strings.grdp | 36 +++ .../ad_block_service_browsertest.cc | 4 +- .../brave_adblock_browser_proxy.ts | 69 +++++- .../brave_adblock_subpage.html | 10 + .../brave_adblock_subpage.ts | 8 + .../brave_adblock_scriptlet_editor.html | 51 +++++ .../brave_adblock_scriptlet_editor.ts | 126 +++++++++++ .../brave_adblock_scriptlet_list.html | 51 +++++ .../brave_adblock_scriptlet_list.ts | 94 ++++++++ browser/resources/settings/sources.gni | 2 + .../webui/settings/brave_adblock_handler.cc | 102 ++++++++- .../ui/webui/settings/brave_adblock_handler.h | 4 + ...ave_settings_localized_strings_provider.cc | 29 +++ .../content/browser/ad_block_service.cc | 26 ++- .../content/browser/ad_block_service.h | 15 +- .../brave_shields/core/browser/BUILD.gn | 2 + .../ad_block_custom_resource_provider.cc | 209 ++++++++++++++++++ .../ad_block_custom_resource_provider.h | 64 ++++++ .../ad_block_default_resource_provider.cc | 3 +- .../browser/ad_block_resource_provider.cc | 2 +- .../core/browser/ad_block_resource_provider.h | 2 +- .../brave_shields/core/common/features.cc | 4 + .../brave_shields/core/common/features.h | 1 + .../brave_shields/core/common/pref_names.h | 2 + 24 files changed, 891 insertions(+), 25 deletions(-) create mode 100644 browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html create mode 100644 browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts create mode 100644 browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_list.html create mode 100644 browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_list.ts create mode 100644 components/brave_shields/core/browser/ad_block_custom_resource_provider.cc create mode 100644 components/brave_shields/core/browser/ad_block_custom_resource_provider.h diff --git a/app/brave_settings_strings.grdp b/app/brave_settings_strings.grdp index 8fa7fbb411c4..19d9acbb92d9 100644 --- a/app/brave_settings_strings.grdp +++ b/app/brave_settings_strings.grdp @@ -577,6 +577,42 @@ Filter lists + + Custom scriptlets + + + Add new scriptlet + + + Add new scriptlet + + + Edit scriptlet + + + Name + + + Content + + + Cancel + + + Save + + + Are you sure you want to delete this scriptlet? If this scriptlet is used in any custom filter, it will no longer work. + + + Scriptlet is already exists. + + + Invalid scriptlet name. + + + Scriptlet is not found. + Shortcuts diff --git a/browser/brave_shields/ad_block_service_browsertest.cc b/browser/brave_shields/ad_block_service_browsertest.cc index 672513999b4e..598446109f5f 100644 --- a/browser/brave_shields/ad_block_service_browsertest.cc +++ b/browser/brave_shields/ad_block_service_browsertest.cc @@ -251,9 +251,7 @@ void AdBlockServiceTest::UpdateAdBlockResources(const std::string& resources) { brave_shields::AdBlockService* service = g_brave_browser_process->ad_block_service(); - static_cast( - service->resource_provider()) - ->OnComponentReady(component_path); + service->default_resource_provider()->OnComponentReady(component_path); } void AdBlockServiceTest::UpdateAdBlockInstanceWithRules( diff --git a/browser/resources/settings/default_brave_shields_page/brave_adblock_browser_proxy.ts b/browser/resources/settings/default_brave_shields_page/brave_adblock_browser_proxy.ts index df24e2c75d90..79aaebdd47aa 100644 --- a/browser/resources/settings/default_brave_shields_page/brave_adblock_browser_proxy.ts +++ b/browser/resources/settings/default_brave_shields_page/brave_adblock_browser_proxy.ts @@ -5,7 +5,22 @@ // @ts-nocheck TODO(petemill): Define types and remove ts-nocheck -import { sendWithPromise, addWebUiListener } from 'chrome://resources/js/cr.js'; +import { sendWithPromise, addWebUiListener } from 'chrome://resources/js/cr.js' + +export class Scriptlet { + name: string + kind: object = { + mime: 'application/javascript' + } + content: string +} + +export enum ErrorCode { + kOK = 0, + kInvalidName, + kAlreadyExists, + kNotFound, +} export interface BraveAdblockBrowserProxy { getRegionalLists(): Promise // TODO(petemill): Define the expected type @@ -19,6 +34,10 @@ export interface BraveAdblockBrowserProxy { updateSubscription(url: string) deleteSubscription(url: string) viewSubscription(url: string) + getCustomScriptlets(): Promise + addCustomScriptlet(scriptlet: Scriptlet): Promise + updateCustomScriptlet(name: string, scriptlet: Scriptlet): Promise + removeCustomScriptlet(name: string): Promise } export class BraveAdblockBrowserProxyImpl implements BraveAdblockBrowserProxy { @@ -74,7 +93,53 @@ export class BraveAdblockBrowserProxyImpl implements BraveAdblockBrowserProxy { chrome.send('brave_adblock.viewSubscription', [url]) } - addWebUiListener (event_name, callback) { + utf8ToBase64_(str) { + const uint8Array = new TextEncoder().encode(str) + const base64String = btoa(String.fromCharCode.apply(null, uint8Array)) + return base64String + } + + base64ToUtf8_(base64) { + const binaryString = atob(base64) + const bytes = new Uint8Array(binaryString.length) + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i) + } + return new TextDecoder().decode(bytes) + } + + getCustomScriptlets() { + return sendWithPromise('brave_adblock.getCustomScriptlets') + .then((scriptlets) => { + for (const scriptlet of scriptlets) { + scriptlet.content = this.base64ToUtf8_(scriptlet.content) + } + return scriptlets + }) + .catch((error) => { + throw error + }) + } + + addCustomScriptlet(scriptlet) { + scriptlet.content = this.utf8ToBase64_(scriptlet.content) + return sendWithPromise('brave_adblock.addCustomScriptlet', scriptlet) + } + + updateCustomScriptlet(name, scriptlet) { + scriptlet.content = this.utf8ToBase64_(scriptlet.content) + return sendWithPromise( + 'brave_adblock.updateCustomScriptlet', + name, + scriptlet + ) + } + + removeCustomScriptlet(name) { + return sendWithPromise('brave_adblock.removeCustomScriptlet', name) + } + + addWebUiListener(event_name, callback) { addWebUiListener(event_name, callback) } } diff --git a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html index 2f6a04deb9a8..1ead88bd052a 100644 --- a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html +++ b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html @@ -328,3 +328,13 @@ > + diff --git a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts index 5660ae8d40d1..773527887f50 100644 --- a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts +++ b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts @@ -19,6 +19,8 @@ import {BaseMixin} from '../base_mixin.js'; import {BraveAdblockBrowserProxyImpl} from './brave_adblock_browser_proxy.js' import {getTemplate} from './brave_adblock_subpage.html.js' +import { loadTimeData } from '../i18n_setup.js' + const AdBlockSubpageBase = PrefsMixin(I18nMixin(BaseMixin(PolymerElement))) class AdBlockSubpage extends AdBlockSubpageBase { @@ -41,6 +43,12 @@ class AdBlockSubpage extends AdBlockSubpageBase { type: Boolean, value: false }, + cosmeticFilteringCustomScriptletsEnabled_: { + type: Boolean, + value: loadTimeData.getBoolean( + 'cosmeticFilteringCustomScriptletsEnabled' + ) + } } } diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html new file mode 100644 index 000000000000..f50c568fc443 --- /dev/null +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html @@ -0,0 +1,51 @@ + + + +
[[dialogTitle_]]
+
+
+
$i18n{adblockCustomSciptletDialogNameLabel}
+ + +
+
+
$i18n{adblockCustomScriptletDialogContentLabel}
+ +
+
+
+ $i18n{adblockCustomScriptletDialogCancelButton} + + + $i18n{adblockCustomScriptletDialogSaveButton} + +
+
\ No newline at end of file diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts new file mode 100644 index 000000000000..499155f5cd9c --- /dev/null +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts @@ -0,0 +1,126 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// 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 https://mozilla.org/MPL/2.0/. + +import 'chrome://resources/cr_elements/cr_button/cr_button.js' +import { CrDialogElement } from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.js' +import 'chrome://resources/cr_elements/cr_input/cr_input.js' + +import { PrefsMixin } from '/shared/settings/prefs/prefs_mixin.js' +import { I18nMixin } from 'chrome://resources/cr_elements/i18n_mixin.js' +import { PolymerElement } from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js' + +import { getTemplate } from './brave_adblock_scriptlet_editor.html.js' + +import { + Scriptlet, + BraveAdblockBrowserProxyImpl, + ErrorCode +} from '../brave_adblock_browser_proxy.js' + +interface AdblockScriptletEditor { + $: { + dialog: CrDialogElement + } +} + +const AdblockScriptletEditorBase = I18nMixin(PrefsMixin(PolymerElement)) + +class AdblockScriptletEditor extends AdblockScriptletEditorBase { + static get is() { + return 'adblock-scriptlet-editor' + } + + static get template() { + return getTemplate() + } + + static get properties() { + return { + scriptlet: Scriptlet, + dialogTitle_: String, + isScriptletValid_: Boolean, + scriptletErrorMessage_: String + } + } + + scriptlet: Scriptlet + dialogTitle_: string + isScriptletValid_: boolean + scriptletErrorMessage_: string + + scriptletName_: string + browserProxy_ = BraveAdblockBrowserProxyImpl.getInstance() + + override ready() { + super.ready() + this.scriptletName_ = this.scriptlet.name + + if (this.scriptletName_) { + this.dialogTitle_ = this.i18n('adblockEditCustomScriptletDialogTitle') + } else { + this.dialogTitle_ = this.i18n('adblockAddCustomScriptletDialogTitle') + } + + this.updateError(ErrorCode.kOK) + } + + updateError(error_code: ErrorCode) { + this.isScriptletValid_ = error_code === ErrorCode.kOK + switch (error_code) { + case ErrorCode.kOK: + this.scriptletErrorMessage_ = '' + break + case ErrorCode.kAlreadyExists: + this.scriptletErrorMessage_ = this.i18n( + 'adblockEditCustomScriptletAlreadyExistsError' + ) + break + case ErrorCode.kInvalidName: + this.scriptletErrorMessage_ = this.i18n( + 'adblockEditCustomScriptletInvalidNameError' + ) + break + case ErrorCode.kNotFound: + this.scriptletErrorMessage_ = this.i18n( + 'adblockEditCustomScriptletNotFoundError' + ) + break + } + } + + cancelClicked_() { + this.$.dialog.cancel() + } + + saveClicked_() { + if (this.scriptletName_) { + this.browserProxy_ + .updateCustomScriptlet(this.scriptletName_, this.scriptlet) + .then((e) => { + this.updateError(e) + if (this.isScriptletValid_) { + this.$.dialog.close() + } + }) + } else { + this.browserProxy_.addCustomScriptlet(this.scriptlet).then((e) => { + this.updateError(e) + if (this.isScriptletValid_) { + this.$.dialog.close() + } + }) + } + } + + validateName_() { + if (!/^[a-z-_.]*$/.test(this.scriptlet.name)) { + this.updateError(ErrorCode.kInvalidName) + } else { + this.updateError(ErrorCode.kOK) + } + } +} + +customElements.define(AdblockScriptletEditor.is, AdblockScriptletEditor) diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_list.html b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_list.html new file mode 100644 index 000000000000..e57ea6c5e2f3 --- /dev/null +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_list.html @@ -0,0 +1,51 @@ + + +
+
+ +
+ + + $i18n{adblockAddCustomScriptletButton} + +
+ + \ No newline at end of file diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_list.ts b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_list.ts new file mode 100644 index 000000000000..4dd273057e82 --- /dev/null +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_list.ts @@ -0,0 +1,94 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * 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 https://mozilla.org/MPL/2.0/. */ + +import 'chrome://resources/cr_elements/cr_button/cr_button.js' +import 'chrome://resources/cr_elements/icons.html.js' + +import { PrefsMixin } from '/shared/settings/prefs/prefs_mixin.js' +import { I18nMixin } from 'chrome://resources/cr_elements/i18n_mixin.js' +import { PolymerElement } from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js' + +import { BaseMixin } from '../../base_mixin.js' + +import { getTemplate } from './brave_adblock_scriptlet_list.html.js' + +import { + Scriptlet, + BraveAdblockBrowserProxyImpl +} from '../brave_adblock_browser_proxy.js' + +import './brave_adblock_scriptlet_editor.js' + +const AdblockScriptletListBase = PrefsMixin( + I18nMixin(BaseMixin(PolymerElement)) +) + +class AdblockScriptletList extends AdblockScriptletListBase { + static get is() { + return 'adblock-scriptlet-list' + } + + static get template() { + return getTemplate() + } + + static get properties() { + return { + customScriptletsList_: { + type: Array + }, + editingScriptlet_: Scriptlet, + isEditing_: Boolean + } + } + + customScriptletsList_: Scriptlet[] + editingScriptlet_: Scriptlet | null = null + isEditing_: boolean = false + + browserProxy_ = BraveAdblockBrowserProxyImpl.getInstance() + + override ready() { + super.ready() + this.isEditing_ = false + this.browserProxy_.getCustomScriptlets().then((scriptlets) => { + this.customScriptletsList_ = scriptlets + }) + } + + handleAdd_(_: any) { + this.editingScriptlet_ = new Scriptlet() + this.isEditing_ = true + } + + handleEdit_(e: any) { + this.editingScriptlet_ = this.customScriptletsList_[e.model.index] + this.isEditing_ = true + } + + handleDelete_(e: any) { + const messageText = this.i18n('adblockCustomScriptletDeleteConfirmation') + if (!confirm(messageText)) { + return + } + + this.browserProxy_.removeCustomScriptlet( + this.customScriptletsList_[e.model.index].name + ) + this.browserProxy_.getCustomScriptlets().then((scriptlets) => { + this.customScriptletsList_ = scriptlets + }) + } + + scriptletEditorClosed_(_: any) { + this.editingScriptlet_ = null + this.isEditing_ = false + this.browserProxy_.getCustomScriptlets().then((scriptlets) => { + this.customScriptletsList_ = scriptlets + }) + } +} + +customElements.define(AdblockScriptletList.is, AdblockScriptletList) diff --git a/browser/resources/settings/sources.gni b/browser/resources/settings/sources.gni index 41f3df19e0df..bab0ef51bd3f 100644 --- a/browser/resources/settings/sources.gni +++ b/browser/resources/settings/sources.gni @@ -52,6 +52,8 @@ brave_settings_web_component_files = [ "brave_web3_domains_page/brave_web3_domains_page.ts", "default_brave_shields_page/brave_adblock_subpage.ts", "default_brave_shields_page/components/brave_adblock_editor.ts", + "default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts", + "default_brave_shields_page/components/brave_adblock_scriptlet_list.ts", "default_brave_shields_page/components/brave_adblock_subscribe_dropdown.ts", "default_brave_shields_page/default_brave_shields_page.ts", "getting_started_page/getting_started.ts", diff --git a/browser/ui/webui/settings/brave_adblock_handler.cc b/browser/ui/webui/settings/brave_adblock_handler.cc index c2fc7523178a..95c1ccef1dd7 100644 --- a/browser/ui/webui/settings/brave_adblock_handler.cc +++ b/browser/ui/webui/settings/brave_adblock_handler.cc @@ -9,6 +9,7 @@ #include #include +#include "base/feature_list.h" #include "base/functional/bind.h" #include "base/json/values_util.h" #include "base/values.h" @@ -17,6 +18,8 @@ #include "brave/components/brave_shields/content/browser/ad_block_custom_filters_provider.h" #include "brave/components/brave_shields/content/browser/ad_block_service.h" #include "brave/components/brave_shields/core/browser/ad_block_component_service_manager.h" +#include "brave/components/brave_shields/core/browser/ad_block_custom_resource_provider.h" +#include "brave/components/brave_shields/core/common/features.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" @@ -86,6 +89,26 @@ void BraveAdBlockHandler::RegisterMessages() { "brave_adblock.updateCustomFilters", base::BindRepeating(&BraveAdBlockHandler::UpdateCustomFilters, base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "brave_adblock.getCustomScriptlets", + base::BindRepeating(&BraveAdBlockHandler::GetCustomScriptlets, + base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "brave_adblock.addCustomScriptlet", + base::BindRepeating(&BraveAdBlockHandler::AddCustomScriptlet, + base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "brave_adblock.updateCustomScriptlet", + base::BindRepeating(&BraveAdBlockHandler::UpdateCustomScriptlet, + base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "brave_adblock.removeCustomScriptlet", + base::BindRepeating(&BraveAdBlockHandler::RemoveCustomScriptlet, + base::Unretained(this))); } void BraveAdBlockHandler::OnJavascriptAllowed() { @@ -117,8 +140,9 @@ void BraveAdBlockHandler::GetRegionalLists(const base::Value::List& args) { void BraveAdBlockHandler::EnableFilterList(const base::Value::List& args) { DCHECK_EQ(args.size(), 2U); - if (!args[0].is_string() || !args[1].is_bool()) + if (!args[0].is_string() || !args[1].is_bool()) { return; + } std::string uuid = args[0].GetString(); bool enabled = args[1].GetBool(); @@ -162,14 +186,16 @@ void BraveAdBlockHandler::GetCustomFilters(const base::Value::List& args) { void BraveAdBlockHandler::AddSubscription(const base::Value::List& args) { DCHECK_EQ(args.size(), 1U); AllowJavascript(); - if (!args[0].is_string()) + if (!args[0].is_string()) { return; + } std::string subscription_url_string = args[0].GetString(); const GURL subscription_url = GURL(subscription_url_string); - if (!subscription_url.is_valid()) + if (!subscription_url.is_valid()) { return; + } g_brave_browser_process->ad_block_service() ->subscription_service_manager() @@ -182,14 +208,16 @@ void BraveAdBlockHandler::SetSubscriptionEnabled( const base::Value::List& args) { DCHECK_EQ(args.size(), 2U); AllowJavascript(); - if (!args[0].is_string() || !args[1].is_bool()) + if (!args[0].is_string() || !args[1].is_bool()) { return; + } std::string subscription_url_string = args[0].GetString(); bool enabled = args[1].GetBool(); const GURL subscription_url = GURL(subscription_url_string); - if (!subscription_url.is_valid()) + if (!subscription_url.is_valid()) { return; + } g_brave_browser_process->ad_block_service() ->subscription_service_manager() ->EnableSubscription(subscription_url, enabled); @@ -200,8 +228,9 @@ void BraveAdBlockHandler::SetSubscriptionEnabled( void BraveAdBlockHandler::UpdateSubscription(const base::Value::List& args) { DCHECK_EQ(args.size(), 1U); AllowJavascript(); - if (!args[0].is_string()) + if (!args[0].is_string()) { return; + } std::string subscription_url_string = args[0].GetString(); const GURL subscription_url = GURL(subscription_url_string); @@ -217,8 +246,9 @@ void BraveAdBlockHandler::UpdateSubscription(const base::Value::List& args) { void BraveAdBlockHandler::DeleteSubscription(const base::Value::List& args) { DCHECK_EQ(args.size(), 1U); AllowJavascript(); - if (!args[0].is_string()) + if (!args[0].is_string()) { return; + } std::string subscription_url_string = args[0].GetString(); const GURL subscription_url = GURL(subscription_url_string); @@ -235,8 +265,9 @@ void BraveAdBlockHandler::DeleteSubscription(const base::Value::List& args) { void BraveAdBlockHandler::ViewSubscriptionSource( const base::Value::List& args) { DCHECK_EQ(args.size(), 1U); - if (!args[0].is_string()) + if (!args[0].is_string()) { return; + } std::string subscription_url_string = args[0].GetString(); const GURL subscription_url = GURL(subscription_url_string); @@ -253,8 +284,9 @@ void BraveAdBlockHandler::ViewSubscriptionSource( } void BraveAdBlockHandler::UpdateCustomFilters(const base::Value::List& args) { - if (!args[0].is_string()) + if (!args[0].is_string()) { return; + } std::string custom_filters = args[0].GetString(); g_brave_browser_process->ad_block_service() @@ -262,6 +294,58 @@ void BraveAdBlockHandler::UpdateCustomFilters(const base::Value::List& args) { ->UpdateCustomFilters(custom_filters); } +void BraveAdBlockHandler::GetCustomScriptlets(const base::Value::List& args) { + CHECK(base::FeatureList::IsEnabled( + brave_shields::features::kCosmeticFilteringCustomScriptlets)); + CHECK(args.size() == 1u && args[0].is_string()); + AllowJavascript(); + + const auto& custom_resources = g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->GetCustomResources(); + + ResolveJavascriptCallback(args[0].GetString(), custom_resources); +} + +void BraveAdBlockHandler::AddCustomScriptlet(const base::Value::List& args) { + CHECK(base::FeatureList::IsEnabled( + brave_shields::features::kCosmeticFilteringCustomScriptlets)); + CHECK(args.size() == 2u && args[0].is_string() && args[1].is_dict()); + AllowJavascript(); + auto error_code = g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->AddResource(args[1]); + ResolveJavascriptCallback(args[0].GetString(), + base::Value(static_cast(error_code))); +} + +void BraveAdBlockHandler::UpdateCustomScriptlet(const base::Value::List& args) { + CHECK(base::FeatureList::IsEnabled( + brave_shields::features::kCosmeticFilteringCustomScriptlets)); + CHECK(args.size() == 3u && args[0].is_string() && args[1].is_string() && + args[2].is_dict()); + AllowJavascript(); + auto error_code = g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->UpdateResource(args[1].GetString(), args[2]); + ResolveJavascriptCallback(args[0].GetString(), + base::Value(static_cast(error_code))); +} + +void BraveAdBlockHandler::RemoveCustomScriptlet(const base::Value::List& args) { + CHECK(base::FeatureList::IsEnabled( + brave_shields::features::kCosmeticFilteringCustomScriptlets)); + AllowJavascript(); + CHECK(args.size() == 2u && args[0].is_string() && args[1].is_string()); + AllowJavascript(); + auto error_code = g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->RemoveResource(args[1].GetString()); + + ResolveJavascriptCallback(args[0].GetString(), + base::Value(static_cast(error_code))); +} + void BraveAdBlockHandler::RefreshSubscriptionsList() { FireWebUIListener("brave_adblock.onGetListSubscriptions", GetSubscriptions()); } diff --git a/browser/ui/webui/settings/brave_adblock_handler.h b/browser/ui/webui/settings/brave_adblock_handler.h index d4c84ec4deb2..cff4a0dd37f0 100644 --- a/browser/ui/webui/settings/brave_adblock_handler.h +++ b/browser/ui/webui/settings/brave_adblock_handler.h @@ -49,6 +49,10 @@ class BraveAdBlockHandler : public settings::SettingsPageUIHandler, void DeleteSubscription(const base::Value::List& args); void ViewSubscriptionSource(const base::Value::List& args); void UpdateCustomFilters(const base::Value::List& args); + void GetCustomScriptlets(const base::Value::List& args); + void AddCustomScriptlet(const base::Value::List& args); + void UpdateCustomScriptlet(const base::Value::List& args); + void RemoveCustomScriptlet(const base::Value::List& args); void RefreshSubscriptionsList(); diff --git a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc index aaad06fd65e7..c9ea69796ec5 100644 --- a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc +++ b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc @@ -845,6 +845,30 @@ void BraveAddCommonStrings(content::WebUIDataSource* html_source, {"adblockSubscribeUrlUpdateFailed", IDS_BRAVE_ADBLOCK_SUBSCRIBE_URL_UPDATE_FAILED}, {"adblockCustomListsLabel", IDS_BRAVE_ADBLOCK_CUSTOM_LISTS_LABEL}, + {"adblockCustomSciptletsListLabel", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLETS_LIST_LABEL}, + {"adblockAddCustomScriptletButton", + IDS_BRAVE_ADBLOCK_ADD_CUSTOM_SCRIPTLET_BUTTON}, + {"adblockAddCustomScriptletDialogTitle", + IDS_BRAVE_ADBLOCK_ADD_CUSTOM_SCRIPTLET_DIALOG_TITLE}, + {"adblockEditCustomScriptletDialogTitle", + IDS_BRAVE_ADBLOCK_EDIT_CUSTOM_SCRIPTLET_DIALOG_TITLE}, + {"adblockCustomSciptletDialogNameLabel", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DIALOG_NAME_LABEL}, + {"adblockCustomScriptletDialogContentLabel", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DIALOG_CONTENT_LABEL}, + {"adblockCustomScriptletDialogCancelButton", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DIALOG_CANCEL_BUTTON}, + {"adblockCustomScriptletDialogSaveButton", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DIALOG_SAVE_BUTTON}, + {"adblockCustomScriptletDeleteConfirmation", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_DELETE_CONFIRMATION}, + {"adblockEditCustomScriptletAlreadyExistsError", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_ALREADY_EXISTS_ERROR}, + {"adblockEditCustomScriptletInvalidNameError", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_INVALID_NAME_ERROR}, + {"adblockEditCustomScriptletNotFoundError", + IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLET_NOT_FOUND_ERROR}, {"braveShortcutsPage", IDS_SETTINGS_BRAVE_SHORTCUTS_TITLE}, {"shortcutsPageSearchPlaceholder", IDS_SHORTCUTS_PAGE_SEARCH_PLACEHOLDER}, @@ -1003,6 +1027,11 @@ void BraveAddLocalizedStrings(content::WebUIDataSource* html_source, html_source->AddLocalizedStrings(kSessionOnlyToEphemeralStrings); } + html_source->AddBoolean( + "cosmeticFilteringCustomScriptletsEnabled", + base::FeatureList::IsEnabled( + brave_shields::features::kCosmeticFilteringCustomScriptlets)); + // Always disable upstream's side panel align option. // We add our customized option at preferred position. html_source->AddBoolean("showSidePanelOptions", false); diff --git a/components/brave_shields/content/browser/ad_block_service.cc b/components/brave_shields/content/browser/ad_block_service.cc index a823cf99b892..c90c1203c1c8 100644 --- a/components/brave_shields/content/browser/ad_block_service.cc +++ b/components/brave_shields/content/browser/ad_block_service.cc @@ -22,6 +22,7 @@ #include "brave/components/brave_shields/content/browser/ad_block_subscription_service_manager.h" #include "brave/components/brave_shields/core/browser/ad_block_component_filters_provider.h" #include "brave/components/brave_shields/core/browser/ad_block_component_service_manager.h" +#include "brave/components/brave_shields/core/browser/ad_block_custom_resource_provider.h" #include "brave/components/brave_shields/core/browser/ad_block_default_resource_provider.h" #include "brave/components/brave_shields/core/browser/ad_block_filter_list_catalog_provider.h" #include "brave/components/brave_shields/core/browser/ad_block_filters_provider_manager.h" @@ -264,6 +265,11 @@ AdBlockCustomFiltersProvider* AdBlockService::custom_filters_provider() { return custom_filters_provider_.get(); } +AdBlockCustomResourceProvider* AdBlockService::custom_resource_provider() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return custom_resource_provider_.get(); +} + AdBlockSubscriptionServiceManager* AdBlockService::subscription_service_manager() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -306,8 +312,19 @@ AdBlockService::AdBlockService( SetupDiscardPolicy(policy); } - resource_provider_ = std::make_unique( - component_update_service_); + auto default_resource_provider = + std::make_unique( + component_update_service_); + default_resource_provider_ = default_resource_provider.get(); + + if (base::FeatureList::IsEnabled( + features::kCosmeticFilteringCustomScriptlets)) { + custom_resource_provider_ = new AdBlockCustomResourceProvider( + local_state_, std::move(default_resource_provider)); + resource_provider_.reset(custom_resource_provider_.get()); + } else { + resource_provider_ = std::move(default_resource_provider); + } filter_list_catalog_provider_ = std::make_unique( component_update_service_); @@ -401,15 +418,16 @@ void RegisterPrefsForAdBlockService(PrefRegistrySimple* registry) { registry->RegisterBooleanPref( prefs::kAdBlockMobileNotificationsListSettingTouched, false); registry->RegisterStringPref(prefs::kAdBlockCustomFilters, std::string()); + registry->RegisterListPref(prefs::kAdBlockCustomResources); registry->RegisterDictionaryPref(prefs::kAdBlockRegionalFilters); registry->RegisterDictionaryPref(prefs::kAdBlockListSubscriptions); registry->RegisterBooleanPref(prefs::kAdBlockCheckedDefaultRegion, false); registry->RegisterBooleanPref(prefs::kAdBlockCheckedAllDefaultRegions, false); } -AdBlockResourceProvider* AdBlockService::resource_provider() { +AdBlockDefaultResourceProvider* AdBlockService::default_resource_provider() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return resource_provider_.get(); + return default_resource_provider_.get(); } void AdBlockService::UseSourceProviderForTest( diff --git a/components/brave_shields/content/browser/ad_block_service.h b/components/brave_shields/content/browser/ad_block_service.h index f7966d2011ff..a276d6e284da 100644 --- a/components/brave_shields/content/browser/ad_block_service.h +++ b/components/brave_shields/content/browser/ad_block_service.h @@ -47,6 +47,7 @@ class AdBlockComponentFiltersProvider; class AdBlockDefaultResourceProvider; class AdBlockComponentServiceManager; class AdBlockCustomFiltersProvider; +class AdBlockCustomResourceProvider; class AdBlockLocalhostFiltersProvider; class AdBlockFilterListCatalogProvider; class AdBlockSubscriptionServiceManager; @@ -82,8 +83,9 @@ class AdBlockService { std::unique_ptr> filter_set_; raw_ptr adblock_engine_; - raw_ptr filters_provider_; // not owned - raw_ptr resource_provider_; // not owned + raw_ptr filters_provider_; // not owned + raw_ptr resource_provider_; // not owned + raw_ptr custom_resource_provider_; // not owned scoped_refptr task_runner_; bool is_filter_provider_manager_; @@ -123,6 +125,7 @@ class AdBlockService { AdBlockComponentServiceManager* component_service_manager(); AdBlockSubscriptionServiceManager* subscription_service_manager(); AdBlockCustomFiltersProvider* custom_filters_provider(); + AdBlockCustomResourceProvider* custom_resource_provider(); void EnableTag(const std::string& tag, bool enabled); void AddUserCosmeticFilter(const std::string& filter); @@ -149,7 +152,7 @@ class AdBlockService { static std::string g_ad_block_dat_file_version_; - AdBlockResourceProvider* resource_provider(); + AdBlockDefaultResourceProvider* default_resource_provider(); AdBlockComponentFiltersProvider* default_filters_provider() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return default_filters_provider_.get(); @@ -181,8 +184,12 @@ class AdBlockService { AdBlockListP3A list_p3a_; - std::unique_ptr resource_provider_ + std::unique_ptr resource_provider_ GUARDED_BY_CONTEXT(sequence_checker_); + raw_ptr default_resource_provider_ + GUARDED_BY_CONTEXT(sequence_checker_) = nullptr; + raw_ptr custom_resource_provider_ + GUARDED_BY_CONTEXT(sequence_checker_) = nullptr; std::unique_ptr custom_filters_provider_ GUARDED_BY_CONTEXT(sequence_checker_); std::unique_ptr localhost_filters_provider_ diff --git a/components/brave_shields/core/browser/BUILD.gn b/components/brave_shields/core/browser/BUILD.gn index efde19effef2..b5049455c628 100644 --- a/components/brave_shields/core/browser/BUILD.gn +++ b/components/brave_shields/core/browser/BUILD.gn @@ -11,6 +11,8 @@ static_library("browser") { "ad_block_component_installer.h", "ad_block_component_service_manager.cc", "ad_block_component_service_manager.h", + "ad_block_custom_resource_provider.cc", + "ad_block_custom_resource_provider.h", "ad_block_default_resource_provider.cc", "ad_block_default_resource_provider.h", "ad_block_filter_list_catalog_provider.cc", diff --git a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc new file mode 100644 index 000000000000..a43f0717ea46 --- /dev/null +++ b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc @@ -0,0 +1,209 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// 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 https://mozilla.org/MPL/2.0/. + +#include "brave/components/brave_shields/core/browser/ad_block_custom_resource_provider.h" + +#include +#include + +#include "base/feature_list.h" +#include "base/json/json_writer.h" +#include "base/ranges/algorithm.h" +#include "base/strings/strcat.h" +#include "base/strings/string_util.h" +#include "brave/components/brave_shields/core/common/features.h" +#include "brave/components/brave_shields/core/common/pref_names.h" +#include "components/prefs/pref_service.h" +#include "components/prefs/scoped_user_pref_update.h" + +namespace brave_shields { + +namespace { + +constexpr const char kNameField[] = "name"; +constexpr const char kContentField[] = "content"; +constexpr const char kKindField[] = "kind"; +constexpr const char kMimeField[] = "mime"; + +bool IsValidResource(const base::Value& resource) { + if (!resource.is_dict() || !resource.GetDict().FindString(kNameField) || + !resource.GetDict().FindString(kContentField)) { + // Invalid resource structure. + return false; + } + + const auto* name = resource.GetDict().FindString(kNameField); + if (name->empty() || !base::IsStringASCII(*name)) { + return false; + } + + auto* kind = resource.GetDict().FindDict(kKindField); + if (!kind || !kind->FindString(kMimeField)) { + return false; + } + return true; +} + +const std::string& GetResourceName(const base::Value& resource) { + DCHECK(IsValidResource(resource)); + return *resource.GetDict().FindString(kNameField); +} + +base::Value::List::iterator FindResource(base::Value::List& resources, + const std::string& name) { + return base::ranges::find_if(resources, [name](const base::Value& v) { + CHECK(IsValidResource(v)); + return GetResourceName(v) == name; + }); +} + +std::string_view JsonListStr(std::string_view json) { + const auto start = json.find('['); + const auto end = json.rfind(']'); + if (start == std::string_view::npos || end == std::string_view::npos || + start >= end) { + return std::string_view(); + } + return json.substr(start + 1, end - start - 1); +} + +std::string MergeResources(const std::string& default_resources, + const std::string& custom_resources) { + auto default_resources_str = JsonListStr(default_resources); + if (default_resources_str.empty()) { + return custom_resources; + } + auto custom_resources_str = JsonListStr(custom_resources); + if (custom_resources_str.empty()) { + return default_resources; + } + return base::StrCat( + {"[", default_resources_str, ",", custom_resources_str, "]"}); +} + +base::Value FixName(const base::Value& resource) { + base::Value result = resource.Clone(); + + std::string* name = result.GetDict().FindString(kNameField); + *name = base::ToLowerASCII(*name); + if (!base::EndsWith(*name, ".js")) { + name->append(".js"); + } + + return result; +} + +} // namespace + +AdBlockCustomResourceProvider::AdBlockCustomResourceProvider( + PrefService* local_state, + std::unique_ptr default_resource_provider) + : local_state_(local_state), + default_resource_provider_(std::move(default_resource_provider)) { + CHECK(base::FeatureList::IsEnabled( + brave_shields::features::kCosmeticFilteringCustomScriptlets)); + CHECK(default_resource_provider_); + default_resource_provider_->AddObserver(this); +} + +AdBlockCustomResourceProvider::~AdBlockCustomResourceProvider() { + default_resource_provider_->RemoveObserver(this); +} + +const base::Value& AdBlockCustomResourceProvider::GetCustomResources() { + return local_state_->GetValue(prefs::kAdBlockCustomResources); +} + +AdBlockCustomResourceProvider::ErrorCode +AdBlockCustomResourceProvider::AddResource(const base::Value& resource) { + if (!IsValidResource(resource)) { + return ErrorCode::kInvalid; + } + + ScopedListPrefUpdate update(local_state_, prefs::kAdBlockCustomResources); + if (FindResource(update.Get(), GetResourceName(resource)) != update->end()) { + return ErrorCode::kAlreadyExists; + } + update->Append(FixName(resource)); + ReloadResourcesAndNotify(); + return ErrorCode::kOk; +} + +AdBlockCustomResourceProvider::ErrorCode +AdBlockCustomResourceProvider::UpdateResource(const std::string& old_name, + const base::Value& resource) { + if (!IsValidResource(resource)) { + return ErrorCode::kInvalid; + } + ScopedListPrefUpdate update(local_state_, prefs::kAdBlockCustomResources); + auto updated_resource = FindResource(update.Get(), old_name); + if (updated_resource == update->end()) { + return ErrorCode::kNotFound; + } + const std::string& new_name = GetResourceName(resource); + if (old_name != new_name) { + if (FindResource(update.Get(), new_name) != update->end()) { + return ErrorCode::kNotFound; + } + } + + *updated_resource = FixName(resource); + ReloadResourcesAndNotify(); + return ErrorCode::kOk; +} + +AdBlockCustomResourceProvider::ErrorCode +AdBlockCustomResourceProvider::RemoveResource( + const std::string& resource_name) { + ScopedListPrefUpdate update(local_state_, prefs::kAdBlockCustomResources); + auto updated_resource = FindResource(update.Get(), resource_name); + update->erase(updated_resource); + ReloadResourcesAndNotify(); + return ErrorCode::kOk; +} + +void AdBlockCustomResourceProvider::LoadResources( + base::OnceCallback on_load) { + default_resource_provider_->LoadResources( + base::BindOnce(&AdBlockCustomResourceProvider::OnDefaultResourcesLoaded, + weak_ptr_factory_.GetWeakPtr(), std::move(on_load))); +} + +void AdBlockCustomResourceProvider::OnResourcesLoaded( + const std::string& resources_json) { + NotifyResourcesLoaded( + MergeResources(resources_json, GetCustomResourcesJson())); +} + +std::string AdBlockCustomResourceProvider::GetCustomResourcesJson() { + return base::WriteJson(GetCustomResources()).value_or(std::string()); +} + +void AdBlockCustomResourceProvider::OnDefaultResourcesLoaded( + base::OnceCallback on_load, + const std::string& resources_json) { + const auto& custom_resources = + local_state_->GetList(prefs::kAdBlockCustomResources); + std::string custom_resources_json = + base::WriteJson(custom_resources).value_or(std::string()); + + if (custom_resources.empty()) { + std::move(on_load).Run(resources_json); + } else if (resources_json.empty()) { + std::move(on_load).Run(custom_resources_json); + } else { + // Merge. + std::move(on_load).Run( + MergeResources(resources_json, custom_resources_json)); + } +} + +void AdBlockCustomResourceProvider::ReloadResourcesAndNotify() { + LoadResources( + base::BindOnce(&AdBlockCustomResourceProvider::NotifyResourcesLoaded, + weak_ptr_factory_.GetWeakPtr())); +} + +} // namespace brave_shields diff --git a/components/brave_shields/core/browser/ad_block_custom_resource_provider.h b/components/brave_shields/core/browser/ad_block_custom_resource_provider.h new file mode 100644 index 000000000000..5f5a2fae0097 --- /dev/null +++ b/components/brave_shields/core/browser/ad_block_custom_resource_provider.h @@ -0,0 +1,64 @@ +// Copyright (c) 2024 The Brave Authors. All rights reserved. +// 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 https://mozilla.org/MPL/2.0/. + +#ifndef BRAVE_COMPONENTS_BRAVE_SHIELDS_CORE_BROWSER_AD_BLOCK_CUSTOM_RESOURCE_PROVIDER_H_ +#define BRAVE_COMPONENTS_BRAVE_SHIELDS_CORE_BROWSER_AD_BLOCK_CUSTOM_RESOURCE_PROVIDER_H_ + +#include + +#include "base/functional/callback.h" +#include "base/memory/raw_ptr.h" +#include "base/values.h" +#include "brave/components/brave_shields/core/browser/ad_block_resource_provider.h" + +class PrefService; + +namespace brave_shields { + +class AdBlockCustomResourceProvider + : public AdBlockResourceProvider, + private AdBlockResourceProvider::Observer { + public: + enum class ErrorCode { + kOk, + kInvalid, + kAlreadyExists, + kNotFound, + }; + + AdBlockCustomResourceProvider( + PrefService* local_state, + std::unique_ptr default_resource_provider); + ~AdBlockCustomResourceProvider() override; + + const base::Value& GetCustomResources(); + ErrorCode AddResource(const base::Value& resource); + ErrorCode UpdateResource(const std::string& name, + const base::Value& resource); + ErrorCode RemoveResource(const std::string& resource_name); + + // AdBlockResourceProvider: + void LoadResources( + base::OnceCallback) override; + + private: + // AdBlockResourceProvider::Observer: + void OnResourcesLoaded(const std::string& resources_json) override; + + std::string GetCustomResourcesJson(); + void OnDefaultResourcesLoaded( + base::OnceCallback on_load, + const std::string& resources_json); + void ReloadResourcesAndNotify(); + + const raw_ptr local_state_ = nullptr; + std::unique_ptr default_resource_provider_ = nullptr; + + base::WeakPtrFactory weak_ptr_factory_{this}; +}; + +} // namespace brave_shields + +#endif // BRAVE_COMPONENTS_BRAVE_SHIELDS_CORE_BROWSER_AD_BLOCK_CUSTOM_RESOURCE_PROVIDER_H_ diff --git a/components/brave_shields/core/browser/ad_block_default_resource_provider.cc b/components/brave_shields/core/browser/ad_block_default_resource_provider.cc index a48d48497ad0..e2e649c4418e 100644 --- a/components/brave_shields/core/browser/ad_block_default_resource_provider.cc +++ b/components/brave_shields/core/browser/ad_block_default_resource_provider.cc @@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/task/thread_pool.h" +#include "brave/components/brave_component_updater/browser/dat_file_util.h" #include "brave/components/brave_shields/core/browser/ad_block_component_installer.h" namespace { @@ -59,7 +60,7 @@ void AdBlockDefaultResourceProvider::OnComponentReady( FROM_HERE, {base::MayBlock()}, base::BindOnce(&brave_component_updater::GetDATFileAsString, resources_path), - base::BindOnce(&AdBlockDefaultResourceProvider::OnResourcesLoaded, + base::BindOnce(&AdBlockDefaultResourceProvider::NotifyResourcesLoaded, weak_factory_.GetWeakPtr())); } diff --git a/components/brave_shields/core/browser/ad_block_resource_provider.cc b/components/brave_shields/core/browser/ad_block_resource_provider.cc index 0f08aaee3510..7c590a5c6812 100644 --- a/components/brave_shields/core/browser/ad_block_resource_provider.cc +++ b/components/brave_shields/core/browser/ad_block_resource_provider.cc @@ -27,7 +27,7 @@ void AdBlockResourceProvider::RemoveObserver( } } -void AdBlockResourceProvider::OnResourcesLoaded( +void AdBlockResourceProvider::NotifyResourcesLoaded( const std::string& resources_json) { for (auto& observer : observers_) { observer.OnResourcesLoaded(resources_json); diff --git a/components/brave_shields/core/browser/ad_block_resource_provider.h b/components/brave_shields/core/browser/ad_block_resource_provider.h index c478ad19d238..0d5d9550ece5 100644 --- a/components/brave_shields/core/browser/ad_block_resource_provider.h +++ b/components/brave_shields/core/browser/ad_block_resource_provider.h @@ -36,7 +36,7 @@ class AdBlockResourceProvider { base::OnceCallback) = 0; protected: - void OnResourcesLoaded(const std::string& resources_json); + void NotifyResourcesLoaded(const std::string& resources_json); private: base::ObserverList observers_; diff --git a/components/brave_shields/core/common/features.cc b/components/brave_shields/core/common/features.cc index 155a271f32d3..e1f544e55913 100644 --- a/components/brave_shields/core/common/features.cc +++ b/components/brave_shields/core/common/features.cc @@ -139,6 +139,10 @@ BASE_FEATURE(kCosmeticFilteringJsPerformance, "CosmeticFilteringJsPerformance", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kCosmeticFilteringCustomScriptlets, + "CostemicFilteringCustomScriptlets", + base::FEATURE_DISABLED_BY_DEFAULT); + constexpr base::FeatureParam kComponentUpdateCheckIntervalMins{ &kAdBlockDefaultResourceUpdateInterval, "update_interval_mins", 100}; diff --git a/components/brave_shields/core/common/features.h b/components/brave_shields/core/common/features.h index 0714290d1558..37d04cd46e22 100644 --- a/components/brave_shields/core/common/features.h +++ b/components/brave_shields/core/common/features.h @@ -39,6 +39,7 @@ BASE_DECLARE_FEATURE(kCosmeticFilteringExtraPerfMetrics); BASE_DECLARE_FEATURE(kCosmeticFilteringJsPerformance); BASE_DECLARE_FEATURE(kCosmeticFilteringSyncLoad); BASE_DECLARE_FEATURE(kBlockAllCookiesToggle); +BASE_DECLARE_FEATURE(kCosmeticFilteringCustomScriptlets); extern const base::FeatureParam kComponentUpdateCheckIntervalMins; extern const base::FeatureParam kCosmeticFilteringSubFrameFirstSelectorsPollingDelayMs; diff --git a/components/brave_shields/core/common/pref_names.h b/components/brave_shields/core/common/pref_names.h index bbe3a4a999a3..1613b8ffe718 100644 --- a/components/brave_shields/core/common/pref_names.h +++ b/components/brave_shields/core/common/pref_names.h @@ -21,6 +21,8 @@ inline constexpr char kAdBlockMobileNotificationsListSettingTouched[] = "brave.ad_block.mobile_notifications_list_setting_touched"; inline constexpr char kAdBlockCustomFilters[] = "brave.ad_block.custom_filters"; +inline constexpr char kAdBlockCustomResources[] = + "brave.ad_block.custom_resources"; inline constexpr char kAdBlockRegionalFilters[] = "brave.ad_block.regional_filters"; inline constexpr char kAdBlockListSubscriptions[] = From 7108965d40ba3d43e071d33da8e4196ebebdffa4 Mon Sep 17 00:00:00 2001 From: boocmp Date: Sun, 20 Oct 2024 17:04:49 +0700 Subject: [PATCH 02/21] Added tests. --- .../ad_block_custom_resources_browsertest.cc | 218 ++++++++++++++++++ .../ad_block_service_browsertest.cc | 2 +- .../ad_block_service_browsertest.h | 2 +- .../brave_adblock_subpage.ts | 1 + .../brave_adblock_scriptlet_editor.html | 4 +- .../brave_adblock_scriptlet_editor.ts | 34 ++- .../brave_adblock_scriptlet_list.html | 8 +- .../brave_adblock_scriptlet_list.ts | 16 +- test/BUILD.gn | 1 + 9 files changed, 270 insertions(+), 16 deletions(-) create mode 100644 browser/brave_shields/ad_block_custom_resources_browsertest.cc diff --git a/browser/brave_shields/ad_block_custom_resources_browsertest.cc b/browser/brave_shields/ad_block_custom_resources_browsertest.cc new file mode 100644 index 000000000000..ae36928bb455 --- /dev/null +++ b/browser/brave_shields/ad_block_custom_resources_browsertest.cc @@ -0,0 +1,218 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * 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 https://mozilla.org/MPL/2.0/. */ + +#include "base/base64.h" +#include "base/test/scoped_feature_list.h" +#include "base/values.h" +#include "brave/browser/brave_browser_process.h" +#include "brave/browser/brave_shields/ad_block_service_browsertest.h" +#include "brave/browser/ui/webui/brave_settings_ui.h" +#include "brave/components/brave_shields/content/browser/ad_block_service.h" +#include "brave/components/brave_shields/core/browser/ad_block_custom_resource_provider.h" +#include "brave/components/brave_shields/core/common/features.h" +#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "url/gurl.h" + +#if BUILDFLAG(IS_ANDROID) +#include "chrome/test/base/android/android_browser_test.h" +#else +#include "chrome/test/base/in_process_browser_test.h" +#endif + +namespace { + +void AwaitElement(content::WebContents* web_contents, + const std::string& root, + const std::string& id) { + constexpr const char kScript[] = R"js( + (async () => { + while (!window.testing[$1].getElementById($2)) { + await new Promise(r => setTimeout(r, 10)); + } + return true; + })(); + )js"; + EXPECT_TRUE( + content::EvalJs(web_contents, content::JsReplace(kScript, root, id)) + .ExtractBool()); +} + +bool ClickAddCustomScriptlet(content::WebContents* web_contents) { + AwaitElement(web_contents, "adblockScriptletList", "add-custom-scriptlet"); + return EvalJs(web_contents, + "window.testing.adblockScriptletList.getElementById('add-" + "custom-scriptlet').click()") + .value.is_none(); +} + +bool SetCustomScriptletValue(content::WebContents* web_contents, + const std::string& id, + const std::string& value) { + AwaitElement(web_contents, "adblockScriptletEditor", id); + constexpr const char kSetValue[] = R"js( + (function() { + const e = window.testing.adblockScriptletEditor.getElementById($1); + e.value = $2; + const event = new Event('input', {bubbles: true}); + event.simulated = true; + return e.dispatchEvent(event); + })(); + )js"; + return EvalJs(web_contents, content::JsReplace(kSetValue, id, value)) + .value.GetBool(); +} + +bool SetCustomScriptletName(content::WebContents* web_contents, + const std::string& name) { + return SetCustomScriptletValue(web_contents, "scriptlet-name", name); +} + +bool SetCustomScriptletContent(content::WebContents* web_contents, + const std::string& content) { + return SetCustomScriptletValue(web_contents, "scriptlet-content", content); +} + +std::string GetCustomScriptletValue(content::WebContents* web_contents, + const std::string& id) { + AwaitElement(web_contents, "adblockScriptletEditor", id); + return EvalJs(web_contents, + "window.testing.adblockScriptletEditor.getElementById('" + id + + "').value") + .value.GetString(); +} + +std::string GetCustomScriptletName(content::WebContents* web_contents) { + return GetCustomScriptletValue(web_contents, "scriptlet-name"); +} + +std::string GetCustomScriptletContent(content::WebContents* web_contents) { + return GetCustomScriptletValue(web_contents, "scriptlet-content"); +} + +bool ClickSaveCustomScriptlet(content::WebContents* web_contents) { + AwaitElement(web_contents, "adblockScriptletEditor", "save"); + return EvalJs(web_contents, + "window.testing.adblockScriptletEditor.getElementById('save')." + "click()") + .value.is_none(); +} + +bool ClickCustomScriplet(content::WebContents* web_contents, + const std::string& name, + const std::string& button) { + AwaitElement(web_contents, "adblockScriptletList", name); + constexpr const char kClick[] = R"js( + (function() { + const e = window.testing.adblockScriptletList.getElementById($1); + const b = e.querySelector($2); + b.click(); + })(); + )js"; + return EvalJs(web_contents, content::JsReplace(kClick, name, "#" + button)) + .value.is_none(); +} + +} // namespace + +class AdblockCustomResourcesTest : public AdBlockServiceTest { + public: + AdblockCustomResourcesTest() { + feature_list_.InitAndEnableFeature( + brave_shields::features::kCosmeticFilteringCustomScriptlets); + BraveSettingsUI::ShouldExposeElementsForTesting() = true; + } + + ~AdblockCustomResourcesTest() override { + BraveSettingsUI::ShouldExposeElementsForTesting() = true; + } + + void SaveCustomScriptlet(const std::string& name, const std::string& value) { + ASSERT_EQ(GURL("chrome://settings/shields/filters"), + web_contents()->GetLastCommittedURL()); + + ASSERT_TRUE(SetCustomScriptletContent(web_contents(), value)); + ASSERT_TRUE(SetCustomScriptletName(web_contents(), name)); + ASSERT_TRUE(ClickSaveCustomScriptlet(web_contents())); + } + + void CheckCustomScriptlet(const base::Value& custom_scriptlet, + const std::string& name, + const std::string& content) { + ASSERT_TRUE(custom_scriptlet.is_dict()); + EXPECT_EQ(name, *custom_scriptlet.GetDict().FindString("name")); + EXPECT_EQ(base::Base64Encode(content), + *custom_scriptlet.GetDict().FindString("content")); + EXPECT_EQ("application/javascript", + *custom_scriptlet.GetDict().FindStringByDottedPath("kind.mime")); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; + +IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, AddEditRemoveScriptlet) { + NavigateToURL(GURL("brave://settings/shields/filters")); + + constexpr const char kContent[] = "window.test = 'custom-script'"; + + ASSERT_TRUE(ClickAddCustomScriptlet(web_contents())); + SaveCustomScriptlet("custom-script", kContent); + + auto* ad_block_service = g_brave_browser_process->ad_block_service(); + + { + const auto& custom_resources = + ad_block_service->custom_resource_provider()->GetCustomResources(); + ASSERT_TRUE(custom_resources.is_list()); + ASSERT_EQ(1u, custom_resources.GetList().size()); + CheckCustomScriptlet(custom_resources.GetList().front(), + "brave-custom-script.js", kContent); + } + + constexpr const char kEditedContent[] = "window.test = 'edited'"; + + ASSERT_TRUE( + ClickCustomScriplet(web_contents(), "brave-custom-script.js", "edit")); + + EXPECT_EQ("brave-custom-script.js", GetCustomScriptletName(web_contents())); + EXPECT_EQ(kContent, GetCustomScriptletContent(web_contents())); + SaveCustomScriptlet("custom-script-edited", kEditedContent); + { + const auto& custom_resources = + ad_block_service->custom_resource_provider()->GetCustomResources(); + ASSERT_TRUE(custom_resources.is_list()); + ASSERT_EQ(1u, custom_resources.GetList().size()); + CheckCustomScriptlet(custom_resources.GetList().front(), + "brave-custom-script-edited.js", kEditedContent); + } + + ASSERT_TRUE(ClickCustomScriplet(web_contents(), + "brave-custom-script-edited.js", "delete")); + { + const auto& custom_resources = + ad_block_service->custom_resource_provider()->GetCustomResources(); + ASSERT_TRUE(custom_resources.is_list()); + ASSERT_TRUE(custom_resources.GetList().empty()); + } +} + +IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, ExecCustomScriptlet) { + NavigateToURL(GURL("brave://settings/shields/filters")); + + constexpr const char kContent[] = "window.test = 'custom-script'"; + + ASSERT_TRUE(ClickAddCustomScriptlet(web_contents())); + SaveCustomScriptlet("custom-script", kContent); + + UpdateAdBlockInstanceWithRules("a.com##+js(brave-custom-script)"); + + GURL tab_url = + embedded_test_server()->GetURL("a.com", "/cosmetic_filtering.html"); + NavigateToURL(tab_url); + + EXPECT_EQ("custom-script", EvalJs(web_contents(), "window.test")); +} diff --git a/browser/brave_shields/ad_block_service_browsertest.cc b/browser/brave_shields/ad_block_service_browsertest.cc index 598446109f5f..494f3f1fd8b7 100644 --- a/browser/brave_shields/ad_block_service_browsertest.cc +++ b/browser/brave_shields/ad_block_service_browsertest.cc @@ -312,7 +312,7 @@ base::FilePath AdBlockServiceTest::GetTestDataDir() { return base::PathService::CheckedGet(brave::DIR_TEST_DATA); } -void AdBlockServiceTest::NavigateToURL(GURL url) { +void AdBlockServiceTest::NavigateToURL(const GURL& url) { content::NavigateToURLBlockUntilNavigationsComplete(web_contents(), url, 1, true); } diff --git a/browser/brave_shields/ad_block_service_browsertest.h b/browser/brave_shields/ad_block_service_browsertest.h index f5aa04122996..f33e1dda9118 100644 --- a/browser/brave_shields/ad_block_service_browsertest.h +++ b/browser/brave_shields/ad_block_service_browsertest.h @@ -53,7 +53,7 @@ class AdBlockServiceTest : public PlatformBrowserTest { void AssertTagExists(const std::string& tag, bool expected_exists) const; void InitEmbeddedTestServer(); base::FilePath GetTestDataDir(); - void NavigateToURL(GURL url); + void NavigateToURL(const GURL& url); void SetDefaultComponentIdAndBase64PublicKeyForTest(); void SetRegionalComponentIdAndBase64PublicKeyForTest(); void InstallComponent( diff --git a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts index 773527887f50..fa75171a2ebf 100644 --- a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts +++ b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts @@ -9,6 +9,7 @@ import 'chrome://resources/cr_elements/cr_button/cr_button.js'; import 'chrome://resources/cr_elements/icons.html.js'; import './components/brave_adblock_subscribe_dropdown.js'; import './components/brave_adblock_editor.js'; +import './components/brave_adblock_scriptlet_list.js'; import {PrefsMixin} from '/shared/settings/prefs/prefs_mixin.js'; import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js'; diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html index f50c568fc443..7729afcb5a79 100644 --- a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html @@ -1,4 +1,4 @@ - @@ -36,7 +41,8 @@
$i18n{adblockCustomScriptletDialogContentLabel}
-
diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts index 4baef4fa897b..c2268fef28ee 100644 --- a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts @@ -128,7 +128,7 @@ class AdblockScriptletEditor extends AdblockScriptletEditorBase { validateName_() { this.scriptlet.name = this.scriptlet.name.toLowerCase() - if (!/^a-zA-Z0-9-_.]*$/.test(this.scriptlet.name)) { + if (!/^[a-zA-Z0-9-_.]*$/.test(this.scriptlet.name)) { this.updateError(ErrorCode.kInvalidName) } else { this.updateError(ErrorCode.kOK) diff --git a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc index 043e19293c84..867b1cc7babe 100644 --- a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc +++ b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc @@ -27,9 +27,15 @@ constexpr const char kContentField[] = "content"; constexpr const char kMimeField[] = "kind.mime"; constexpr const char kAppJs[] = "application/javascript"; +bool HasName(const base::Value& resource) { + return resource.is_dict() && !!resource.GetDict().FindString(kNameField); +} + bool IsValidResource(const base::Value& resource) { - if (!resource.is_dict() || !resource.GetDict().FindString(kNameField) || - !resource.GetDict().FindString(kContentField)) { + if (!HasName(resource)) { + return false; + } + if (!resource.GetDict().FindString(kContentField)) { // Invalid resource structure. return false; } @@ -57,16 +63,15 @@ bool IsValidResource(const base::Value& resource) { } const std::string& GetResourceName(const base::Value& resource) { - DCHECK(IsValidResource(resource)); + if (!HasName(resource)) { + return base::EmptyString(); + } return *resource.GetDict().FindString(kNameField); } base::Value::List::iterator FindResource(base::Value::List& resources, const std::string& name) { return base::ranges::find_if(resources, [name](const base::Value& v) { - if (!IsValidResource(v)) { - return false; - } return GetResourceName(v) == name; }); } From 774ff6a6c07ae6380deecfc64795f15455fed62a Mon Sep 17 00:00:00 2001 From: boocmp Date: Thu, 24 Oct 2024 21:22:14 +0700 Subject: [PATCH 07/21] Changed regex for name checking. --- .../components/brave_adblock_scriptlet_editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts index c2268fef28ee..f9ed293ac9e3 100644 --- a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.ts @@ -128,7 +128,7 @@ class AdblockScriptletEditor extends AdblockScriptletEditorBase { validateName_() { this.scriptlet.name = this.scriptlet.name.toLowerCase() - if (!/^[a-zA-Z0-9-_.]*$/.test(this.scriptlet.name)) { + if (!/^[a-zA-Z0-9-_.]+$/.test(this.scriptlet.name)) { this.updateError(ErrorCode.kInvalidName) } else { this.updateError(ErrorCode.kOK) From cf2967f1609b38d12af117a1ee5d4b9610ca012f Mon Sep 17 00:00:00 2001 From: boocmp Date: Thu, 24 Oct 2024 21:31:22 +0700 Subject: [PATCH 08/21] Highlight placeholder with red color. --- .../components/brave_adblock_scriptlet_editor.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html index 83924353c01b..f52d49559923 100644 --- a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html @@ -26,8 +26,8 @@ textarea::placeholder { white-space: pre-line; + color: var(--leo-color-systemfeedback-error-text); } - From 626c6fc798f0c032ab43074cca065cf1f47b2abd Mon Sep 17 00:00:00 2001 From: boocmp Date: Thu, 31 Oct 2024 12:29:13 +0700 Subject: [PATCH 09/21] Removed unnecessary AllowJavascript. base::StartsWith replaced with std::string::starts_with. Added test. --- .../ad_block_custom_resources_browsertest.cc | 29 +++++++++++++++++++ .../webui/settings/brave_adblock_handler.cc | 1 - .../ad_block_custom_resource_provider.cc | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/browser/brave_shields/ad_block_custom_resources_browsertest.cc b/browser/brave_shields/ad_block_custom_resources_browsertest.cc index ae36928bb455..ec0bcc3c0325 100644 --- a/browser/brave_shields/ad_block_custom_resources_browsertest.cc +++ b/browser/brave_shields/ad_block_custom_resources_browsertest.cc @@ -216,3 +216,32 @@ IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, ExecCustomScriptlet) { EXPECT_EQ("custom-script", EvalJs(web_contents(), "window.test")); } + +IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, NameConflicts) { + constexpr const char kBraveFix[] = "window.test = 'default-script'"; + constexpr const char kBraveFixResource[] = R"json( + [{ + "name": "brave-fix.js", + "kind": { "mime": "application/javascript" }, + "content": "$1" + }] + )json"; + + UpdateAdBlockResources(base::ReplaceStringPlaceholders( + kBraveFixResource, {base::Base64Encode(kBraveFix)}, nullptr)); + + NavigateToURL(GURL("brave://settings/shields/filters")); + + constexpr const char kContent[] = "window.test = 'custom-script'"; + + ASSERT_TRUE(ClickAddCustomScriptlet(web_contents())); + SaveCustomScriptlet("brave-fix", kContent); + + UpdateAdBlockInstanceWithRules("a.com##+js(brave-fix)"); + + GURL tab_url = + embedded_test_server()->GetURL("a.com", "/cosmetic_filtering.html"); + NavigateToURL(tab_url); + + EXPECT_EQ("default-script", EvalJs(web_contents(), "window.test")); +} diff --git a/browser/ui/webui/settings/brave_adblock_handler.cc b/browser/ui/webui/settings/brave_adblock_handler.cc index 95c1ccef1dd7..92d7904c2064 100644 --- a/browser/ui/webui/settings/brave_adblock_handler.cc +++ b/browser/ui/webui/settings/brave_adblock_handler.cc @@ -335,7 +335,6 @@ void BraveAdBlockHandler::UpdateCustomScriptlet(const base::Value::List& args) { void BraveAdBlockHandler::RemoveCustomScriptlet(const base::Value::List& args) { CHECK(base::FeatureList::IsEnabled( brave_shields::features::kCosmeticFilteringCustomScriptlets)); - AllowJavascript(); CHECK(args.size() == 2u && args[0].is_string() && args[1].is_string()); AllowJavascript(); auto error_code = g_brave_browser_process->ad_block_service() diff --git a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc index 867b1cc7babe..bb672077cfb7 100644 --- a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc +++ b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc @@ -52,7 +52,7 @@ bool IsValidResource(const base::Value& resource) { if (*mime == kAppJs) { // Resource is a scriptlet: - if (!base::StartsWith(*name, "brave-") || !base::EndsWith(*name, ".js")) { + if (!name->starts_with("brave-") || !name->ends_with(".js")) { return false; } } else { From 1aa3bea8d924d6b292acd2ecbc7f563a73ca7224 Mon Sep 17 00:00:00 2001 From: boocmp Date: Thu, 7 Nov 2024 22:26:14 +0700 Subject: [PATCH 10/21] Using leveldb instead of local_state to store the custom scriplets. --- .../ad_block_custom_resources_browsertest.cc | 33 +-- .../brave_adblock_scriptlet_editor.html | 3 +- .../webui/settings/brave_adblock_handler.cc | 59 ++++-- .../ui/webui/settings/brave_adblock_handler.h | 9 +- .../content/browser/ad_block_service.cc | 3 +- .../brave_shields/core/browser/BUILD.gn | 1 + .../ad_block_custom_resource_provider.cc | 195 ++++++++++++------ .../ad_block_custom_resource_provider.h | 44 +++- .../brave_shields/core/common/pref_names.h | 2 - 9 files changed, 234 insertions(+), 115 deletions(-) diff --git a/browser/brave_shields/ad_block_custom_resources_browsertest.cc b/browser/brave_shields/ad_block_custom_resources_browsertest.cc index ec0bcc3c0325..0e0f6f7eebac 100644 --- a/browser/brave_shields/ad_block_custom_resources_browsertest.cc +++ b/browser/brave_shields/ad_block_custom_resources_browsertest.cc @@ -4,6 +4,7 @@ * You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "base/base64.h" +#include "base/test/bind.h" #include "base/test/scoped_feature_list.h" #include "base/values.h" #include "brave/browser/brave_browser_process.h" @@ -13,16 +14,11 @@ #include "brave/components/brave_shields/core/browser/ad_block_custom_resource_provider.h" #include "brave/components/brave_shields/core/common/features.h" #include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" +#include "chrome/test/base/in_process_browser_test.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "url/gurl.h" -#if BUILDFLAG(IS_ANDROID) -#include "chrome/test/base/android/android_browser_test.h" -#else -#include "chrome/test/base/in_process_browser_test.h" -#endif - namespace { void AwaitElement(content::WebContents* web_contents, @@ -150,6 +146,20 @@ class AdblockCustomResourcesTest : public AdBlockServiceTest { *custom_scriptlet.GetDict().FindStringByDottedPath("kind.mime")); } + base::Value GetCustomResources() { + base::RunLoop loop; + base::Value result; + g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->GetCustomResources( + base::BindLambdaForTesting([&loop, &result](base::Value resources) { + result = std::move(resources); + loop.Quit(); + })); + loop.Run(); + return result; + } + private: base::test::ScopedFeatureList feature_list_; }; @@ -162,11 +172,8 @@ IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, AddEditRemoveScriptlet) { ASSERT_TRUE(ClickAddCustomScriptlet(web_contents())); SaveCustomScriptlet("custom-script", kContent); - auto* ad_block_service = g_brave_browser_process->ad_block_service(); - { - const auto& custom_resources = - ad_block_service->custom_resource_provider()->GetCustomResources(); + const auto& custom_resources = GetCustomResources(); ASSERT_TRUE(custom_resources.is_list()); ASSERT_EQ(1u, custom_resources.GetList().size()); CheckCustomScriptlet(custom_resources.GetList().front(), @@ -182,8 +189,7 @@ IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, AddEditRemoveScriptlet) { EXPECT_EQ(kContent, GetCustomScriptletContent(web_contents())); SaveCustomScriptlet("custom-script-edited", kEditedContent); { - const auto& custom_resources = - ad_block_service->custom_resource_provider()->GetCustomResources(); + const auto& custom_resources = GetCustomResources(); ASSERT_TRUE(custom_resources.is_list()); ASSERT_EQ(1u, custom_resources.GetList().size()); CheckCustomScriptlet(custom_resources.GetList().front(), @@ -193,8 +199,7 @@ IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, AddEditRemoveScriptlet) { ASSERT_TRUE(ClickCustomScriplet(web_contents(), "brave-custom-script-edited.js", "delete")); { - const auto& custom_resources = - ad_block_service->custom_resource_provider()->GetCustomResources(); + const auto& custom_resources = GetCustomResources(); ASSERT_TRUE(custom_resources.is_list()); ASSERT_TRUE(custom_resources.GetList().empty()); } diff --git a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html index f52d49559923..f23868f1dc23 100644 --- a/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html +++ b/browser/resources/settings/default_brave_shields_page/components/brave_adblock_scriptlet_editor.html @@ -41,8 +41,7 @@
$i18n{adblockCustomScriptletDialogContentLabel}
-
diff --git a/browser/ui/webui/settings/brave_adblock_handler.cc b/browser/ui/webui/settings/brave_adblock_handler.cc index 92d7904c2064..faca5c1d8443 100644 --- a/browser/ui/webui/settings/brave_adblock_handler.cc +++ b/browser/ui/webui/settings/brave_adblock_handler.cc @@ -298,25 +298,31 @@ void BraveAdBlockHandler::GetCustomScriptlets(const base::Value::List& args) { CHECK(base::FeatureList::IsEnabled( brave_shields::features::kCosmeticFilteringCustomScriptlets)); CHECK(args.size() == 1u && args[0].is_string()); - AllowJavascript(); - const auto& custom_resources = g_brave_browser_process->ad_block_service() - ->custom_resource_provider() - ->GetCustomResources(); + g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->GetCustomResources( + base::BindOnce(&BraveAdBlockHandler::OnGetCustomScriptlets, + weak_factory_.GetWeakPtr(), args[0].GetString())); +} - ResolveJavascriptCallback(args[0].GetString(), custom_resources); +void BraveAdBlockHandler::OnGetCustomScriptlets(const std::string& callback_id, + base::Value custom_resources) { + AllowJavascript(); + ResolveJavascriptCallback(callback_id, custom_resources); } void BraveAdBlockHandler::AddCustomScriptlet(const base::Value::List& args) { CHECK(base::FeatureList::IsEnabled( brave_shields::features::kCosmeticFilteringCustomScriptlets)); CHECK(args.size() == 2u && args[0].is_string() && args[1].is_dict()); - AllowJavascript(); - auto error_code = g_brave_browser_process->ad_block_service() - ->custom_resource_provider() - ->AddResource(args[1]); - ResolveJavascriptCallback(args[0].GetString(), - base::Value(static_cast(error_code))); + + g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->AddResource( + args[1], + base::BindOnce(&BraveAdBlockHandler::OnScriptletUpdateStatus, + weak_factory_.GetWeakPtr(), args[0].GetString())); } void BraveAdBlockHandler::UpdateCustomScriptlet(const base::Value::List& args) { @@ -324,24 +330,33 @@ void BraveAdBlockHandler::UpdateCustomScriptlet(const base::Value::List& args) { brave_shields::features::kCosmeticFilteringCustomScriptlets)); CHECK(args.size() == 3u && args[0].is_string() && args[1].is_string() && args[2].is_dict()); - AllowJavascript(); - auto error_code = g_brave_browser_process->ad_block_service() - ->custom_resource_provider() - ->UpdateResource(args[1].GetString(), args[2]); - ResolveJavascriptCallback(args[0].GetString(), - base::Value(static_cast(error_code))); + + g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->UpdateResource( + args[1].GetString(), args[2], + base::BindOnce(&BraveAdBlockHandler::OnScriptletUpdateStatus, + weak_factory_.GetWeakPtr(), args[0].GetString())); } void BraveAdBlockHandler::RemoveCustomScriptlet(const base::Value::List& args) { CHECK(base::FeatureList::IsEnabled( brave_shields::features::kCosmeticFilteringCustomScriptlets)); CHECK(args.size() == 2u && args[0].is_string() && args[1].is_string()); - AllowJavascript(); - auto error_code = g_brave_browser_process->ad_block_service() - ->custom_resource_provider() - ->RemoveResource(args[1].GetString()); - ResolveJavascriptCallback(args[0].GetString(), + g_brave_browser_process->ad_block_service() + ->custom_resource_provider() + ->RemoveResource( + args[1].GetString(), + base::BindOnce(&BraveAdBlockHandler::OnScriptletUpdateStatus, + weak_factory_.GetWeakPtr(), args[0].GetString())); +} + +void BraveAdBlockHandler::OnScriptletUpdateStatus( + const std::string& callback_id, + brave_shields::AdBlockCustomResourceProvider::ErrorCode error_code) { + AllowJavascript(); + ResolveJavascriptCallback(callback_id, base::Value(static_cast(error_code))); } diff --git a/browser/ui/webui/settings/brave_adblock_handler.h b/browser/ui/webui/settings/brave_adblock_handler.h index cff4a0dd37f0..90656c8ca324 100644 --- a/browser/ui/webui/settings/brave_adblock_handler.h +++ b/browser/ui/webui/settings/brave_adblock_handler.h @@ -8,12 +8,12 @@ #include -#include "base/scoped_observation.h" - #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/scoped_observation.h" #include "brave/components/brave_shields/content/browser/ad_block_subscription_service_manager.h" #include "brave/components/brave_shields/content/browser/ad_block_subscription_service_manager_observer.h" +#include "brave/components/brave_shields/core/browser/ad_block_custom_resource_provider.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" class Profile; @@ -50,9 +50,14 @@ class BraveAdBlockHandler : public settings::SettingsPageUIHandler, void ViewSubscriptionSource(const base::Value::List& args); void UpdateCustomFilters(const base::Value::List& args); void GetCustomScriptlets(const base::Value::List& args); + void OnGetCustomScriptlets(const std::string& callback_id, + base::Value custom_resources); void AddCustomScriptlet(const base::Value::List& args); void UpdateCustomScriptlet(const base::Value::List& args); void RemoveCustomScriptlet(const base::Value::List& args); + void OnScriptletUpdateStatus( + const std::string& callback_id, + brave_shields::AdBlockCustomResourceProvider::ErrorCode error_code); void RefreshSubscriptionsList(); diff --git a/components/brave_shields/content/browser/ad_block_service.cc b/components/brave_shields/content/browser/ad_block_service.cc index c90c1203c1c8..d854afe10d4d 100644 --- a/components/brave_shields/content/browser/ad_block_service.cc +++ b/components/brave_shields/content/browser/ad_block_service.cc @@ -320,7 +320,7 @@ AdBlockService::AdBlockService( if (base::FeatureList::IsEnabled( features::kCosmeticFilteringCustomScriptlets)) { custom_resource_provider_ = new AdBlockCustomResourceProvider( - local_state_, std::move(default_resource_provider)); + profile_dir_, std::move(default_resource_provider)); resource_provider_.reset(custom_resource_provider_.get()); } else { resource_provider_ = std::move(default_resource_provider); @@ -418,7 +418,6 @@ void RegisterPrefsForAdBlockService(PrefRegistrySimple* registry) { registry->RegisterBooleanPref( prefs::kAdBlockMobileNotificationsListSettingTouched, false); registry->RegisterStringPref(prefs::kAdBlockCustomFilters, std::string()); - registry->RegisterListPref(prefs::kAdBlockCustomResources); registry->RegisterDictionaryPref(prefs::kAdBlockRegionalFilters); registry->RegisterDictionaryPref(prefs::kAdBlockListSubscriptions); registry->RegisterBooleanPref(prefs::kAdBlockCheckedDefaultRegion, false); diff --git a/components/brave_shields/core/browser/BUILD.gn b/components/brave_shields/core/browser/BUILD.gn index b5049455c628..1e9ff7e9949f 100644 --- a/components/brave_shields/core/browser/BUILD.gn +++ b/components/brave_shields/core/browser/BUILD.gn @@ -37,6 +37,7 @@ static_library("browser") { "//brave/components/brave_shields/core/common", "//components/component_updater:component_updater", "//components/prefs", + "//components/value_store", "//crypto", ] public_deps = [ "//base" ] diff --git a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc index bb672077cfb7..99b3ab7ebbe2 100644 --- a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc +++ b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc @@ -13,14 +13,21 @@ #include "base/ranges/algorithm.h" #include "base/strings/strcat.h" #include "base/strings/string_util.h" +#include "base/task/sequenced_task_runner.h" #include "brave/components/brave_shields/core/common/features.h" -#include "brave/components/brave_shields/core/common/pref_names.h" -#include "components/prefs/pref_service.h" #include "components/prefs/scoped_user_pref_update.h" +#include "components/value_store/value_store_factory_impl.h" +#include "components/value_store/value_store_frontend.h" +#include "components/value_store/value_store_task_runner.h" namespace brave_shields { +using base::Value; namespace { +constexpr const char kStorageUMA[] = "Custom Resources"; +constexpr const base::FilePath::CharType kStorageName[] = + FILE_PATH_LITERAL("Custom Resources"); +constexpr const char kStorageScriptletsKey[] = "SCRIPTLETS"; constexpr const char kNameField[] = "name"; constexpr const char kContentField[] = "content"; @@ -103,13 +110,18 @@ std::string MergeResources(const std::string& default_resources, } // namespace AdBlockCustomResourceProvider::AdBlockCustomResourceProvider( - PrefService* local_state, + const base::FilePath& storage_root, std::unique_ptr default_resource_provider) - : local_state_(local_state), - default_resource_provider_(std::move(default_resource_provider)) { + : default_resource_provider_(std::move(default_resource_provider)) { CHECK(base::FeatureList::IsEnabled( brave_shields::features::kCosmeticFilteringCustomScriptlets)); CHECK(default_resource_provider_); + auto factory = + base::MakeRefCounted(storage_root); + storage_ = std::make_unique( + std::move(factory), base::FilePath(kStorageName), kStorageUMA, + base::SequencedTaskRunner::GetCurrentDefault(), + value_store::GetValueStoreTaskRunner()); default_resource_provider_->AddObserver(this); } @@ -117,56 +129,51 @@ AdBlockCustomResourceProvider::~AdBlockCustomResourceProvider() { default_resource_provider_->RemoveObserver(this); } -const base::Value& AdBlockCustomResourceProvider::GetCustomResources() { - return local_state_->GetValue(prefs::kAdBlockCustomResources); +void AdBlockCustomResourceProvider::GetCustomResources( + base::OnceCallback callback) { + storage_->Get( + kStorageScriptletsKey, + base::BindOnce( + [](base::OnceCallback callback, + std::optional value) { + if (value && value->is_list()) { + std::move(callback).Run(std::move(*value)); + } else { + std::move(callback).Run(base::Value(base::Value::Type::LIST)); + } + }, + std::move(callback))); } -AdBlockCustomResourceProvider::ErrorCode -AdBlockCustomResourceProvider::AddResource(const base::Value& resource) { +void AdBlockCustomResourceProvider::AddResource(const base::Value& resource, + StatusCallback on_complete) { if (!IsValidResource(resource)) { - return ErrorCode::kInvalid; + return std::move(on_complete).Run(ErrorCode::kInvalid); } - - ScopedListPrefUpdate update(local_state_, prefs::kAdBlockCustomResources); - if (FindResource(update.Get(), GetResourceName(resource)) != update->end()) { - return ErrorCode::kAlreadyExists; - } - update->Append(resource.Clone()); - ReloadResourcesAndNotify(); - return ErrorCode::kOk; + GetCustomResources( + base::BindOnce(&AdBlockCustomResourceProvider::AddResourceInternal, + weak_ptr_factory_.GetWeakPtr(), resource.Clone(), + std::move(on_complete))); } -AdBlockCustomResourceProvider::ErrorCode -AdBlockCustomResourceProvider::UpdateResource(const std::string& old_name, - const base::Value& resource) { +void AdBlockCustomResourceProvider::UpdateResource(const std::string& old_name, + const base::Value& resource, + StatusCallback on_complete) { if (!IsValidResource(resource)) { - return ErrorCode::kInvalid; + return std::move(on_complete).Run(ErrorCode::kInvalid); } - ScopedListPrefUpdate update(local_state_, prefs::kAdBlockCustomResources); - auto updated_resource = FindResource(update.Get(), old_name); - if (updated_resource == update->end()) { - return ErrorCode::kNotFound; - } - const std::string& new_name = GetResourceName(resource); - if (old_name != new_name) { - if (FindResource(update.Get(), new_name) != update->end()) { - return ErrorCode::kNotFound; - } - } - - *updated_resource = resource.Clone(); - ReloadResourcesAndNotify(); - return ErrorCode::kOk; + GetCustomResources( + base::BindOnce(&AdBlockCustomResourceProvider::UpdateResourceInternal, + weak_ptr_factory_.GetWeakPtr(), old_name, resource.Clone(), + std::move(on_complete))); } -AdBlockCustomResourceProvider::ErrorCode -AdBlockCustomResourceProvider::RemoveResource( - const std::string& resource_name) { - ScopedListPrefUpdate update(local_state_, prefs::kAdBlockCustomResources); - auto updated_resource = FindResource(update.Get(), resource_name); - update->erase(updated_resource); - ReloadResourcesAndNotify(); - return ErrorCode::kOk; +void AdBlockCustomResourceProvider::RemoveResource( + const std::string& resource_name, + StatusCallback on_complete) { + GetCustomResources(base::BindOnce( + &AdBlockCustomResourceProvider::RemoveResourceInternal, + weak_ptr_factory_.GetWeakPtr(), resource_name, std::move(on_complete))); } void AdBlockCustomResourceProvider::LoadResources( @@ -178,30 +185,96 @@ void AdBlockCustomResourceProvider::LoadResources( void AdBlockCustomResourceProvider::OnResourcesLoaded( const std::string& resources_json) { - NotifyResourcesLoaded( - MergeResources(resources_json, GetCustomResourcesJson())); + OnDefaultResourcesLoaded( + base::BindOnce(&AdBlockCustomResourceProvider::NotifyResourcesLoaded, + weak_ptr_factory_.GetWeakPtr()), + resources_json); } -std::string AdBlockCustomResourceProvider::GetCustomResourcesJson() { - return base::WriteJson(GetCustomResources()).value_or(std::string()); +void AdBlockCustomResourceProvider::AddResourceInternal( + base::Value resource, + StatusCallback on_complete, + base::Value resources) { + CHECK(resources.is_list()); + auto& list = resources.GetList(); + if (FindResource(list, GetResourceName(resource)) != list.end()) { + return std::move(on_complete).Run(ErrorCode::kAlreadyExists); + } + list.Append(std::move(resource)); + SaveResources(std::move(resources)); + ReloadResourcesAndNotify(); + std::move(on_complete).Run(ErrorCode::kOk); +} + +void AdBlockCustomResourceProvider::UpdateResourceInternal( + const std::string& old_name, + base::Value resource, + StatusCallback on_complete, + base::Value resources) { + CHECK(resources.is_list()); + auto updated_resource = FindResource(resources.GetList(), old_name); + if (updated_resource == resources.GetList().end()) { + return std::move(on_complete).Run(ErrorCode::kNotFound); + } + + const std::string& new_name = GetResourceName(resource); + if (old_name != new_name) { + if (FindResource(resources.GetList(), new_name) != + resources.GetList().end()) { + return std::move(on_complete).Run(ErrorCode::kNotFound); + } + } + + *updated_resource = std::move(resource); + SaveResources(std::move(resources)); + ReloadResourcesAndNotify(); + std::move(on_complete).Run(ErrorCode::kOk); +} + +void AdBlockCustomResourceProvider::RemoveResourceInternal( + const std::string& name, + StatusCallback on_complete, + base::Value resources) { + CHECK(resources.is_list()); + auto updated_resource = FindResource(resources.GetList(), name); + if (updated_resource != resources.GetList().end()) { + resources.GetList().erase(updated_resource); + SaveResources(std::move(resources)); + ReloadResourcesAndNotify(); + std::move(on_complete).Run(ErrorCode::kOk); + } else { + std::move(on_complete).Run(ErrorCode::kNotFound); + } +} + +void AdBlockCustomResourceProvider::SaveResources(base::Value resources) { + storage_->Set(kStorageScriptletsKey, std::move(resources)); } void AdBlockCustomResourceProvider::OnDefaultResourcesLoaded( base::OnceCallback on_load, const std::string& resources_json) { - const auto& custom_resources = - local_state_->GetList(prefs::kAdBlockCustomResources); - std::string custom_resources_json = - base::WriteJson(custom_resources).value_or(std::string()); - - if (custom_resources.empty()) { - std::move(on_load).Run(resources_json); - } else if (resources_json.empty()) { - std::move(on_load).Run(custom_resources_json); + GetCustomResources(base::BindOnce( + &AdBlockCustomResourceProvider::OnCustomResourcesLoaded, + weak_ptr_factory_.GetWeakPtr(), std::move(on_load), resources_json)); +} + +void AdBlockCustomResourceProvider::OnCustomResourcesLoaded( + base::OnceCallback on_load, + const std::string& default_resources_json, + base::Value custom_resources) { + CHECK(custom_resources.is_list()); + + if (custom_resources.GetList().empty()) { + std::move(on_load).Run(default_resources_json); } else { - // Merge. - std::move(on_load).Run( - MergeResources(resources_json, custom_resources_json)); + auto custom_resources_json = base::WriteJson(custom_resources); + if (!custom_resources_json) { + std::move(on_load).Run(default_resources_json); + } else { + std::move(on_load).Run( + MergeResources(default_resources_json, *custom_resources_json)); + } } } diff --git a/components/brave_shields/core/browser/ad_block_custom_resource_provider.h b/components/brave_shields/core/browser/ad_block_custom_resource_provider.h index 5f5a2fae0097..8eecba8bb65e 100644 --- a/components/brave_shields/core/browser/ad_block_custom_resource_provider.h +++ b/components/brave_shields/core/browser/ad_block_custom_resource_provider.h @@ -8,12 +8,14 @@ #include +#include "base/files/file_path.h" #include "base/functional/callback.h" -#include "base/memory/raw_ptr.h" #include "base/values.h" #include "brave/components/brave_shields/core/browser/ad_block_resource_provider.h" -class PrefService; +namespace value_store { +class ValueStoreFrontend; +} // namespace value_store namespace brave_shields { @@ -28,16 +30,21 @@ class AdBlockCustomResourceProvider kNotFound, }; + using GetCallback = base::OnceCallback; + using StatusCallback = base::OnceCallback; + AdBlockCustomResourceProvider( - PrefService* local_state, + const base::FilePath& storage_root, std::unique_ptr default_resource_provider); ~AdBlockCustomResourceProvider() override; - const base::Value& GetCustomResources(); - ErrorCode AddResource(const base::Value& resource); - ErrorCode UpdateResource(const std::string& name, - const base::Value& resource); - ErrorCode RemoveResource(const std::string& resource_name); + void GetCustomResources(GetCallback callback); + void AddResource(const base::Value& resource, StatusCallback on_complete); + void UpdateResource(const std::string& name, + const base::Value& resource, + StatusCallback on_complete); + void RemoveResource(const std::string& resource_name, + StatusCallback on_complete); // AdBlockResourceProvider: void LoadResources( @@ -47,14 +54,31 @@ class AdBlockCustomResourceProvider // AdBlockResourceProvider::Observer: void OnResourcesLoaded(const std::string& resources_json) override; - std::string GetCustomResourcesJson(); + void AddResourceInternal(base::Value resource, + StatusCallback on_complete, + base::Value resources); + void UpdateResourceInternal(const std::string& name, + base::Value resource, + StatusCallback on_complete, + base::Value resources); + void RemoveResourceInternal(const std::string& name, + StatusCallback on_complete, + base::Value resources); + + void SaveResources(base::Value resources); + void OnDefaultResourcesLoaded( base::OnceCallback on_load, const std::string& resources_json); + void OnCustomResourcesLoaded( + base::OnceCallback on_load, + const std::string& default_resources, + base::Value custom_resources); + void ReloadResourcesAndNotify(); - const raw_ptr local_state_ = nullptr; std::unique_ptr default_resource_provider_ = nullptr; + std::unique_ptr storage_; base::WeakPtrFactory weak_ptr_factory_{this}; }; diff --git a/components/brave_shields/core/common/pref_names.h b/components/brave_shields/core/common/pref_names.h index 1613b8ffe718..bbe3a4a999a3 100644 --- a/components/brave_shields/core/common/pref_names.h +++ b/components/brave_shields/core/common/pref_names.h @@ -21,8 +21,6 @@ inline constexpr char kAdBlockMobileNotificationsListSettingTouched[] = "brave.ad_block.mobile_notifications_list_setting_touched"; inline constexpr char kAdBlockCustomFilters[] = "brave.ad_block.custom_filters"; -inline constexpr char kAdBlockCustomResources[] = - "brave.ad_block.custom_resources"; inline constexpr char kAdBlockRegionalFilters[] = "brave.ad_block.regional_filters"; inline constexpr char kAdBlockListSubscriptions[] = From 46eaedf40fb6f6da18da68a273f5d912e79458e0 Mon Sep 17 00:00:00 2001 From: boocmp Date: Thu, 14 Nov 2024 22:15:15 +0700 Subject: [PATCH 11/21] Added adblock `Developer mode` protected pref. --- app/brave_settings_strings.grdp | 6 +++ browser/brave_profile_prefs.cc | 2 + .../ad_block_custom_resources_browsertest.cc | 6 +++ .../ad_block_pref_service_factory.cc | 4 +- .../ad_block_service_browsertest.cc | 18 ++++++++ .../ad_block_service_browsertest.h | 1 + .../api/settings_private/brave_prefs_util.cc | 2 + .../brave_adblock_subpage.html | 43 ++++++++++++------- .../brave_adblock_subpage.ts | 13 ++++++ .../default_brave_shields_page.html | 2 +- ...ave_settings_localized_strings_provider.cc | 2 + .../memory_infra_background_allowlist.cc | 4 ++ .../prefs/chrome_pref_service_factory.cc | 17 ++++++++ chromium_src/chrome/browser/prefs/sources.gni | 1 + .../ad_block_custom_filters_provider.cc | 10 ++++- .../ad_block_custom_filters_provider.h | 3 ++ .../content/browser/ad_block_pref_service.cc | 19 +++++++- .../content/browser/ad_block_pref_service.h | 4 +- .../content/browser/ad_block_service.cc | 9 ++++ .../content/browser/ad_block_service.h | 1 + .../ad_block_custom_resource_provider.cc | 19 ++++++-- .../ad_block_custom_resource_provider.h | 4 ++ .../brave_shields/core/common/pref_names.h | 2 + ...prefs-chrome_pref_service_factory.cc.patch | 12 ++++++ 24 files changed, 179 insertions(+), 25 deletions(-) create mode 100644 chromium_src/chrome/browser/prefs/chrome_pref_service_factory.cc create mode 100644 patches/chrome-browser-prefs-chrome_pref_service_factory.cc.patch diff --git a/app/brave_settings_strings.grdp b/app/brave_settings_strings.grdp index af0145abfd84..6529687e4e7c 100644 --- a/app/brave_settings_strings.grdp +++ b/app/brave_settings_strings.grdp @@ -577,6 +577,12 @@ Filter lists + + Developer mode + + + Allows to add custom filtering rules and scriptlets + Custom scriptlets diff --git a/browser/brave_profile_prefs.cc b/browser/brave_profile_prefs.cc index 23bf54eaf2d7..81a45ec7ee39 100644 --- a/browser/brave_profile_prefs.cc +++ b/browser/brave_profile_prefs.cc @@ -256,6 +256,8 @@ void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { true); registry->RegisterBooleanPref(brave_shields::prefs::kLinkedInEmbedControlType, false); + registry->RegisterBooleanPref(brave_shields::prefs::kAdBlockDeveloperMode, + false); // WebTorrent #if BUILDFLAG(ENABLE_BRAVE_WEBTORRENT) diff --git a/browser/brave_shields/ad_block_custom_resources_browsertest.cc b/browser/brave_shields/ad_block_custom_resources_browsertest.cc index 0e0f6f7eebac..6beeb4e2b490 100644 --- a/browser/brave_shields/ad_block_custom_resources_browsertest.cc +++ b/browser/brave_shields/ad_block_custom_resources_browsertest.cc @@ -165,6 +165,8 @@ class AdblockCustomResourcesTest : public AdBlockServiceTest { }; IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, AddEditRemoveScriptlet) { + EnableDeveloperMode(true); + NavigateToURL(GURL("brave://settings/shields/filters")); constexpr const char kContent[] = "window.test = 'custom-script'"; @@ -206,6 +208,8 @@ IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, AddEditRemoveScriptlet) { } IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, ExecCustomScriptlet) { + EnableDeveloperMode(true); + NavigateToURL(GURL("brave://settings/shields/filters")); constexpr const char kContent[] = "window.test = 'custom-script'"; @@ -223,6 +227,8 @@ IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, ExecCustomScriptlet) { } IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, NameConflicts) { + EnableDeveloperMode(true); + constexpr const char kBraveFix[] = "window.test = 'default-script'"; constexpr const char kBraveFixResource[] = R"json( [{ diff --git a/browser/brave_shields/ad_block_pref_service_factory.cc b/browser/brave_shields/ad_block_pref_service_factory.cc index fdba7c933fd4..3b67787c795d 100644 --- a/browser/brave_shields/ad_block_pref_service_factory.cc +++ b/browser/brave_shields/ad_block_pref_service_factory.cc @@ -11,6 +11,7 @@ #include "base/no_destructor.h" #include "brave/browser/brave_browser_process.h" #include "brave/components/brave_shields/content/browser/ad_block_pref_service.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/net/proxy_service_factory.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" @@ -47,7 +48,8 @@ AdBlockPrefServiceFactory::BuildServiceInstanceForBrowserContext( Profile* profile = Profile::FromBrowserContext(context); auto service = std::make_unique( - g_brave_browser_process->ad_block_service(), profile->GetPrefs()); + g_brave_browser_process->ad_block_service(), profile->GetPrefs(), + g_browser_process->local_state()); auto pref_proxy_config_tracker = ProxyServiceFactory::CreatePrefProxyConfigTrackerOfProfile( diff --git a/browser/brave_shields/ad_block_service_browsertest.cc b/browser/brave_shields/ad_block_service_browsertest.cc index 494f3f1fd8b7..c648cfe2e6c8 100644 --- a/browser/brave_shields/ad_block_service_browsertest.cc +++ b/browser/brave_shields/ad_block_service_browsertest.cc @@ -274,6 +274,11 @@ void AdBlockServiceTest::UpdateAdBlockInstanceWithRules( engine_observer.Wait(); } +void AdBlockServiceTest::EnableDeveloperMode(bool enabled) { + browser()->profile()->GetPrefs()->SetBoolean( + brave_shields::prefs::kAdBlockDeveloperMode, enabled); +} + void AdBlockServiceTest::UpdateCustomAdBlockInstanceWithRules( const std::string& rules) { brave_shields::AdBlockService* ad_block_service = @@ -428,6 +433,8 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, // Load a page with an ad image, and make sure it is blocked by custom // filters. IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, AdsGetBlockedByCustomBlocker) { + EnableDeveloperMode(true); + UpdateAdBlockInstanceWithRules(""); UpdateCustomAdBlockInstanceWithRules("*ad_banner.png"); @@ -445,6 +452,8 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, AdsGetBlockedByCustomBlocker) { // Load a page with an ad image, with a corresponding exception installed in // the custom filters, and make sure it is not blocked. IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, DefaultBlockCustomException) { + EnableDeveloperMode(true); + UpdateAdBlockInstanceWithRules("*ad_banner.png"); UpdateCustomAdBlockInstanceWithRules("@@ad_banner.png"); @@ -1775,6 +1784,8 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, DefaultNoRemoveparam) { // `$removeparam` should still be activated in default blocking mode if it comes // from custom filters IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, MAYBE_DefaultRemoveparamFromCustom) { + EnableDeveloperMode(true); + DisableAggressiveMode(); UpdateCustomAdBlockInstanceWithRules("*$subdocument,removeparam=evil"); @@ -1835,6 +1846,8 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CspRule) { // The policy resulting from two of the same kind of directive will be the // union of both. IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CspRuleMerging) { + EnableDeveloperMode(true); + UpdateAdBlockInstanceWithRules( "||example.com^$csp=script-src 'nonce-abcdef' 'unsafe-eval' 'self'"); UpdateCustomAdBlockInstanceWithRules( @@ -2227,6 +2240,8 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringDynamic) { // Test cosmetic filtering on elements added dynamically, using a rule from the // custom filters IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringDynamicCustom) { + EnableDeveloperMode(true); + ASSERT_TRUE(g_brave_browser_process->ad_block_service() ->custom_filters_provider() ->UpdateCustomFilters("##.blockme")); @@ -2690,6 +2705,7 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, // exception policy. IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CustomCosmeticFilteringOverridesImportant) { + EnableDeveloperMode(true); UpdateCustomAdBlockInstanceWithRules("###inline-block-important"); GURL tab_url = @@ -3000,6 +3016,8 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, ListEnabled) { // Content Picker and the context menu are disabled for Android. #if !BUILDFLAG(IS_ANDROID) IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, ContentPicker) { + EnableDeveloperMode(true); + const GURL tab_url = embedded_test_server()->GetURL("a.com", "/cosmetic_filtering.html"); NavigateToURL(tab_url); diff --git a/browser/brave_shields/ad_block_service_browsertest.h b/browser/brave_shields/ad_block_service_browsertest.h index f33e1dda9118..b4e3c9f7b869 100644 --- a/browser/brave_shields/ad_block_service_browsertest.h +++ b/browser/brave_shields/ad_block_service_browsertest.h @@ -49,6 +49,7 @@ class AdBlockServiceTest : public PlatformBrowserTest { bool first_party_protections = false); void UpdateAdBlockResources(const std::string& resources); void UpdateAdBlockInstanceWithRules(const std::string& rules); + void EnableDeveloperMode(bool enabled); void UpdateCustomAdBlockInstanceWithRules(const std::string& rules); void AssertTagExists(const std::string& tag, bool expected_exists) const; void InitEmbeddedTestServer(); diff --git a/browser/extensions/api/settings_private/brave_prefs_util.cc b/browser/extensions/api/settings_private/brave_prefs_util.cc index c3fc2d0ca927..d928a4d7da44 100644 --- a/browser/extensions/api/settings_private/brave_prefs_util.cc +++ b/browser/extensions/api/settings_private/brave_prefs_util.cc @@ -100,6 +100,8 @@ const PrefsUtil::TypedPrefMap& BravePrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::kBoolean; (*s_brave_allowlist)[brave_shields::prefs::kReduceLanguageEnabled] = settings_api::PrefType::kBoolean; + (*s_brave_allowlist)[brave_shields::prefs::kAdBlockDeveloperMode] = + settings_api::PrefType::kBoolean; // Webcompat Reporter (*s_brave_allowlist)[webcompat_reporter::prefs::kContactInfoSaveFlagPrefs] = diff --git a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html index 1ead88bd052a..42a66e1fd920 100644 --- a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html +++ b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html @@ -316,25 +316,36 @@ -
-
-
- $i18n{adblockCustomFiltersLabel} -
-
- -
-
- \ No newline at end of file diff --git a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts index fa75171a2ebf..6084cd477162 100644 --- a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts +++ b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.ts @@ -22,6 +22,8 @@ import {getTemplate} from './brave_adblock_subpage.html.js' import { loadTimeData } from '../i18n_setup.js' +import type {SettingsToggleButtonElement} from '../controls/settings_toggle_button.js'; + const AdBlockSubpageBase = PrefsMixin(I18nMixin(BaseMixin(PolymerElement))) class AdBlockSubpage extends AdBlockSubpageBase { @@ -165,6 +167,17 @@ class AdBlockSubpage extends AdBlockSubpageBase { isLastAttemptFailed_(item) { return item.last_successful_update_attempt !== 0 && item.last_successful_update_attempt === item.last_update_attempt } + + onDeveloperModeChange_(event: Event) { + const target = event.target as SettingsToggleButtonElement + if (target.checked) { + setTimeout(() => { + this.browserProxy_.getCustomFilters().then((value) => { + this.customFilters_ = value + }) + }) + } + } } customElements.define(AdBlockSubpage.is, AdBlockSubpage) diff --git a/browser/resources/settings/default_brave_shields_page/default_brave_shields_page.html b/browser/resources/settings/default_brave_shields_page/default_brave_shields_page.html index 82f02dbc5dd3..eb767bbb4df2 100644 --- a/browser/resources/settings/default_brave_shields_page/default_brave_shields_page.html +++ b/browser/resources/settings/default_brave_shields_page/default_brave_shields_page.html @@ -135,7 +135,7 @@ - + diff --git a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc index 9fc0705520ca..b3aabfb056a5 100644 --- a/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc +++ b/browser/ui/webui/settings/brave_settings_localized_strings_provider.cc @@ -845,6 +845,8 @@ void BraveAddCommonStrings(content::WebUIDataSource* html_source, {"adblockSubscribeUrlUpdateFailed", IDS_BRAVE_ADBLOCK_SUBSCRIBE_URL_UPDATE_FAILED}, {"adblockCustomListsLabel", IDS_BRAVE_ADBLOCK_CUSTOM_LISTS_LABEL}, + {"adblockDeveloperModeLabel", IDS_BRAVE_ADBLOCK_DEVELOPER_MODE_LABEL}, + {"adblockDeveloperModeDesc", IDS_BRAVE_ADBLOCK_DEVELOPER_MODE_DESC}, {"adblockCustomSciptletsListLabel", IDS_BRAVE_ADBLOCK_CUSTOM_SCRIPTLETS_LIST_LABEL}, {"adblockAddCustomScriptletButton", diff --git a/chromium_src/base/trace_event/memory_infra_background_allowlist.cc b/chromium_src/base/trace_event/memory_infra_background_allowlist.cc index 8e78022b602c..7f7fef50020a 100644 --- a/chromium_src/base/trace_event/memory_infra_background_allowlist.cc +++ b/chromium_src/base/trace_event/memory_infra_background_allowlist.cc @@ -17,6 +17,10 @@ bool IsMemoryAllocatorDumpNameInAllowlist(const std::string& name) { "extensions/value_store/Extensions.Database.Open.BraveWallet/")) { return true; } + if (name.starts_with("extensions/value_store/" + "Extensions.Database.Open.AdBlock Custom Resources/")) { + return true; + } return IsMemoryAllocatorDumpNameInAllowlist_ChromiumImpl(name); } diff --git a/chromium_src/chrome/browser/prefs/chrome_pref_service_factory.cc b/chromium_src/chrome/browser/prefs/chrome_pref_service_factory.cc new file mode 100644 index 000000000000..45a097f3dc92 --- /dev/null +++ b/chromium_src/chrome/browser/prefs/chrome_pref_service_factory.cc @@ -0,0 +1,17 @@ +/* Copyright (c) 2024 The Brave Authors. All rights reserved. + * 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 https://mozilla.org/MPL/2.0/. */ + +#include "chrome/browser/prefs/chrome_pref_service_factory.h" + +#include "brave/components/brave_shields/core/common/pref_names.h" + +#define BRAVE_TRACKED_PREFS_EXTEND \ + {1000, brave_shields::prefs::kAdBlockDeveloperMode, \ + EnforcementLevel::ENFORCE_ON_LOAD, PrefTrackingStrategy::ATOMIC, \ + ValueType::IMPERSONAL}, + +#include "src/chrome/browser/prefs/chrome_pref_service_factory.cc" + +#undef BRAVE_TRACKED_PREFS_EXTEND diff --git a/chromium_src/chrome/browser/prefs/sources.gni b/chromium_src/chrome/browser/prefs/sources.gni index 7d5814edf9b0..81d84ce0a9ea 100644 --- a/chromium_src/chrome/browser/prefs/sources.gni +++ b/chromium_src/chrome/browser/prefs/sources.gni @@ -6,6 +6,7 @@ import("//third_party/widevine/cdm/widevine.gni") brave_chromium_src_chrome_browser_prefs_deps = [ + "//brave/components/brave_shields/core/common", "//brave/components/brave_sync", "//brave/components/brave_vpn/common/buildflags", "//brave/components/brave_wallet/browser", diff --git a/components/brave_shields/content/browser/ad_block_custom_filters_provider.cc b/components/brave_shields/content/browser/ad_block_custom_filters_provider.cc index 1af34f41efed..867da4d5ba96 100644 --- a/components/brave_shields/content/browser/ad_block_custom_filters_provider.cc +++ b/components/brave_shields/content/browser/ad_block_custom_filters_provider.cc @@ -37,6 +37,14 @@ AdBlockCustomFiltersProvider::AdBlockCustomFiltersProvider( AdBlockCustomFiltersProvider::~AdBlockCustomFiltersProvider() {} +void AdBlockCustomFiltersProvider::EnableDeveloperMode(bool enabled) { + if (developer_mode_enabled_ == enabled) { + return; + } + developer_mode_enabled_ = enabled; + NotifyObservers(engine_is_default_); +} + void AdBlockCustomFiltersProvider::AddUserCosmeticFilter( const std::string& filter) { std::string custom_filters = GetCustomFilters(); @@ -56,7 +64,7 @@ void AdBlockCustomFiltersProvider::CreateSiteExemption( std::string AdBlockCustomFiltersProvider::GetCustomFilters() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!local_state_) { + if (!local_state_ || !developer_mode_enabled_) { return std::string(); } return local_state_->GetString(prefs::kAdBlockCustomFilters); diff --git a/components/brave_shields/content/browser/ad_block_custom_filters_provider.h b/components/brave_shields/content/browser/ad_block_custom_filters_provider.h index a15d7aa5fc94..dc435058e915 100644 --- a/components/brave_shields/content/browser/ad_block_custom_filters_provider.h +++ b/components/brave_shields/content/browser/ad_block_custom_filters_provider.h @@ -28,6 +28,8 @@ class AdBlockCustomFiltersProvider : public AdBlockFiltersProvider { AdBlockCustomFiltersProvider& operator=(const AdBlockCustomFiltersProvider&) = delete; + void EnableDeveloperMode(bool enabled); + void AddUserCosmeticFilter(const std::string& filter); void CreateSiteExemption(const std::string& host); @@ -43,6 +45,7 @@ class AdBlockCustomFiltersProvider : public AdBlockFiltersProvider { private: const raw_ptr local_state_; + bool developer_mode_enabled_ = false; SEQUENCE_CHECKER(sequence_checker_); }; diff --git a/components/brave_shields/content/browser/ad_block_pref_service.cc b/components/brave_shields/content/browser/ad_block_pref_service.cc index 79a4d74a283e..b05f4f86cda6 100644 --- a/components/brave_shields/content/browser/ad_block_pref_service.cc +++ b/components/brave_shields/content/browser/ad_block_pref_service.cc @@ -35,7 +35,8 @@ std::string GetTagFromPrefName(const std::string& pref_name) { } // namespace AdBlockPrefService::AdBlockPrefService(AdBlockService* ad_block_service, - PrefService* prefs) + PrefService* prefs, + PrefService* local_state) : ad_block_service_(ad_block_service), prefs_(prefs) { pref_change_registrar_.reset(new PrefChangeRegistrar()); pref_change_registrar_->Init(prefs_); @@ -56,6 +57,17 @@ AdBlockPrefService::AdBlockPrefService(AdBlockService* ad_block_service, OnPreferenceChanged(prefs::kFBEmbedControlType); OnPreferenceChanged(prefs::kTwitterEmbedControlType); OnPreferenceChanged(prefs::kLinkedInEmbedControlType); + + if (!prefs->HasPrefPath(prefs::kAdBlockDeveloperMode)) { + const std::string& custom_filters = + local_state->GetString(prefs::kAdBlockCustomFilters); + prefs->SetBoolean(prefs::kAdBlockDeveloperMode, !custom_filters.empty()); + } + pref_change_registrar_->Add( + prefs::kAdBlockDeveloperMode, + base::BindRepeating(&AdBlockPrefService::OnDeveloperModeChanged, + base::Unretained(this))); + OnDeveloperModeChanged(); } AdBlockPrefService::~AdBlockPrefService() = default; @@ -110,6 +122,11 @@ void AdBlockPrefService::OnPreferenceChanged(const std::string& pref_name) { ad_block_service_->EnableTag(tag, enabled); } +void AdBlockPrefService::OnDeveloperModeChanged() { + const bool enabled = prefs_->GetBoolean(prefs::kAdBlockDeveloperMode); + ad_block_service_->EnableDeveloperMode(enabled); +} + void AdBlockPrefService::OnProxyConfigChanged( const net::ProxyConfigWithAnnotation& config, net::ProxyConfigService::ConfigAvailability availability) { diff --git a/components/brave_shields/content/browser/ad_block_pref_service.h b/components/brave_shields/content/browser/ad_block_pref_service.h index c747237a65e2..6b141a1c2f10 100644 --- a/components/brave_shields/content/browser/ad_block_pref_service.h +++ b/components/brave_shields/content/browser/ad_block_pref_service.h @@ -27,7 +27,8 @@ class AdBlockPrefService : public KeyedService, public net::ProxyConfigService::Observer { public: explicit AdBlockPrefService(AdBlockService* ad_block_service, - PrefService* prefs); + PrefService* prefs, + PrefService* local_state); ~AdBlockPrefService() override; void StartProxyTracker( @@ -40,6 +41,7 @@ class AdBlockPrefService : public KeyedService, void Shutdown() override; void OnPreferenceChanged(const std::string& pref_name); + void OnDeveloperModeChanged(); // net::ProxyConfigService::Observer: void OnProxyConfigChanged( diff --git a/components/brave_shields/content/browser/ad_block_service.cc b/components/brave_shields/content/browser/ad_block_service.cc index d854afe10d4d..7e36da10927e 100644 --- a/components/brave_shields/content/browser/ad_block_service.cc +++ b/components/brave_shields/content/browser/ad_block_service.cc @@ -358,6 +358,15 @@ AdBlockService::AdBlockService( AdBlockService::~AdBlockService() = default; +void AdBlockService::EnableDeveloperMode(bool enabled) { + if (custom_resource_provider()) { + custom_resource_provider()->EnableDeveloperMode(enabled); + } + if (custom_filters_provider()) { + custom_filters_provider()->EnableDeveloperMode(enabled); + } +} + void AdBlockService::EnableTag(const std::string& tag, bool enabled) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // Tags only need to be modified for the default engine. diff --git a/components/brave_shields/content/browser/ad_block_service.h b/components/brave_shields/content/browser/ad_block_service.h index 2725cf319307..801eea0d2117 100644 --- a/components/brave_shields/content/browser/ad_block_service.h +++ b/components/brave_shields/content/browser/ad_block_service.h @@ -128,6 +128,7 @@ class AdBlockService { AdBlockCustomFiltersProvider* custom_filters_provider(); AdBlockCustomResourceProvider* custom_resource_provider(); + void EnableDeveloperMode(bool enabled); void EnableTag(const std::string& tag, bool enabled); void AddUserCosmeticFilter(const std::string& filter); diff --git a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc index 99b3ab7ebbe2..e04df536aa84 100644 --- a/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc +++ b/components/brave_shields/core/browser/ad_block_custom_resource_provider.cc @@ -7,6 +7,7 @@ #include #include +#include #include "base/feature_list.h" #include "base/json/json_writer.h" @@ -15,18 +16,16 @@ #include "base/strings/string_util.h" #include "base/task/sequenced_task_runner.h" #include "brave/components/brave_shields/core/common/features.h" -#include "components/prefs/scoped_user_pref_update.h" #include "components/value_store/value_store_factory_impl.h" #include "components/value_store/value_store_frontend.h" #include "components/value_store/value_store_task_runner.h" namespace brave_shields { -using base::Value; namespace { -constexpr const char kStorageUMA[] = "Custom Resources"; +constexpr const char kStorageUMA[] = "AdBlock Custom Resources"; constexpr const base::FilePath::CharType kStorageName[] = - FILE_PATH_LITERAL("Custom Resources"); + FILE_PATH_LITERAL("AdBlock Custom Resources"); constexpr const char kStorageScriptletsKey[] = "SCRIPTLETS"; constexpr const char kNameField[] = "name"; @@ -129,8 +128,20 @@ AdBlockCustomResourceProvider::~AdBlockCustomResourceProvider() { default_resource_provider_->RemoveObserver(this); } +void AdBlockCustomResourceProvider::EnableDeveloperMode(bool enabled) { + if (developer_mode_enabled_ == enabled) { + return; + } + developer_mode_enabled_ = enabled; + ReloadResourcesAndNotify(); +} + void AdBlockCustomResourceProvider::GetCustomResources( base::OnceCallback callback) { + if (!developer_mode_enabled_) { + return std::move(callback).Run(base::Value(base::Value::Type::LIST)); + } + storage_->Get( kStorageScriptletsKey, base::BindOnce( diff --git a/components/brave_shields/core/browser/ad_block_custom_resource_provider.h b/components/brave_shields/core/browser/ad_block_custom_resource_provider.h index 8eecba8bb65e..9237eb0c85d2 100644 --- a/components/brave_shields/core/browser/ad_block_custom_resource_provider.h +++ b/components/brave_shields/core/browser/ad_block_custom_resource_provider.h @@ -6,6 +6,7 @@ #ifndef BRAVE_COMPONENTS_BRAVE_SHIELDS_CORE_BROWSER_AD_BLOCK_CUSTOM_RESOURCE_PROVIDER_H_ #define BRAVE_COMPONENTS_BRAVE_SHIELDS_CORE_BROWSER_AD_BLOCK_CUSTOM_RESOURCE_PROVIDER_H_ +#include #include #include "base/files/file_path.h" @@ -38,6 +39,8 @@ class AdBlockCustomResourceProvider std::unique_ptr default_resource_provider); ~AdBlockCustomResourceProvider() override; + void EnableDeveloperMode(bool enabled); + void GetCustomResources(GetCallback callback); void AddResource(const base::Value& resource, StatusCallback on_complete); void UpdateResource(const std::string& name, @@ -79,6 +82,7 @@ class AdBlockCustomResourceProvider std::unique_ptr default_resource_provider_ = nullptr; std::unique_ptr storage_; + bool developer_mode_enabled_ = false; base::WeakPtrFactory weak_ptr_factory_{this}; }; diff --git a/components/brave_shields/core/common/pref_names.h b/components/brave_shields/core/common/pref_names.h index bbe3a4a999a3..516090873ef6 100644 --- a/components/brave_shields/core/common/pref_names.h +++ b/components/brave_shields/core/common/pref_names.h @@ -25,6 +25,8 @@ inline constexpr char kAdBlockRegionalFilters[] = "brave.ad_block.regional_filters"; inline constexpr char kAdBlockListSubscriptions[] = "brave.ad_block.list_subscriptions"; +inline constexpr char kAdBlockDeveloperMode[] = "brave.ad_block.developer_mode"; + inline constexpr char kFBEmbedControlType[] = "brave.fb_embed_default"; inline constexpr char kTwitterEmbedControlType[] = "brave.twitter_embed_default"; diff --git a/patches/chrome-browser-prefs-chrome_pref_service_factory.cc.patch b/patches/chrome-browser-prefs-chrome_pref_service_factory.cc.patch new file mode 100644 index 000000000000..5f4c033cd057 --- /dev/null +++ b/patches/chrome-browser-prefs-chrome_pref_service_factory.cc.patch @@ -0,0 +1,12 @@ +diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc +index 2df13b1a2d35b2f45181348845aecc4fa0bb7815..31e37ad410e58252ba07c2736be3f6e3595bb1e0 100644 +--- a/chrome/browser/prefs/chrome_pref_service_factory.cc ++++ b/chrome/browser/prefs/chrome_pref_service_factory.cc +@@ -189,6 +189,7 @@ const prefs::TrackedPreferenceMetadata kTrackedPrefs[] = { + + // See note at top, new items added here also need to be added to + // histograms.xml's TrackedPreference enum. ++ BRAVE_TRACKED_PREFS_EXTEND + }; + + // One more than the last tracked preferences ID above. From 7992daedc8e3e2ac3ec17ed58364ab0241f4ec25 Mon Sep 17 00:00:00 2001 From: boocmp Date: Fri, 15 Nov 2024 13:46:32 +0700 Subject: [PATCH 12/21] Unit tests issue. --- .../content/browser/ad_block_pref_service.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/brave_shields/content/browser/ad_block_pref_service.cc b/components/brave_shields/content/browser/ad_block_pref_service.cc index b05f4f86cda6..40b47baf4396 100644 --- a/components/brave_shields/content/browser/ad_block_pref_service.cc +++ b/components/brave_shields/content/browser/ad_block_pref_service.cc @@ -58,10 +58,12 @@ AdBlockPrefService::AdBlockPrefService(AdBlockService* ad_block_service, OnPreferenceChanged(prefs::kTwitterEmbedControlType); OnPreferenceChanged(prefs::kLinkedInEmbedControlType); - if (!prefs->HasPrefPath(prefs::kAdBlockDeveloperMode)) { - const std::string& custom_filters = - local_state->GetString(prefs::kAdBlockCustomFilters); - prefs->SetBoolean(prefs::kAdBlockDeveloperMode, !custom_filters.empty()); + if (local_state) { // Can be null in unit tests. + if (!prefs->HasPrefPath(prefs::kAdBlockDeveloperMode)) { + const std::string& custom_filters = + local_state->GetString(prefs::kAdBlockCustomFilters); + prefs->SetBoolean(prefs::kAdBlockDeveloperMode, !custom_filters.empty()); + } } pref_change_registrar_->Add( prefs::kAdBlockDeveloperMode, From fe63c1630ec527079817719aa45fe828d0ae3651 Mon Sep 17 00:00:00 2001 From: boocmp Date: Fri, 15 Nov 2024 13:49:03 +0700 Subject: [PATCH 13/21] Typo. --- components/brave_shields/core/common/features.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/brave_shields/core/common/features.cc b/components/brave_shields/core/common/features.cc index e1f544e55913..924ce75f0eb0 100644 --- a/components/brave_shields/core/common/features.cc +++ b/components/brave_shields/core/common/features.cc @@ -140,7 +140,7 @@ BASE_FEATURE(kCosmeticFilteringJsPerformance, base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kCosmeticFilteringCustomScriptlets, - "CostemicFilteringCustomScriptlets", + "CosmeticFilteringCustomScriptlets", base::FEATURE_DISABLED_BY_DEFAULT); constexpr base::FeatureParam kComponentUpdateCheckIntervalMins{ From d291ee03dd119aeba5b778bb14e1ed327c0face6 Mon Sep 17 00:00:00 2001 From: boocmp Date: Mon, 18 Nov 2024 19:58:01 +0700 Subject: [PATCH 14/21] Moved 'developer mode' check to the new UpdateCustomFiltersFromSettings method. Added test. --- .../ad_block_custom_resources_browsertest.cc | 18 +++++++++++++++++- .../ad_block_service_browsertest.cc | 13 ------------- browser/ui/webui/brave_adblock_ui.cc | 5 +++-- .../ad_block_custom_filters_provider.cc | 10 +++++++++- .../browser/ad_block_custom_filters_provider.h | 6 ++++++ 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/browser/brave_shields/ad_block_custom_resources_browsertest.cc b/browser/brave_shields/ad_block_custom_resources_browsertest.cc index 6beeb4e2b490..83492ea00c1b 100644 --- a/browser/brave_shields/ad_block_custom_resources_browsertest.cc +++ b/browser/brave_shields/ad_block_custom_resources_browsertest.cc @@ -13,8 +13,9 @@ #include "brave/components/brave_shields/content/browser/ad_block_service.h" #include "brave/components/brave_shields/core/browser/ad_block_custom_resource_provider.h" #include "brave/components/brave_shields/core/common/features.h" -#include "chrome/browser/interstitials/security_interstitial_page_test_utils.h" +#include "brave/components/brave_shields/core/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" +#include "components/prefs/pref_service.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" #include "url/gurl.h" @@ -256,3 +257,18 @@ IN_PROC_BROWSER_TEST_F(AdblockCustomResourcesTest, NameConflicts) { EXPECT_EQ("default-script", EvalJs(web_contents(), "window.test")); } + +class AdblockGotCustomResourcesTest : public AdblockCustomResourcesTest { + public: + void SetUpLocalStatePrefService(PrefService* local_state) override { + local_state->SetString(brave_shields::prefs::kAdBlockCustomFilters, + "custom"); + + AdblockCustomResourcesTest::SetUpLocalStatePrefService(local_state); + } +}; + +IN_PROC_BROWSER_TEST_F(AdblockGotCustomResourcesTest, DeveloperModeEnabled) { + EXPECT_TRUE(profile()->GetPrefs()->GetBoolean( + brave_shields::prefs::kAdBlockDeveloperMode)); +} diff --git a/browser/brave_shields/ad_block_service_browsertest.cc b/browser/brave_shields/ad_block_service_browsertest.cc index c648cfe2e6c8..735c253f738d 100644 --- a/browser/brave_shields/ad_block_service_browsertest.cc +++ b/browser/brave_shields/ad_block_service_browsertest.cc @@ -433,8 +433,6 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, // Load a page with an ad image, and make sure it is blocked by custom // filters. IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, AdsGetBlockedByCustomBlocker) { - EnableDeveloperMode(true); - UpdateAdBlockInstanceWithRules(""); UpdateCustomAdBlockInstanceWithRules("*ad_banner.png"); @@ -452,8 +450,6 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, AdsGetBlockedByCustomBlocker) { // Load a page with an ad image, with a corresponding exception installed in // the custom filters, and make sure it is not blocked. IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, DefaultBlockCustomException) { - EnableDeveloperMode(true); - UpdateAdBlockInstanceWithRules("*ad_banner.png"); UpdateCustomAdBlockInstanceWithRules("@@ad_banner.png"); @@ -1784,8 +1780,6 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, DefaultNoRemoveparam) { // `$removeparam` should still be activated in default blocking mode if it comes // from custom filters IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, MAYBE_DefaultRemoveparamFromCustom) { - EnableDeveloperMode(true); - DisableAggressiveMode(); UpdateCustomAdBlockInstanceWithRules("*$subdocument,removeparam=evil"); @@ -1846,8 +1840,6 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CspRule) { // The policy resulting from two of the same kind of directive will be the // union of both. IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CspRuleMerging) { - EnableDeveloperMode(true); - UpdateAdBlockInstanceWithRules( "||example.com^$csp=script-src 'nonce-abcdef' 'unsafe-eval' 'self'"); UpdateCustomAdBlockInstanceWithRules( @@ -2240,8 +2232,6 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringDynamic) { // Test cosmetic filtering on elements added dynamically, using a rule from the // custom filters IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringDynamicCustom) { - EnableDeveloperMode(true); - ASSERT_TRUE(g_brave_browser_process->ad_block_service() ->custom_filters_provider() ->UpdateCustomFilters("##.blockme")); @@ -2705,7 +2695,6 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, // exception policy. IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CustomCosmeticFilteringOverridesImportant) { - EnableDeveloperMode(true); UpdateCustomAdBlockInstanceWithRules("###inline-block-important"); GURL tab_url = @@ -3016,8 +3005,6 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, ListEnabled) { // Content Picker and the context menu are disabled for Android. #if !BUILDFLAG(IS_ANDROID) IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, ContentPicker) { - EnableDeveloperMode(true); - const GURL tab_url = embedded_test_server()->GetURL("a.com", "/cosmetic_filtering.html"); NavigateToURL(tab_url); diff --git a/browser/ui/webui/brave_adblock_ui.cc b/browser/ui/webui/brave_adblock_ui.cc index 82b8021e253b..a40cca776d37 100644 --- a/browser/ui/webui/brave_adblock_ui.cc +++ b/browser/ui/webui/brave_adblock_ui.cc @@ -177,13 +177,14 @@ void AdblockDOMHandler::HandleGetListSubscriptions( void AdblockDOMHandler::HandleUpdateCustomFilters( const base::Value::List& args) { DCHECK_EQ(args.size(), 1U); - if (!args[0].is_string()) + if (!args[0].is_string()) { return; + } std::string custom_filters = args[0].GetString(); g_brave_browser_process->ad_block_service() ->custom_filters_provider() - ->UpdateCustomFilters(custom_filters); + ->UpdateCustomFiltersFromSettings(custom_filters); } void AdblockDOMHandler::HandleSubmitNewSubscription( diff --git a/components/brave_shields/content/browser/ad_block_custom_filters_provider.cc b/components/brave_shields/content/browser/ad_block_custom_filters_provider.cc index 867da4d5ba96..0d1359d5ba9e 100644 --- a/components/brave_shields/content/browser/ad_block_custom_filters_provider.cc +++ b/components/brave_shields/content/browser/ad_block_custom_filters_provider.cc @@ -64,7 +64,7 @@ void AdBlockCustomFiltersProvider::CreateSiteExemption( std::string AdBlockCustomFiltersProvider::GetCustomFilters() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!local_state_ || !developer_mode_enabled_) { + if (!local_state_) { return std::string(); } return local_state_->GetString(prefs::kAdBlockCustomFilters); @@ -83,6 +83,14 @@ bool AdBlockCustomFiltersProvider::UpdateCustomFilters( return true; } +bool AdBlockCustomFiltersProvider::UpdateCustomFiltersFromSettings( + const std::string& custom_filters) { + if (!developer_mode_enabled_) { + return false; + } + return UpdateCustomFilters(custom_filters); +} + void AdBlockCustomFiltersProvider::LoadFilterSet( base::OnceCallback< void(base::OnceCallback*)>)> cb) { diff --git a/components/brave_shields/content/browser/ad_block_custom_filters_provider.h b/components/brave_shields/content/browser/ad_block_custom_filters_provider.h index dc435058e915..8fc7366136a2 100644 --- a/components/brave_shields/content/browser/ad_block_custom_filters_provider.h +++ b/components/brave_shields/content/browser/ad_block_custom_filters_provider.h @@ -36,6 +36,10 @@ class AdBlockCustomFiltersProvider : public AdBlockFiltersProvider { std::string GetCustomFilters(); bool UpdateCustomFilters(const std::string& custom_filters); + // Used in BraveAdBlockHandler and updates the manually edited custom filters + // only if developer mode is turned on. + bool UpdateCustomFiltersFromSettings(const std::string& custom_filters); + // AdBlockFiltersProvider void LoadFilterSet( base::OnceCallback local_state_; bool developer_mode_enabled_ = false; From c31708bfaa645750bde0cd81e5b113faf192eb37 Mon Sep 17 00:00:00 2001 From: boocmp Date: Mon, 18 Nov 2024 21:35:11 +0700 Subject: [PATCH 15/21] Android compilation. --- browser/brave_shields/ad_block_service_browsertest.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/brave_shields/ad_block_service_browsertest.cc b/browser/brave_shields/ad_block_service_browsertest.cc index 735c253f738d..c34c88e79915 100644 --- a/browser/brave_shields/ad_block_service_browsertest.cc +++ b/browser/brave_shields/ad_block_service_browsertest.cc @@ -275,8 +275,8 @@ void AdBlockServiceTest::UpdateAdBlockInstanceWithRules( } void AdBlockServiceTest::EnableDeveloperMode(bool enabled) { - browser()->profile()->GetPrefs()->SetBoolean( - brave_shields::prefs::kAdBlockDeveloperMode, enabled); + profile()->GetPrefs()->SetBoolean(brave_shields::prefs::kAdBlockDeveloperMode, + enabled); } void AdBlockServiceTest::UpdateCustomAdBlockInstanceWithRules( From fe5a7d9548aefa48d031a98f2134f94812c69ccf Mon Sep 17 00:00:00 2001 From: boocmp Date: Tue, 19 Nov 2024 08:32:48 +0700 Subject: [PATCH 16/21] Changed the devmode toggle's desc. Exposed feature to the flags UI. --- app/brave_settings_strings.grdp | 2 +- browser/about_flags.cc | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/brave_settings_strings.grdp b/app/brave_settings_strings.grdp index 6529687e4e7c..e00a51d0fa37 100644 --- a/app/brave_settings_strings.grdp +++ b/app/brave_settings_strings.grdp @@ -581,7 +581,7 @@ Developer mode
- Allows to add custom filtering rules and scriptlets + Allow adding custom filters and scriptlets Custom scriptlets diff --git a/browser/about_flags.cc b/browser/about_flags.cc index 362447e1f43e..00a26143ff7b 100644 --- a/browser/about_flags.cc +++ b/browser/about_flags.cc @@ -452,6 +452,16 @@ FEATURE_VALUE_TYPE(kExtensionsManifestV2), \ })) +#define BRAVE_ADBLOCK_CUSTOM_SCRIPTLETS \ + EXPAND_FEATURE_ENTRIES({ \ + "brave-adblock-custom-scriptlets", \ + "Brave Adblock Custom Scriptlets", \ + "Allows adding custom scriptlets from settings", \ + kOsDesktop | kOsAndroid, \ + FEATURE_VALUE_TYPE( \ + brave_shields::features::kCosmeticFilteringCustomScriptlets), \ + }) + // Keep the last item empty. #define LAST_BRAVE_FEATURE_ENTRIES_ITEM @@ -994,6 +1004,7 @@ BRAVE_MIDDLE_CLICK_AUTOSCROLL_FEATURE_ENTRY \ BRAVE_EXTENSIONS_MANIFEST_V2 \ BRAVE_WORKAROUND_NEW_WINDOW_FLASH \ + BRAVE_ADBLOCK_CUSTOM_SCRIPTLETS \ LAST_BRAVE_FEATURE_ENTRIES_ITEM // Keep it as the last item. namespace flags_ui { namespace { From 825fd9385df14a389ec7e17a00284ff860a150ee Mon Sep 17 00:00:00 2001 From: boocmp Date: Mon, 25 Nov 2024 15:42:44 +0700 Subject: [PATCH 17/21] Make custom filters readonly if dev-mode is disabled. --- .../brave_adblock_subpage.html | 24 ++++++++++--------- .../components/brave_adblock_editor.html | 5 +++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html index 42a66e1fd920..85a44c17a409 100644 --- a/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html +++ b/browser/resources/settings/default_brave_shields_page/brave_adblock_subpage.html @@ -325,19 +325,21 @@ on-settings-boolean-control-change="onDeveloperModeChange_"> -