From 5d3edde1f9e262971844a2e768f3d5ce859832ce Mon Sep 17 00:00:00 2001 From: brave-builds Date: Mon, 16 Sep 2024 09:48:12 +0000 Subject: [PATCH] Uplift of #25411 (squashed) to beta --- app/brave_command_ids.h | 3 + app/brave_generated_resources.grd | 5 + browser/brave_content_browser_client.cc | 6 ++ .../ad_block_service_browsertest.cc | 79 ++++++++++++-- .../cosmetic_filters_tab_helper.cc | 100 ++++++++++++++++++ .../cosmetic_filters_tab_helper.h | 45 ++++++++ browser/cosmetic_filters/sources.gni | 17 +++ browser/extensions/BUILD.gn | 2 - browser/extensions/api/brave_shields_api.cc | 49 --------- browser/extensions/api/brave_shields_api.h | 40 ------- browser/extensions/brave_shields_apitest.cc | 43 -------- browser/sources.gni | 3 + .../render_view_context_menu.cc | 41 +++++++ .../render_view_context_menu.h | 16 ++- common/extensions/api/_api_features.json | 9 -- common/extensions/api/api_sources.gni | 1 - common/extensions/api/brave_shields.json | 34 ------ .../extension/brave_extension/BUILD.gn | 4 - .../extension/brave_extension/background.ts | 8 +- .../background/api/cosmeticFilterAPI.ts | 8 -- .../brave_extension/background/events.ts | 5 - .../background/events/cosmeticFilterEvents.ts | 53 ---------- .../extension/brave_extension/manifest.json | 4 - .../brave_extension/extension/resources.grd | 1 - .../ad_block_custom_filters_provider.cc | 8 +- .../ad_block_custom_filters_provider.h | 3 +- .../content/browser/ad_block_service.cc | 5 + .../content/browser/ad_block_service.h | 1 + .../common/cosmetic_filters.mojom | 17 +++ .../renderer/cosmetic_filters_js_handler.cc | 60 ++++++++++- .../renderer/cosmetic_filters_js_handler.h | 17 ++- .../cosmetic_filters/resources/data/BUILD.gn | 19 +++- .../resources/data/element_picker.html} | 21 ++-- .../resources/data/element_picker.ts} | 50 +++++++-- components/definitions/chromel.d.ts | 6 +- .../events/cosmeticFilterEvents_test.ts | 53 ---------- test/BUILD.gn | 1 - 37 files changed, 470 insertions(+), 367 deletions(-) create mode 100644 browser/cosmetic_filters/cosmetic_filters_tab_helper.cc create mode 100644 browser/cosmetic_filters/cosmetic_filters_tab_helper.h create mode 100644 browser/cosmetic_filters/sources.gni delete mode 100644 browser/extensions/api/brave_shields_api.cc delete mode 100644 browser/extensions/api/brave_shields_api.h delete mode 100644 browser/extensions/brave_shields_apitest.cc delete mode 100644 common/extensions/api/brave_shields.json delete mode 100644 components/brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI.ts delete mode 100644 components/brave_extension/extension/brave_extension/background/events.ts delete mode 100644 components/brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents.ts rename components/{brave_extension/extension/brave_extension/elementPicker.html => cosmetic_filters/resources/data/element_picker.html} (88%) rename components/{brave_extension/extension/brave_extension/content_element_picker.ts => cosmetic_filters/resources/data/element_picker.ts} (92%) delete mode 100644 components/test/brave_extension/background/events/cosmeticFilterEvents_test.ts diff --git a/app/brave_command_ids.h b/app/brave_command_ids.h index 1605b9d2d930..88f832c60d8b 100644 --- a/app/brave_command_ids.h +++ b/app/brave_command_ids.h @@ -134,6 +134,9 @@ // Wayback machine #define IDC_SHOW_WAYBACK_MACHINE_BUBBLE 56350 +// Adblock (user-defined cosmetic filters) +#define IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS 56351 + #define IDC_BRAVE_COMMANDS_LAST 57000 #endif // BRAVE_APP_BRAVE_COMMAND_IDS_H_ diff --git a/app/brave_generated_resources.grd b/app/brave_generated_resources.grd index 3d5ae0891386..4aa1c934a233 100644 --- a/app/brave_generated_resources.grd +++ b/app/brave_generated_resources.grd @@ -346,6 +346,11 @@ Or change later at $2brave://settings/ext Check details + + + Block elements + + .onion diff --git a/browser/brave_content_browser_client.cc b/browser/brave_content_browser_client.cc index 406108a0f11d..87f1c8c273f8 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -25,6 +25,7 @@ #include "brave/browser/brave_wallet/brave_wallet_context_utils.h" #include "brave/browser/brave_wallet/brave_wallet_provider_delegate_impl.h" #include "brave/browser/brave_wallet/brave_wallet_service_factory.h" +#include "brave/browser/cosmetic_filters/cosmetic_filters_tab_helper.h" #include "brave/browser/debounce/debounce_service_factory.h" #include "brave/browser/ephemeral_storage/ephemeral_storage_service_factory.h" #include "brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.h" @@ -587,6 +588,11 @@ void BraveContentBrowserClient:: &render_frame_host)); #endif // BUILDFLAG(ENABLE_PLAYLIST) + associated_registry.AddInterface< + cosmetic_filters::mojom::CosmeticFiltersHandler>(base::BindRepeating( + &cosmetic_filters::CosmeticFiltersTabHelper::BindCosmeticFiltersHandler, + &render_frame_host)); + ChromeContentBrowserClient:: RegisterAssociatedInterfaceBindersForRenderFrameHost(render_frame_host, associated_registry); diff --git a/browser/brave_shields/ad_block_service_browsertest.cc b/browser/brave_shields/ad_block_service_browsertest.cc index 705378ad5eef..863fd1d5c743 100644 --- a/browser/brave_shields/ad_block_service_browsertest.cc +++ b/browser/brave_shields/ad_block_service_browsertest.cc @@ -22,6 +22,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/thread_test_helper.h" #include "base/threading/thread_restrictions.h" +#include "brave/app/brave_command_ids.h" #include "brave/browser/brave_browser_process.h" #include "brave/browser/net/brave_ad_block_tp_network_delegate_helper.h" #include "brave/components/brave_shields/content/browser/ad_block_custom_filters_provider.h" @@ -46,6 +47,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_isolated_world_ids.h" #include "chrome/test/base/chrome_test_utils.h" #include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_service.h" @@ -58,6 +60,7 @@ #if BUILDFLAG(IS_ANDROID) #include "chrome/test/base/android/android_browser_test.h" #else +#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" @@ -104,6 +107,16 @@ using brave_shields::features::kBraveAdblockDefault1pBlocking; using brave_shields::features::kBraveAdblockScriptletDebugLogs; using brave_shields::features::kCosmeticFilteringJsPerformance; +namespace { +void WaitForSelectorBlocked(const content::ToRenderFrameHost& target, + const std::string& selector) { + const char kTemplate[] = R"(waitCSSSelector($1, 'display', 'none'))"; + + ASSERT_TRUE( + EvalJs(target, content::JsReplace(kTemplate, selector)).ExtractBool()); +} +} // namespace + AdBlockServiceTest::AdBlockServiceTest() : https_server_(net::EmbeddedTestServer::Type::TYPE_HTTPS) {} AdBlockServiceTest::~AdBlockServiceTest() = default; @@ -1713,10 +1726,10 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, RemoveparamSubresource) { #define MAYBE_RemoveparamTopLevelNavigation \ DISABLED_RemoveparamTopLevelNavigation #define MAYBE_DefaultRemoveparamFromCustom DISABLED_DefaultRemoveparamFromCustom -#else +#else // BUILDFLAG(IS_ANDROID) #define MAYBE_RemoveparamTopLevelNavigation RemoveparamTopLevelNavigation #define MAYBE_DefaultRemoveparamFromCustom DefaultRemoveparamFromCustom -#endif +#endif // BUILDFLAG(IS_ANDROID) // `$removeparam` should be respected for top-level navigations IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, @@ -2719,6 +2732,60 @@ 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) { + const GURL tab_url = + embedded_test_server()->GetURL("a.com", "/cosmetic_filtering.html"); + NavigateToURL(tab_url); + const char kPickerIsInjected[] = + "document.getElementById('brave-element-picker') != null"; + ASSERT_FALSE( + content::EvalJs(web_contents(), kPickerIsInjected).ExtractBool()); + + const auto click_menu = [&]() { + content::ContextMenuParams params; + params.page_url = tab_url; + TestRenderViewContextMenu menu(*web_contents()->GetPrimaryMainFrame(), + params); + menu.Init(); + EXPECT_TRUE(menu.IsItemEnabled(IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS)); + menu.ExecuteCommand(IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS, 0); + }; + + click_menu(); + + ASSERT_TRUE(content::EvalJs(web_contents(), kPickerIsInjected).ExtractBool()); + + EXPECT_TRUE(content::EvalJs(web_contents(), + "checkSelector('#ad-banner', 'display', 'block')") + .ExtractBool()); + + // Emulate selecting some element and clicking `Create` button. + ASSERT_TRUE(content::ExecJs(web_contents(), + "cf_worker.addSiteCosmeticFilter('#ad-banner')", + content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, + ISOLATED_WORLD_ID_BRAVE_INTERNAL)); + + // Reload the page and check the selector is blocked by the new rule. + NavigateToURL(tab_url); + WaitForSelectorBlocked(web_contents(), "#ad-banner"); + EXPECT_FALSE( + content::EvalJs(web_contents(), kPickerIsInjected).ExtractBool()); + + click_menu(); + // Emulate clicking `Manage filters`. + ASSERT_TRUE(content::ExecJs(web_contents(), "cf_worker.manageCustomFilters()", + content::EXECUTE_SCRIPT_DEFAULT_OPTIONS, + ISOLATED_WORLD_ID_BRAVE_INTERNAL)); + + ASSERT_EQ(2, browser()->tab_strip_model()->count()); + ASSERT_TRUE(content::WaitForLoadStop(web_contents())); + EXPECT_EQ(web_contents()->GetLastCommittedURL(), + "chrome://settings/shields/filters"); +} +#endif // !BUILDFLAG(IS_ANDROID) + class AdBlockServiceTestJsPerformance : public AdBlockServiceTest { public: AdBlockServiceTestJsPerformance() { @@ -2743,14 +2810,6 @@ class AdBlockServiceTestJsPerformance : public AdBlockServiceTest { target, content::JsReplace(kTemplate, start_number, end_number))); } - void WaitForSelectorBlocked(const content::ToRenderFrameHost& target, - const std::string& selector) const { - const char kTemplate[] = R"(waitCSSSelector($1, 'display', 'none'))"; - - ASSERT_TRUE( - EvalJs(target, content::JsReplace(kTemplate, selector)).ExtractBool()); - } - void NonBlockingDelay(const base::TimeDelta& delay) { base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( diff --git a/browser/cosmetic_filters/cosmetic_filters_tab_helper.cc b/browser/cosmetic_filters/cosmetic_filters_tab_helper.cc new file mode 100644 index 000000000000..3d610a22d95e --- /dev/null +++ b/browser/cosmetic_filters/cosmetic_filters_tab_helper.cc @@ -0,0 +1,100 @@ +// 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/browser/cosmetic_filters/cosmetic_filters_tab_helper.h" + +#include + +#include "base/strings/string_util.h" +#include "brave/browser/brave_browser_process.h" +#include "brave/components/brave_shields/content/browser/ad_block_service.h" +#include "content/public/browser/web_contents.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" + +#if !BUILDFLAG(IS_ANDROID) +#include "brave/browser/ui/brave_pages.h" +#include "chrome/browser/ui/browser_finder.h" +#endif // !BUILDFLAG(IS_ANDROID) + +namespace cosmetic_filters { + +namespace { +std::string SanitizeSelector(const std::string selector) { + if (!base::IsStringUTF8(selector)) { + return {}; + } + + // The rules are parsed by adblock-rust via lines() method. + // The method checks a newline byte (the 0xA byte) or CRLF (0xD, 0xA bytes). + // https://doc.rust-lang.org/stable/std/io/trait.BufRead.html#method.lines + if (base::Contains(selector, '\n')) { + return {}; + } + + return selector; +} +} // namespace + +// static +void CosmeticFiltersTabHelper::LaunchContentPicker( + content::WebContents* web_contents) { + CosmeticFiltersTabHelper::CreateForWebContents(web_contents); + if (auto* main_rfh = web_contents->GetPrimaryMainFrame()) { + mojo::AssociatedRemote cosmetic_filter_agent; + main_rfh->GetRemoteAssociatedInterfaces()->GetInterface( + &cosmetic_filter_agent); + cosmetic_filter_agent->LaunchContentPicker(); + } +} + +// static +void CosmeticFiltersTabHelper::BindCosmeticFiltersHandler( + content::RenderFrameHost* rfh, + mojo::PendingAssociatedReceiver receiver) { + auto* web_contents = content::WebContents::FromRenderFrameHost(rfh); + if (!web_contents) { + return; + } + CosmeticFiltersTabHelper::CreateForWebContents(web_contents); + if (auto* tab_helper = + CosmeticFiltersTabHelper::FromWebContents(web_contents)) { + tab_helper->receivers_.Bind(rfh, std::move(receiver)); + } +} + +void CosmeticFiltersTabHelper::AddSiteCosmeticFilter( + const std::string& filter) { + // `filter` doesn't have a host, because we don't trust a renderer process. + // Instead, we calculate and add the host explicitly here. + const auto* sender_rfh = receivers_.GetCurrentTargetFrame(); + CHECK(sender_rfh); + const std::string sanitized_filter = SanitizeSelector(filter); + if (!sanitized_filter.empty()) { + const auto host = sender_rfh->GetLastCommittedOrigin().host(); + g_brave_browser_process->ad_block_service()->AddUserCosmeticFilter( + host + "##" + sanitized_filter); + } +} + +void CosmeticFiltersTabHelper::ManageCustomFilters() { +#if !BUILDFLAG(IS_ANDROID) + Browser* browser = chrome::FindLastActive(); + if (browser) { + brave::ShowBraveAdblock(browser); + } +#else // !BUILDFLAG(IS_ANDROID) + NOTIMPLEMENTED(); +#endif // !BUILDFLAG(IS_ANDROID) +} + +CosmeticFiltersTabHelper::CosmeticFiltersTabHelper( + content::WebContents* web_contents) + : content::WebContentsUserData(*web_contents), + receivers_(web_contents, this) {} + +CosmeticFiltersTabHelper::~CosmeticFiltersTabHelper() = default; + +WEB_CONTENTS_USER_DATA_KEY_IMPL(CosmeticFiltersTabHelper); +} // namespace cosmetic_filters diff --git a/browser/cosmetic_filters/cosmetic_filters_tab_helper.h b/browser/cosmetic_filters/cosmetic_filters_tab_helper.h new file mode 100644 index 000000000000..2a9a8b8da8a7 --- /dev/null +++ b/browser/cosmetic_filters/cosmetic_filters_tab_helper.h @@ -0,0 +1,45 @@ +// 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_BROWSER_COSMETIC_FILTERS_COSMETIC_FILTERS_TAB_HELPER_H_ +#define BRAVE_BROWSER_COSMETIC_FILTERS_COSMETIC_FILTERS_TAB_HELPER_H_ + +#include + +#include "brave/components/cosmetic_filters/common/cosmetic_filters.mojom.h" +#include "content/public/browser/render_frame_host_receiver_set.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace cosmetic_filters { +// A tab helper to communicate with instances of CosmeticFiltersJSHandler. +// Currently it's created on demand and used for Content Picker feature. +class CosmeticFiltersTabHelper + : public content::WebContentsUserData, + public mojom::CosmeticFiltersHandler { + public: + CosmeticFiltersTabHelper(const CosmeticFiltersTabHelper&) = delete; + CosmeticFiltersTabHelper& operator=(const CosmeticFiltersTabHelper&) = delete; + ~CosmeticFiltersTabHelper() override; + + static void LaunchContentPicker(content::WebContents* web_contents); + + static void BindCosmeticFiltersHandler( + content::RenderFrameHost* rfh, + mojo::PendingAssociatedReceiver receiver); + + private: + void AddSiteCosmeticFilter(const std::string& filter) override; + void ManageCustomFilters() override; + + friend class content::WebContentsUserData; + + explicit CosmeticFiltersTabHelper(content::WebContents* web_contents); + + content::RenderFrameHostReceiverSet receivers_; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; +} // namespace cosmetic_filters +#endif // BRAVE_BROWSER_COSMETIC_FILTERS_COSMETIC_FILTERS_TAB_HELPER_H_ diff --git a/browser/cosmetic_filters/sources.gni b/browser/cosmetic_filters/sources.gni new file mode 100644 index 000000000000..707a20806ee3 --- /dev/null +++ b/browser/cosmetic_filters/sources.gni @@ -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/. + +brave_browser_cosmetic_filters_sources = [ + "//brave/browser/cosmetic_filters/cosmetic_filters_tab_helper.cc", + "//brave/browser/cosmetic_filters/cosmetic_filters_tab_helper.h", +] + +brave_browser_cosmetic_filters_deps = [ + "//base", + "//brave/browser:browser_process", + "//brave/components/cosmetic_filters/common:mojom", + "//chrome/browser/ui", + "//third_party/blink/public/common", +] diff --git a/browser/extensions/BUILD.gn b/browser/extensions/BUILD.gn index 166d35b87df6..ee2b98c6c1fd 100644 --- a/browser/extensions/BUILD.gn +++ b/browser/extensions/BUILD.gn @@ -22,8 +22,6 @@ source_set("extensions") { "api/brave_extensions_api_client.h", "api/brave_rewards_api.cc", "api/brave_rewards_api.h", - "api/brave_shields_api.cc", - "api/brave_shields_api.h", "api/brave_talk_api.cc", "api/brave_talk_api.h", "api/brave_theme_api.cc", diff --git a/browser/extensions/api/brave_shields_api.cc b/browser/extensions/api/brave_shields_api.cc deleted file mode 100644 index 90f6097314ba..000000000000 --- a/browser/extensions/api/brave_shields_api.cc +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ - -#include "brave/browser/extensions/api/brave_shields_api.h" - -#include -#include - -#include "brave/browser/brave_browser_process.h" -#include "brave/browser/ui/brave_pages.h" -#include "brave/common/extensions/api/brave_shields.h" -#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 "chrome/browser/browser_process.h" -#include "chrome/browser/content_settings/cookie_settings_factory.h" -#include "chrome/browser/ui/browser_finder.h" -#include "components/content_settings/core/browser/cookie_settings.h" -#include "content/public/browser/render_frame_host.h" - -namespace extensions::api { - -ExtensionFunction::ResponseAction -BraveShieldsAddSiteCosmeticFilterFunction::Run() { - std::optional params = - brave_shields::AddSiteCosmeticFilter::Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(params); - if (const auto* rfh = render_frame_host()) { - g_brave_browser_process->ad_block_service() - ->custom_filters_provider() - ->HideElementOnHost(params->css_selector, - rfh->GetLastCommittedURL().host()); - } - - return RespondNow(NoArguments()); -} - -ExtensionFunction::ResponseAction -BraveShieldsOpenFilterManagementPageFunction::Run() { - Browser* browser = chrome::FindLastActive(); - if (browser) { - brave::ShowBraveAdblock(browser); - } - - return RespondNow(NoArguments()); -} - -} // namespace extensions::api diff --git a/browser/extensions/api/brave_shields_api.h b/browser/extensions/api/brave_shields_api.h deleted file mode 100644 index 04c3248d84f3..000000000000 --- a/browser/extensions/api/brave_shields_api.h +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ - -#ifndef BRAVE_BROWSER_EXTENSIONS_API_BRAVE_SHIELDS_API_H_ -#define BRAVE_BROWSER_EXTENSIONS_API_BRAVE_SHIELDS_API_H_ - -#include -#include - -#include "extensions/browser/extension_function.h" - -namespace extensions { -namespace api { - -class BraveShieldsAddSiteCosmeticFilterFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveShields.addSiteCosmeticFilter", UNKNOWN) - - protected: - ~BraveShieldsAddSiteCosmeticFilterFunction() override {} - - ResponseAction Run() override; -}; - -class BraveShieldsOpenFilterManagementPageFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveShields.openFilterManagementPage", UNKNOWN) - - protected: - ~BraveShieldsOpenFilterManagementPageFunction() override {} - - ResponseAction Run() override; -}; - -} // namespace api -} // namespace extensions - -#endif // BRAVE_BROWSER_EXTENSIONS_API_BRAVE_SHIELDS_API_H_ diff --git a/browser/extensions/brave_shields_apitest.cc b/browser/extensions/brave_shields_apitest.cc deleted file mode 100644 index f1e12924287b..000000000000 --- a/browser/extensions/brave_shields_apitest.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright (c) 2019 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 http://mozilla.org/MPL/2.0/. */ - -#include "base/path_service.h" -#include "brave/components/constants/brave_paths.h" -#include "chrome/browser/extensions/extension_apitest.h" -#include "content/public/test/browser_test.h" -#include "extensions/test/result_catcher.h" - -namespace extensions { -namespace { - -class BraveShieldsExtensionApiTest : public ExtensionApiTest { - public: - void SetUpOnMainThread() override { - ExtensionApiTest::SetUpOnMainThread(); - base::PathService::Get(brave::DIR_TEST_DATA, &extension_dir_); - extension_dir_ = extension_dir_.AppendASCII("extensions/api_test"); - } - base::FilePath extension_dir_; -}; - -IN_PROC_BROWSER_TEST_F(BraveShieldsExtensionApiTest, BraveExtensionHasAccess) { - ResultCatcher catcher; - const Extension* extension = - LoadExtension(extension_dir_.AppendASCII("braveShields")); - ASSERT_TRUE(extension); - ASSERT_TRUE(catcher.GetNextResult()) << message_; -} - -IN_PROC_BROWSER_TEST_F(BraveShieldsExtensionApiTest, - NotBraveExtensionHasNoAccess) { - ResultCatcher catcher; - const Extension* extension = - LoadExtension(extension_dir_.AppendASCII("notBraveShields")); - ASSERT_TRUE(extension); - ASSERT_TRUE(catcher.GetNextResult()) << message_; -} - -} // namespace -} // namespace extensions diff --git a/browser/sources.gni b/browser/sources.gni index 9a833a02a30b..53c9e6b8c020 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -17,6 +17,7 @@ import("//brave/browser/brave_vpn/sources.gni") import("//brave/browser/brave_wallet/android/sources.gni") import("//brave/browser/brave_wallet/notifications/sources.gni") import("//brave/browser/browsing_data/sources.gni") +import("//brave/browser/cosmetic_filters/sources.gni") import("//brave/browser/debounce/sources.gni") import("//brave/browser/download/sources.gni") import("//brave/browser/ephemeral_storage/sources.gni") @@ -501,6 +502,7 @@ brave_chrome_browser_sources += brave_browser_brave_stats_updater_sources brave_chrome_browser_sources += brave_browser_brave_vpn_sources brave_chrome_browser_sources += brave_browser_brave_wallet_sources brave_chrome_browser_sources += brave_browser_browsing_data_sources +brave_chrome_browser_sources += brave_browser_cosmetic_filters_sources brave_chrome_browser_sources += brave_browser_debounce_sources brave_chrome_browser_sources += brave_browser_download_sources brave_chrome_browser_sources += brave_browser_ephemeral_storage_sources @@ -538,6 +540,7 @@ brave_chrome_browser_deps += brave_browser_brave_stats_updater_deps brave_chrome_browser_deps += brave_browser_brave_vpn_deps brave_chrome_browser_deps += brave_browser_brave_wallet_deps brave_chrome_browser_deps += brave_browser_browsing_data_deps +brave_chrome_browser_deps += brave_browser_cosmetic_filters_deps brave_chrome_browser_deps += brave_browser_debounce_deps brave_chrome_browser_deps += brave_browser_download_deps brave_chrome_browser_deps += brave_browser_ephemeral_storage_deps diff --git a/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.cc index 6341d8ddc9b7..d7aeb6cac08a 100644 --- a/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.cc +++ b/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.cc @@ -12,8 +12,11 @@ #include "base/feature_list.h" #include "base/strings/string_util.h" #include "brave/browser/autocomplete/brave_autocomplete_scheme_classifier.h" +#include "brave/browser/brave_shields/brave_shields_tab_helper.h" +#include "brave/browser/cosmetic_filters/cosmetic_filters_tab_helper.h" #include "brave/browser/profiles/profile_util.h" #include "brave/browser/renderer_context_menu/brave_spelling_options_submenu_observer.h" +#include "brave/browser/ui/brave_pages.h" #include "brave/browser/ui/browser_commands.h" #include "brave/browser/ui/browser_dialogs.h" #include "brave/browser/ui/tabs/features.h" @@ -421,6 +424,8 @@ bool BraveRenderViewContextMenu::IsCommandIdEnabled(int id) const { #endif case IDC_CONTENT_CONTEXT_OPENLINK_SPLIT_VIEW: return CanOpenSplitViewForWebContents(source_web_contents_->GetWeakPtr()); + case IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS: + return true; default: return RenderViewContextMenu_Chromium::IsCommandIdEnabled(id); } @@ -491,6 +496,10 @@ void BraveRenderViewContextMenu::ExecuteCommand(int id, int event_flags) { case IDC_CONTENT_CONTEXT_OPENLINK_SPLIT_VIEW: OpenLinkInSplitView(source_web_contents_->GetWeakPtr(), params_.link_url); break; + case IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS: + cosmetic_filters::CosmeticFiltersTabHelper::LaunchContentPicker( + source_web_contents_); + break; default: RenderViewContextMenu_Chromium::ExecuteCommand(id, event_flags); } @@ -682,6 +691,38 @@ void BraveRenderViewContextMenu::AddAccessibilityLabelsServiceItem( // Suppress adding "Get image descriptions from Brave" } +void BraveRenderViewContextMenu::AppendDeveloperItems() { + RenderViewContextMenu_Chromium::AppendDeveloperItems(); + + auto* shields_tab_helper = + brave_shields::BraveShieldsTabHelper::FromWebContents( + source_web_contents_); + bool add_block_elements = + shields_tab_helper && shields_tab_helper->GetAdBlockMode() != + brave_shields::mojom::AdBlockMode::ALLOW; +#if BUILDFLAG(IS_ANDROID) + // Content picker doesn't available for Android. + add_block_elements = false; +#endif // BUILDFLAG(IS_ANDROID) + add_block_elements &= + params_.selection_text.empty() || !params_.link_url.is_empty(); + + const auto page_url = source_web_contents_->GetLastCommittedURL(); + add_block_elements &= page_url.SchemeIsHTTPOrHTTPS(); + if (add_block_elements) { + std::optional inspect_index = + menu_model_.GetIndexOfCommandId(IDC_CONTENT_CONTEXT_INSPECTELEMENT); + if (inspect_index) { + menu_model_.InsertItemWithStringIdAt(*inspect_index, + IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS, + IDS_ADBLOCK_CONTEXT_BLOCK_ELEMENTS); + } else { + menu_model_.AddItemWithStringId(IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS, + IDS_ADBLOCK_CONTEXT_BLOCK_ELEMENTS); + } + } +} + void BraveRenderViewContextMenu::InitMenu() { RenderViewContextMenu_Chromium::InitMenu(); diff --git a/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.h index 74bb22b74ef0..e282233464f0 100644 --- a/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.h +++ b/chromium_src/chrome/browser/renderer_context_menu/render_view_context_menu.h @@ -25,7 +25,9 @@ class BraveRenderViewContextMenu; base::OnceCallback cb); \ static void RegisterMenuShownCallbackForTesting_unused #define AppendReadingModeItem virtual AppendReadingModeItem +#define AppendDeveloperItems virtual AppendDeveloperItems #include "src/chrome/browser/renderer_context_menu/render_view_context_menu.h" // IWYU pragma: export +#undef AppendDeveloperItems #undef AppendReadingModeItem #undef RegisterMenuShownCallbackForTesting #undef RenderViewContextMenu @@ -50,6 +52,8 @@ class BraveRenderViewContextMenu : public RenderViewContextMenu_Chromium { // Do nothing as we have our own speed reader void AppendReadingModeItem() override {} + void AppendDeveloperItems() override; + private: friend class BraveRenderViewContextMenuTest; // RenderViewContextMenuBase: @@ -60,16 +64,18 @@ class BraveRenderViewContextMenu : public RenderViewContextMenu_Chromium { bool IsAIChatEnabled() const; void ExecuteAIChatCommand(int command); void BuildAIChatMenu(); +#endif // BUILDFLAG(ENABLE_AI_CHAT) + +#if BUILDFLAG(ENABLE_TEXT_RECOGNITION) + void CopyTextFromImage(); +#endif +#if BUILDFLAG(ENABLE_AI_CHAT) ui::SimpleMenuModel ai_chat_submenu_model_; ui::SimpleMenuModel ai_chat_change_tone_submenu_model_; ui::SimpleMenuModel ai_chat_change_length_submenu_model_; ui::SimpleMenuModel ai_chat_social_media_post_submenu_model_; -#endif - -#if BUILDFLAG(ENABLE_TEXT_RECOGNITION) - void CopyTextFromImage(); -#endif +#endif // BUILDFLAG(ENABLE_AI_CHAT) }; // Use our own subclass as the real RenderViewContextMenu. diff --git a/common/extensions/api/_api_features.json b/common/extensions/api/_api_features.json index e21d2cd4747a..4804af72f729 100644 --- a/common/extensions/api/_api_features.json +++ b/common/extensions/api/_api_features.json @@ -10,15 +10,6 @@ // WebTorrent 3D9518A72EB02667A773B69DBA9E72E0F4A37423: echo -n lgjmpdmojkpocjcopdikifhejkkjglho | openssl sha1 | tr '[:lower:]' '[:upper:]' { - "braveShields": { - "channel": "stable", - "dependencies": [], - "component_extensions_auto_granted": false, - "contexts": ["privileged_extension", "content_script"], - "allowlist": [ - "A321D47A2B4CA86898167A55CA8B2E02385EA7CD" - ] - }, "braveRewards": [ { "channel": "stable", diff --git a/common/extensions/api/api_sources.gni b/common/extensions/api/api_sources.gni index d506f49bd9e6..8d5d8eada427 100644 --- a/common/extensions/api/api_sources.gni +++ b/common/extensions/api/api_sources.gni @@ -12,7 +12,6 @@ brave_extensions_api_schema_include_rules = schema_sources_ = [ "brave_rewards.json", - "brave_shields.json", "brave_talk.json", "brave_theme.json", "greaselion.json", diff --git a/common/extensions/api/brave_shields.json b/common/extensions/api/brave_shields.json deleted file mode 100644 index 11f261a11669..000000000000 --- a/common/extensions/api/brave_shields.json +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. - -[ - { - "namespace": "braveShields", - "description": "Use the chrome.braveShields API to receive notifications about the status of brave shields.", - "compiler_options": { - "implemented_in": "brave/browser/extensions/api/brave_shields_api.h" - }, - "functions": [ - { - "name": "addSiteCosmeticFilter", - "type": "function", - "description": "Saves an element to hide on a page by creating a new filter of the form `host##cssSelector` in the custom filters of brave://adblock", - "parameters": [ - { - "name": "cssSelector", - "type": "string", - "description": "The CSS selector that should be hidden" - } - ] - }, - { - "name": "openFilterManagementPage", - "type": "function", - "description": "Opens brave://adblock for the purpose of adding, editing, or removing custom filters", - "parameters": [] - } - ] - } -] diff --git a/components/brave_extension/extension/brave_extension/BUILD.gn b/components/brave_extension/extension/brave_extension/BUILD.gn index 70fcebc44025..1e7172712018 100644 --- a/components/brave_extension/extension/brave_extension/BUILD.gn +++ b/components/brave_extension/extension/brave_extension/BUILD.gn @@ -14,10 +14,6 @@ transpile_web_ui("brave_extension") { "brave_extension_background", rebase_path("background.ts"), ], - [ - "content_element_picker", - rebase_path("content_element_picker.ts"), - ], [ "webstore", rebase_path("webstore.ts"), diff --git a/components/brave_extension/extension/brave_extension/background.ts b/components/brave_extension/extension/brave_extension/background.ts index 1ff61be78317..5410bfb7b71a 100644 --- a/components/brave_extension/extension/brave_extension/background.ts +++ b/components/brave_extension/extension/brave_extension/background.ts @@ -1,10 +1,10 @@ -/* 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/. */ +// Copyright (c) 2017 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 './background/greaselion' import './background/webDiscoveryProject' -require('./background/events') if (chrome.test) { chrome.test.sendMessage('brave-extension-enabled') diff --git a/components/brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI.ts b/components/brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI.ts deleted file mode 100644 index 8b808fee4486..000000000000 --- a/components/brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* Copyright (c) 2021 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/. */ - -export const openFilterManagementPage = () => { - chrome.braveShields.openFilterManagementPage() -} diff --git a/components/brave_extension/extension/brave_extension/background/events.ts b/components/brave_extension/extension/brave_extension/background/events.ts deleted file mode 100644 index 4adc00cdb94d..000000000000 --- a/components/brave_extension/extension/brave_extension/background/events.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at https://mozilla.org/MPL/2.0/. */ - -require('./events/cosmeticFilterEvents') diff --git a/components/brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents.ts b/components/brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents.ts deleted file mode 100644 index 1ee7daba6e2f..000000000000 --- a/components/brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents.ts +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2019 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 { getLocale } from '../api/localeAPI' -import { openFilterManagementPage } from '../api/cosmeticFilterAPI' - -// parent menu -chrome.contextMenus.create({ - title: 'Brave', - id: 'brave', - contexts: ['all'] -}) -chrome.contextMenus.create({ - title: getLocale('elementPickerMode'), - id: 'elementPickerMode', - parentId: 'brave', - contexts: ['all'], - enabled: !chrome.extension.inIncognitoContext -}) -chrome.contextMenus.create({ - title: getLocale('manageCustomFilters'), - id: 'manageCustomFilters', - parentId: 'brave', - contexts: ['all'] -}) - -chrome.contextMenus.onClicked.addListener( - (info: chrome.contextMenus.OnClickData, tab: chrome.tabs.Tab) => { - onContextMenuClicked(info, tab) - }) - -export function onContextMenuClicked (info: chrome.contextMenus.OnClickData, tab: chrome.tabs.Tab) { - switch (info.menuItemId) { - case 'manageCustomFilters': - openFilterManagementPage() - break - case 'elementPickerMode': { - chrome.tabs.query({ active: true, currentWindow: true }, (tabs: [chrome.tabs.Tab]) => { - const tabId = tabs[0]?.id; - if (tabId !== undefined) { - chrome.scripting.executeScript({target : {tabId}, - files : [ "out/content_element_picker.bundle.js" ], - }) - } - }) - break - } - default: { - console.warn(`[cosmeticFilterEvents] invalid context menu option: ${info.menuItemId}`) - } - } -} diff --git a/components/brave_extension/extension/brave_extension/manifest.json b/components/brave_extension/extension/brave_extension/manifest.json index afd466141b80..b456191b1d4f 100644 --- a/components/brave_extension/extension/brave_extension/manifest.json +++ b/components/brave_extension/extension/brave_extension/manifest.json @@ -12,9 +12,6 @@ "128": "assets/img/icon-128.png", "256": "assets/img/icon-256.png" }, - "web_accessible_resources": [ - "elementPicker.html" - ], "background": { "scripts": [ "out/brave_extension_background.bundle.js" @@ -51,7 +48,6 @@ "chrome://favicon/*", "webRequestBlocking", "unlimitedStorage", - "scripting", "" ], "content_security_policy": "default-src 'self'; font-src 'self' data:; script-src 'self' 'wasm-eval'; style-src 'unsafe-inline'; img-src 'self' data: chrome://favicon/; connect-src * data: blob: filesystem:;", diff --git a/components/brave_extension/extension/resources.grd b/components/brave_extension/extension/resources.grd index 62013653fb51..f92c5cef9823 100644 --- a/components/brave_extension/extension/resources.grd +++ b/components/brave_extension/extension/resources.grd @@ -18,7 +18,6 @@ - AddUserCosmeticFilter(filter); +} + void AdBlockService::GetDebugInfoAsync(GetDebugInfoCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); diff --git a/components/brave_shields/content/browser/ad_block_service.h b/components/brave_shields/content/browser/ad_block_service.h index 63887eaa134b..cf7fbb34bff7 100644 --- a/components/brave_shields/content/browser/ad_block_service.h +++ b/components/brave_shields/content/browser/ad_block_service.h @@ -123,6 +123,7 @@ class AdBlockService { AdBlockCustomFiltersProvider* custom_filters_provider(); void EnableTag(const std::string& tag, bool enabled); + void AddUserCosmeticFilter(const std::string& filter); // Methods for brave://adblock-internals. using GetDebugInfoCallback = diff --git a/components/cosmetic_filters/common/cosmetic_filters.mojom b/components/cosmetic_filters/common/cosmetic_filters.mojom index 90b8b5a05cf4..a7d23f5c7944 100644 --- a/components/cosmetic_filters/common/cosmetic_filters.mojom +++ b/components/cosmetic_filters/common/cosmetic_filters.mojom @@ -16,3 +16,20 @@ interface CosmeticFiltersResources { UrlCosmeticResources(string url, bool aggressive_blocking) => ( mojo_base.mojom.Value result); }; + +interface CosmeticFiltersHandler { + // Adds a user cosmetic rule for the current site. + // (currently from the content picker feature). + AddSiteCosmeticFilter(string filter); + + // Opens the custom filter section in Shields settings . + ManageCustomFilters(); +}; + +// An interface to render frame agent `CosmeticFiltersJSHandler` to control +// cosmetic filter features. +interface CosmeticFiltersAgent { + // Inject the content picker script to allow a user to select unwanted + // DOM elements. + LaunchContentPicker(); +}; diff --git a/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.cc b/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.cc index 0cd9acc89ca7..5fe7f783a15c 100644 --- a/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.cc +++ b/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.cc @@ -18,11 +18,13 @@ #include "base/trace_event/trace_event.h" #include "brave/components/brave_shields/core/common/features.h" #include "brave/components/content_settings/renderer/brave_content_settings_agent_impl.h" -#include "brave/components/cosmetic_filters/resources/grit/cosmetic_filters_generated_map.h" +#include "brave/components/cosmetic_filters/resources/grit/cosmetic_filters_generated.h" #include "content/public/renderer/render_frame.h" #include "gin/arguments.h" #include "gin/function_template.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h" #include "third_party/blink/public/common/web_preferences/web_preferences.h" #include "third_party/blink/public/platform/browser_interface_broker_proxy.h" #include "third_party/blink/public/platform/scheduler/web_agent_group_scheduler.h" @@ -198,6 +200,16 @@ blink::WebContentSettingsClient* GetWebContentSettingsClient( return nullptr; } +mojo::AssociatedRemote +MakeCosmeticFiltersHandler(content::RenderFrame* render_frame) { + mojo::AssociatedRemote + handler; + render_frame->GetRemoteAssociatedInterfaces()->GetInterface(&handler); + CHECK(handler); + handler.reset_on_disconnect(); + return handler; +} + } // namespace namespace cosmetic_filters { @@ -244,6 +256,10 @@ CosmeticFiltersJSHandler::CosmeticFiltersJSHandler( enabled_1st_party_cf_(false) { EnsureConnected(); + render_frame_->GetAssociatedInterfaceRegistry() + ->AddInterface(base::BindRepeating( + &CosmeticFiltersJSHandler::Bind, weak_ptr_factory_.GetWeakPtr())); + const bool perf_tracker_enabled = base::FeatureList::IsEnabled( brave_shields::features::kCosmeticFilteringExtraPerfMetrics); if (perf_tracker_enabled) { @@ -273,6 +289,18 @@ bool CosmeticFiltersJSHandler::OnIsFirstParty(const std::string& url_string) { url, url_, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); } +void CosmeticFiltersJSHandler::OnAddSiteCosmeticFilter( + const std::string& selector) { + const auto host = url_.host(); + auto handler = MakeCosmeticFiltersHandler(render_frame_); + handler->AddSiteCosmeticFilter(selector); +} + +void CosmeticFiltersJSHandler::OnManageCustomFilters() { + auto handler = MakeCosmeticFiltersHandler(render_frame_); + handler->ManageCustomFilters(); +} + void CosmeticFiltersJSHandler::AddJavaScriptObjectToFrame( v8::Local context) { CHECK(render_frame_); @@ -332,6 +360,16 @@ void CosmeticFiltersJSHandler::BindFunctionsToObject( base::BindRepeating(&CosmeticFiltersJSHandler::OnIsFirstParty, base::Unretained(this))); + BindFunctionToObject( + isolate, javascript_object, "addSiteCosmeticFilter", + base::BindRepeating(&CosmeticFiltersJSHandler::OnAddSiteCosmeticFilter, + base::Unretained(this))); + + BindFunctionToObject( + isolate, javascript_object, "manageCustomFilters", + base::BindRepeating(&CosmeticFiltersJSHandler::OnManageCustomFilters, + base::Unretained(this))); + if (perf_tracker_) { BindFunctionToObject( isolate, javascript_object, "onHandleMutationsBegin", @@ -385,6 +423,24 @@ void CosmeticFiltersJSHandler::OnRemoteDisconnect() { EnsureConnected(); } +void CosmeticFiltersJSHandler::Bind( + mojo::PendingAssociatedReceiver receiver) { + receiver_.reset(); + receiver_.Bind(std::move(receiver)); +} + +void CosmeticFiltersJSHandler::LaunchContentPicker() { + blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); + if (web_frame->IsProvisional()) { + return; + } + web_frame->ExecuteScriptInIsolatedWorld( + isolated_world_id_, + blink::WebScriptSource(blink::WebString::FromUTF8( + LoadDataResource(IDR_COSMETIC_FILTERS_ELEMENT_PICKER_BUNDLE_JS))), + blink::BackForwardCacheAware::kAllow); +} + bool CosmeticFiltersJSHandler::ProcessURL( const GURL& url, std::optional callback) { @@ -699,7 +755,7 @@ void CosmeticFiltersJSHandler::ExecuteObservingBundleEntryPoint() { url_.spec()); static base::NoDestructor s_observing_script( - LoadDataResource(kCosmeticFiltersGenerated[0].id)); + LoadDataResource(IDR_COSMETIC_FILTERS_COSMETIC_FILTERS_BUNDLE_JS)); bundle_injected_ = true; web_frame->ExecuteScriptInIsolatedWorld( diff --git a/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h b/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h index ffeb4d102adc..5e218163511f 100644 --- a/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h +++ b/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h @@ -16,6 +16,7 @@ #include "brave/components/cosmetic_filters/common/cosmetic_filters.mojom.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame_observer.h" +#include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "url/gurl.h" #include "v8/include/v8.h" @@ -26,11 +27,11 @@ namespace cosmetic_filters { // a given render_frame. It also does interactions with CosmeticFiltersResources // class that lives in the main process. -class CosmeticFiltersJSHandler { +class CosmeticFiltersJSHandler : public mojom::CosmeticFiltersAgent { public: CosmeticFiltersJSHandler(content::RenderFrame* render_frame, const int32_t isolated_world_id); - ~CosmeticFiltersJSHandler(); + ~CosmeticFiltersJSHandler() override; // Adds the "cf_worker" JavaScript object and its functions to the current // render_frame_. @@ -54,6 +55,12 @@ class CosmeticFiltersJSHandler { bool EnsureConnected(); void OnRemoteDisconnect(); + void Bind( + mojo::PendingAssociatedReceiver receiver); + + // CosmeticFiltersAgent overrides: + void LaunchContentPicker() override; + // Injects content_cosmetic bundle (if needed) and calls the entry point. void ExecuteObservingBundleEntryPoint(); @@ -67,6 +74,8 @@ class CosmeticFiltersJSHandler { void CSSRulesRoutine(const base::Value::Dict& resources_dict); void OnHiddenClassIdSelectors(base::Value::Dict result); bool OnIsFirstParty(const std::string& url_string); + void OnAddSiteCosmeticFilter(const std::string& selector); + void OnManageCustomFilters(); int OnEventBegin(const std::string& event_name); void OnEventEnd(const std::string& event_name, int); @@ -75,8 +84,8 @@ class CosmeticFiltersJSHandler { bool generichide_ = false; raw_ptr render_frame_ = nullptr; - mojo::Remote - cosmetic_filters_resources_; + mojo::Remote cosmetic_filters_resources_; + mojo::AssociatedReceiver receiver_{this}; int32_t isolated_world_id_; bool enabled_1st_party_cf_; std::vector exceptions_; diff --git a/components/cosmetic_filters/resources/data/BUILD.gn b/components/cosmetic_filters/resources/data/BUILD.gn index c623b7c11c95..eae961e4b2f3 100644 --- a/components/cosmetic_filters/resources/data/BUILD.gn +++ b/components/cosmetic_filters/resources/data/BUILD.gn @@ -1,12 +1,23 @@ +# Copyright (c) 2020 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("//brave/components/common/typescript.gni") import("//tools/grit/grit_rule.gni") import("//tools/grit/repack.gni") transpile_web_ui("cosmetic_filters_resources") { - entry_points = [ [ - "cosmetic_filters", - rebase_path("content_cosmetic.ts"), - ] ] + entry_points = [ + [ + "cosmetic_filters", + rebase_path("content_cosmetic.ts"), + ], + [ + "element_picker", + rebase_path("element_picker.ts"), + ], + ] resource_name = "cosmetic_filters" } diff --git a/components/brave_extension/extension/brave_extension/elementPicker.html b/components/cosmetic_filters/resources/data/element_picker.html similarity index 88% rename from components/brave_extension/extension/brave_extension/elementPicker.html rename to components/cosmetic_filters/resources/data/element_picker.html index a59fa7644132..d7b6a5ef1eed 100644 --- a/components/brave_extension/extension/brave_extension/elementPicker.html +++ b/components/cosmetic_filters/resources/data/element_picker.html @@ -51,22 +51,18 @@ width: 50%; } + #buttons { + display: flex; + justify-content: space-between; + } + button { - float: left; background-color: #ccc; cursor: pointer; padding: 4px 6px; margin: 0 0 0 2px; } - button:first-of-type { - margin-left: 0; - } - - button:last-child { - float: right; - } - svg { cursor: crosshair; box-sizing: border-box; @@ -103,7 +99,10 @@
- - +
+ + + +
diff --git a/components/brave_extension/extension/brave_extension/content_element_picker.ts b/components/cosmetic_filters/resources/data/element_picker.ts similarity index 92% rename from components/brave_extension/extension/brave_extension/content_element_picker.ts rename to components/cosmetic_filters/resources/data/element_picker.ts index 63da360e7373..4562032a9927 100644 --- a/components/brave_extension/extension/brave_extension/content_element_picker.ts +++ b/components/cosmetic_filters/resources/data/element_picker.ts @@ -9,7 +9,7 @@ let shadowRoot: ShadowRoot | null const api = { cosmeticFilterCreate: (selector: string) => { - chrome.braveShields.addSiteCosmeticFilter(selector) + cf_worker.addSiteCosmeticFilter(selector) const styleId = 'brave-content-picker-style' let style = document.getElementById(styleId) @@ -20,6 +20,9 @@ const api = { } style.innerText += `${selector} {display: none !important;}` }, + cosmeticFilterManage: () => { + cf_worker.manageCustomFilters() + }, } // When the picker is activated, it eats all pointer events and takes up the @@ -100,13 +103,23 @@ class ElementSelectorBuilder { if (!(mask & SpecificityFlags.Class) && rule.type === Selector.Class) { continue } - if (!(mask & SpecificityFlags.Attributes) && rule.type === Selector.Attributes) { + if ( + !(mask & SpecificityFlags.Attributes) && + rule.type === Selector.Attributes + ) { continue } - if (!(mask & SpecificityFlags.NthOfType) && rule.type === Selector.NthOfType) { + if ( + !(mask & SpecificityFlags.NthOfType) && + rule.type === Selector.NthOfType + ) { continue } - if (this.hasId && (mask & SpecificityFlags.Id) && rule.type === Selector.Class) { + if ( + this.hasId && + mask & SpecificityFlags.Id && + rule.type === Selector.Class + ) { continue } @@ -144,7 +157,8 @@ class ElementSelectorBuilder { } } -// We search for a CSS selector for the target element. We want the most specific identifiers. +// We search for a CSS selector for the target element. We want the most +// specific identifiers. const cssSelectorFromElement = (elem: Element): ElementSelectorBuilder => { const builder = new ElementSelectorBuilder(elem) @@ -225,7 +239,10 @@ const cssSelectorFromElement = (elem: Element): ElementSelectorBuilder => { } } - const querySelectorNoExcept = (node: Element | null, selector: string): Element[] => { + const querySelectorNoExcept = ( + node: Element | null, + selector: string + ): Element[] => { if (node !== null) { try { const r = node.querySelectorAll(selector) @@ -235,9 +252,14 @@ const cssSelectorFromElement = (elem: Element): ElementSelectorBuilder => { return [] } - if (builder.size() === 0 || querySelectorNoExcept(elem.parentElement, builder.toString()).length > 1) { + if ( + builder.size() === 0 || + querySelectorNoExcept(elem.parentElement, builder.toString()).length > 1 + ) { builder.addTag(tag) - if (querySelectorNoExcept(elem.parentElement, builder.toString()).length > 1) { + if ( + querySelectorNoExcept(elem.parentElement, builder.toString()).length > 1 + ) { let index = 1 let sibling: Element | null = elem.previousElementSibling while (sibling !== null) { @@ -280,12 +302,13 @@ const attachElementPicker = () => { // "src" is a web accessible resource since the URI is chrome-extension://. // This ensures a malicious page cannot modify the iframe contents. pickerDiv = document.createElement('div') + pickerDiv.id = 'brave-element-picker' shadowRoot = pickerDiv.attachShadow({ mode: 'closed' }) // Will be resolved by webpack to the file content. // It's a trusted content so it's safe to use innerHTML. // eslint-disable-next-line no-unsanitized/property - shadowRoot.innerHTML = require('./elementPicker.html') + shadowRoot.innerHTML = require('./element_picker.html') const pickerCSSStyle: string = [ 'background: transparent', @@ -400,7 +423,9 @@ const elementPickerHoverCoordsChanged = (x: number, y: number) => { const elementPickerUserSelectedTarget = (specificity: number) => { if (lastHoveredElem instanceof HTMLElement) { const selector = onTargetSelected(lastHoveredElem, specificity) - recalculateAndSendTargets(Array.from(document.querySelectorAll(selector))) + if (selector !== '') { + recalculateAndSendTargets(Array.from(document.querySelectorAll(selector))) + } return { isValid: selector !== '', selector: selector.trim(), @@ -511,6 +536,11 @@ const launchElementPicker = (root: ShadowRoot) => { quitButton.addEventListener('click', () => { quitElementPicker() }) + + const manageButton = root.getElementById('btnManage')! + manageButton.addEventListener('click', () => { + api.cosmeticFilterManage(); + }) } const highlightElements = (coords: TargetRect[]) => { diff --git a/components/definitions/chromel.d.ts b/components/definitions/chromel.d.ts index 52adc4f29133..2c57f6d4302b 100644 --- a/components/definitions/chromel.d.ts +++ b/components/definitions/chromel.d.ts @@ -240,9 +240,9 @@ declare namespace chrome.windows { const getAllAsync: any } -declare namespace chrome.braveShields { - const addSiteCosmeticFilter: (cssSelector: string) => void - const openFilterManagementPage: () => void +declare namespace cf_worker { + const addSiteCosmeticFilter: (selector: string) => void + const manageCustomFilters: () => void } declare namespace chrome.test { diff --git a/components/test/brave_extension/background/events/cosmeticFilterEvents_test.ts b/components/test/brave_extension/background/events/cosmeticFilterEvents_test.ts deleted file mode 100644 index ef39b7826ce5..000000000000 --- a/components/test/brave_extension/background/events/cosmeticFilterEvents_test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (c) 2019 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 * as cosmeticFilterEvents from '../../../../brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents' - -let selectorToReturn: string - -global.prompt = () => { - return selectorToReturn -} - -describe('cosmeticFilterEvents events', () => { - describe('chrome.contextMenus.onClicked listener', () => { - let contextMenuOnClickedSpy: jest.SpyInstance - let chromeTabsQuerySpy: jest.SpyInstance - let chromeTabsSendMessageSpy: jest.SpyInstance - chrome.braveShields = { - addSiteCosmeticFilter: () => { /* stub */ } - } - beforeEach(() => { - contextMenuOnClickedSpy = jest.spyOn(chrome.tabs, 'create') - chromeTabsQuerySpy = jest.spyOn(chrome.tabs, 'query') - chromeTabsSendMessageSpy = jest.spyOn(chrome.tabs, 'sendMessage') - }) - afterEach(() => { - contextMenuOnClickedSpy.mockRestore() - chromeTabsSendMessageSpy.mockRestore() - }) - - describe('addBlockElement', function () { - it('triggers addBlockElement action (query call)', function () { - const info: chrome.contextMenus.OnClickData = { menuItemId: 'elementPickerMode', editable: false, pageUrl: 'brave.com' } - // calls query - const tab: chrome.tabs.Tab = { - id: 3, - index: 0, - pinned: false, - highlighted: false, - windowId: 1, - active: true, - incognito: false, - selected: true, - discarded: false, - autoDiscardable: false - } - cosmeticFilterEvents.onContextMenuClicked(info, tab) - expect(chromeTabsQuerySpy).toBeCalled() - }) - }) - }) -}) diff --git a/test/BUILD.gn b/test/BUILD.gn index cdf62ccac7e2..cd62ec614fb9 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -1171,7 +1171,6 @@ test("brave_browser_tests") { # API tests and extension deps if (enable_extensions) { sources += [ - "//brave/browser/extensions/brave_shields_apitest.cc", "//brave/browser/extensions/extension_urls_browsertest.cc", "//brave/browser/ui/webui/settings/brave_extensions_manifest_v2_browsertest.cc", "//brave/browser/ui/webui/settings/brave_tor_snowflake_extension_browsertest.cc",