diff --git a/browser/BUILD.gn b/browser/BUILD.gn index e6ea79cbe8ff..88f808202be6 100644 --- a/browser/BUILD.gn +++ b/browser/BUILD.gn @@ -138,6 +138,8 @@ source_set("browser_process") { "//brave/components/brave_wayback_machine:buildflags", "//brave/components/brave_webtorrent/browser/buildflags", "//brave/components/content_settings/core/browser", + "//brave/components/cosmetic_filters/browser", + "//brave/components/cosmetic_filters/common:mojom", "//brave/components/crypto_dot_com/browser/buildflags", "//brave/components/gemini/browser/buildflags", "//brave/components/greaselion/browser/buildflags", @@ -182,6 +184,7 @@ source_set("browser_process") { "//content/public/browser", "//content/public/common", "//extensions/buildflags", + "//mojo/public/cpp/bindings", "//services/metrics/public/cpp:metrics_cpp", "//services/network/public/cpp", "//third_party/blink/public/mojom:mojom_platform_headers", diff --git a/browser/android/BUILD.gn b/browser/android/BUILD.gn index bde60ad02d48..9f0a15cfa684 100644 --- a/browser/android/BUILD.gn +++ b/browser/android/BUILD.gn @@ -5,8 +5,6 @@ source_set("android_browser_process") { check_includes = false sources = [ - "brave_cosmetic_resources_tab_helper.cc", - "brave_cosmetic_resources_tab_helper.h", "brave_feature_list.cc", "brave_relaunch_utils.cc", "brave_shields_content_settings.cc", @@ -29,9 +27,8 @@ source_set("android_browser_process") { ] if (brave_ads_enabled) { - sources += [ - "//brave/browser/brave_ads/android/brave_ads_native_helper.cc", - ] + sources += + [ "//brave/browser/brave_ads/android/brave_ads_native_helper.cc" ] deps += [ "//brave/browser/brave_ads/android:jni_headers", @@ -42,10 +39,10 @@ source_set("android_browser_process") { if (enable_brave_sync) { sources += [ - "brave_sync_worker.cc", - "brave_sync_worker.h", "//brave/browser/sync/brave_sync_devices_android.cc", "//brave/browser/sync/brave_sync_devices_android.h", + "brave_sync_worker.cc", + "brave_sync_worker.h", ] deps += [ "//brave/components/brave_sync", diff --git a/browser/android/brave_cosmetic_resources_tab_helper.cc b/browser/android/brave_cosmetic_resources_tab_helper.cc deleted file mode 100644 index 7d6278143953..000000000000 --- a/browser/android/brave_cosmetic_resources_tab_helper.cc +++ /dev/null @@ -1,130 +0,0 @@ -/* Copyright (c) 2020 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 3.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/android/brave_cosmetic_resources_tab_helper.h" - -#include -#include -#include - -#include "brave/browser/brave_browser_process_impl.h" -#include "brave/components/brave_shields/browser/ad_block_custom_filters_service.h" -#include "brave/components/brave_shields/browser/ad_block_regional_service_manager.h" -#include "brave/components/brave_shields/browser/ad_block_service.h" -#include "brave/components/brave_shields/browser/ad_block_service_helper.h" -#include "brave/components/brave_shields/browser/brave_shields_util.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 "content/browser/web_contents/web_contents_impl.h" -#include "content/public/browser/global_routing_id.h" -#include "content/public/browser/navigation_handle.h" -#include "content/public/renderer/render_frame.h" - - -namespace { -bool ShouldDoCosmeticFiltering(content::WebContents* contents, - const GURL& url) { - Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); - auto* map = HostContentSettingsMapFactory::GetForProfile(profile); - - return ::brave_shields::ShouldDoCosmeticFiltering(map, url); -} - -std::unique_ptr GetUrlCosmeticResourcesOnTaskRunner( - const std::string& url) { - auto result_list = std::make_unique(); - - base::Optional resources = g_brave_browser_process-> - ad_block_service()->UrlCosmeticResources(url); - - if (!resources || !resources->is_dict()) { - return result_list; - } - - base::Optional regional_resources = g_brave_browser_process-> - ad_block_regional_service_manager()->UrlCosmeticResources(url); - - if (regional_resources && regional_resources->is_dict()) { - ::brave_shields::MergeResourcesInto( - std::move(*regional_resources), &*resources, /*force_hide=*/false); - } - - base::Optional custom_resources = g_brave_browser_process-> - ad_block_custom_filters_service()->UrlCosmeticResources(url); - - if (custom_resources && custom_resources->is_dict()) { - ::brave_shields::MergeResourcesInto( - std::move(*custom_resources), &*resources, /*force_hide=*/true); - } - - result_list->Append(std::move(*resources)); - - return result_list; -} - -void GetUrlCosmeticResourcesOnUI(content::GlobalFrameRoutingId frame_id, - std::unique_ptr resources) { - if (!resources) { - return; - } - for (auto i = resources->GetList().begin(); - i < resources->GetList().end(); i++) { - base::DictionaryValue* resources_dict; - if (!i->GetAsDictionary(&resources_dict)) { - continue; - } - std::string to_inject; - resources_dict->GetString("injected_script", &to_inject); - if (to_inject.length() > 1) { - auto* frame_host = content::RenderFrameHost::FromID(frame_id); - if (!frame_host) - return; - frame_host->ExecuteJavaScriptInIsolatedWorld( - base::UTF8ToUTF16(to_inject), - base::NullCallback(), ISOLATED_WORLD_ID_CHROME_INTERNAL); - } - } -} -} // namespace - - -BraveCosmeticResourcesTabHelper::BraveCosmeticResourcesTabHelper( - content::WebContents* contents) - : WebContentsObserver(contents) { -} - -BraveCosmeticResourcesTabHelper::~BraveCosmeticResourcesTabHelper() { -} - -void BraveCosmeticResourcesTabHelper::ProcessURL( - content::WebContents* contents, - content::RenderFrameHost* render_frame_host, const GURL& url) { - if (!render_frame_host || !ShouldDoCosmeticFiltering(contents, url)) { - return; - } - g_brave_browser_process->ad_block_service()->GetTaskRunner()-> - PostTaskAndReplyWithResult(FROM_HERE, - base::BindOnce(&GetUrlCosmeticResourcesOnTaskRunner, url.spec()), - base::BindOnce(&GetUrlCosmeticResourcesOnUI, - content::GlobalFrameRoutingId( - render_frame_host->GetProcess()->GetID(), - render_frame_host->GetRoutingID()))); -} - -void BraveCosmeticResourcesTabHelper::DidFinishNavigation( - content::NavigationHandle* navigation_handle) { - ProcessURL(web_contents(), web_contents()->GetMainFrame(), - web_contents()->GetLastCommittedURL()); -} - -void BraveCosmeticResourcesTabHelper::ResourceLoadComplete( - content::RenderFrameHost* render_frame_host, - const content::GlobalRequestID& request_id, - const blink::mojom::ResourceLoadInfo& resource_load_info) { - ProcessURL(web_contents(), render_frame_host, resource_load_info.final_url); -} - -WEB_CONTENTS_USER_DATA_KEY_IMPL(BraveCosmeticResourcesTabHelper) diff --git a/browser/android/brave_cosmetic_resources_tab_helper.h b/browser/android/brave_cosmetic_resources_tab_helper.h deleted file mode 100644 index 7d99c5c137d7..000000000000 --- a/browser/android/brave_cosmetic_resources_tab_helper.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (c) 2020 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 3.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_ANDROID_BRAVE_COSMETIC_RESOURCES_TAB_HELPER_H_ -#define BRAVE_BROWSER_ANDROID_BRAVE_COSMETIC_RESOURCES_TAB_HELPER_H_ - -#include "base/memory/weak_ptr.h" -#include "content/public/browser/web_contents_observer.h" -#include "content/public/browser/web_contents_user_data.h" - -class BraveCosmeticResourcesTabHelper - : public content::WebContentsObserver, - public content::WebContentsUserData, - public base::SupportsWeakPtr { - public: - explicit BraveCosmeticResourcesTabHelper(content::WebContents* contents); - ~BraveCosmeticResourcesTabHelper() override; - - // content::WebContentsObserver overrides: - void DidFinishNavigation( - content::NavigationHandle* navigation_handle) override; - void ResourceLoadComplete( - content::RenderFrameHost* render_frame_host, - const content::GlobalRequestID& request_id, - const blink::mojom::ResourceLoadInfo& resource_load_info) override; - - WEB_CONTENTS_USER_DATA_KEY_DECL(); - - private: - void ProcessURL(content::WebContents* contents, - content::RenderFrameHost* render_frame_host, const GURL& url); - DISALLOW_COPY_AND_ASSIGN(BraveCosmeticResourcesTabHelper); -}; - -#endif // BRAVE_BROWSER_ANDROID_BRAVE_COSMETIC_RESOURCES_TAB_HELPER_H_ diff --git a/browser/brave_content_browser_client.cc b/browser/brave_content_browser_client.cc index 946d4117fb86..fc0abfcbf322 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -20,7 +20,6 @@ #include "brave/common/pref_names.h" #include "brave/common/webui_url_constants.h" #include "brave/components/binance/browser/buildflags/buildflags.h" -#include "brave/components/gemini/browser/buildflags/buildflags.h" #include "brave/components/brave_rewards/browser/buildflags/buildflags.h" #include "brave/components/brave_shields/browser/brave_shields_util.h" #include "brave/components/brave_shields/browser/brave_shields_web_contents_observer.h" @@ -28,6 +27,9 @@ #include "brave/components/brave_shields/common/brave_shield_constants.h" #include "brave/components/brave_wallet/buildflags/buildflags.h" #include "brave/components/brave_webtorrent/browser/buildflags/buildflags.h" +#include "brave/components/cosmetic_filters/browser/cosmetic_filters_resources.h" +#include "brave/components/cosmetic_filters/common/cosmetic_filters.mojom.h" +#include "brave/components/gemini/browser/buildflags/buildflags.h" #include "brave/components/ipfs/buildflags/buildflags.h" #include "brave/components/speedreader/buildflags.h" #include "brave/components/tor/buildflags/buildflags.h" @@ -37,6 +39,7 @@ #include "chrome/browser/profiles/profile_io_data.h" #include "chrome/common/url_constants.h" #include "components/content_settings/browser/page_specific_content_settings.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/prefs/pref_service.h" #include "components/services/heap_profiling/public/mojom/heap_profiling_client.mojom.h" #include "content/browser/renderer_host/render_frame_host_impl.h" @@ -48,6 +51,7 @@ #include "content/public/common/content_switches.h" #include "content/public/common/service_names.mojom.h" #include "extensions/buildflags/buildflags.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" #include "net/cookies/site_for_cookies.h" #include "third_party/blink/public/common/loader/url_loader_throttle.h" @@ -128,6 +132,24 @@ bool HandleURLRewrite(GURL* url, content::BrowserContext* browser_context) { return false; } +void BindCosmeticFiltersResources( + content::RenderFrameHost* const frame_host, + mojo::PendingReceiver + receiver) { + auto* web_contents = content::WebContents::FromRenderFrameHost(frame_host); + if (!web_contents) + return; + + auto* profile = + Profile::FromBrowserContext(web_contents->GetBrowserContext()); + auto* settings_map = HostContentSettingsMapFactory::GetForProfile(profile); + + mojo::MakeSelfOwnedReceiver( + std::make_unique( + settings_map, g_brave_browser_process->ad_block_service()), + std::move(receiver)); +} + } // namespace BraveContentBrowserClient::BraveContentBrowserClient() @@ -172,6 +194,15 @@ BraveContentBrowserClient::AllowWebBluetooth( return ContentBrowserClient::AllowWebBluetoothResult::BLOCK_GLOBALLY_DISABLED; } +void BraveContentBrowserClient::RegisterBrowserInterfaceBindersForFrame( + content::RenderFrameHost* render_frame_host, + mojo::BinderMapWithContext* map) { + ChromeContentBrowserClient::RegisterBrowserInterfaceBindersForFrame( + render_frame_host, map); + map->Add( + base::BindRepeating(&BindCosmeticFiltersResources)); +} + bool BraveContentBrowserClient::HandleExternalProtocol( const GURL& url, content::WebContents::OnceGetter web_contents_getter, diff --git a/browser/brave_content_browser_client.h b/browser/brave_content_browser_client.h index f90a91cef56b..8103b152cd28 100644 --- a/browser/brave_content_browser_client.h +++ b/browser/brave_content_browser_client.h @@ -51,6 +51,10 @@ class BraveContentBrowserClient : public ChromeContentBrowserClient { const url::Origin& requesting_origin, const url::Origin& embedding_origin) override; + void RegisterBrowserInterfaceBindersForFrame( + content::RenderFrameHost* render_frame_host, + mojo::BinderMapWithContext* map) override; + void AppendExtraCommandLineSwitches(base::CommandLine* command_line, int child_process_id) override; diff --git a/browser/brave_shields/ad_block_service_browsertest.cc b/browser/brave_shields/ad_block_service_browsertest.cc index 94c096748ab8..3e8c7cb8eaf8 100644 --- a/browser/brave_shields/ad_block_service_browsertest.cc +++ b/browser/brave_shields/ad_block_service_browsertest.cc @@ -784,7 +784,6 @@ IN_PROC_BROWSER_TEST_F(CosmeticFilteringFlagDisabledTest, UpdateAdBlockInstanceWithRules( "b.com###ad-banner\n" "##.ad"); - WaitForBraveExtensionShieldsDataReady(); GURL tab_url = @@ -844,13 +843,41 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringSimple) { content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_EQ(true, - EvalJs(contents, "checkSelector('#ad-banner', 'display', 'none')")); - - ASSERT_EQ(true, EvalJs(contents, - "checkSelector('.ad-banner', 'display', 'block')")); - - ASSERT_EQ(true, EvalJs(contents, "checkSelector('.ad', 'display', 'none')")); + auto result_first = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('#ad-banner', 'display', 'none')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result_first.error.empty()); + EXPECT_EQ(base::Value(true), result_first.value); + + auto result_second = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('.ad-banner', 'display', 'block')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result_second.error.empty()); + EXPECT_EQ(base::Value(true), result_second.value); + + auto result_third = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('.ad', 'display', 'none')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result_third.error.empty()); + EXPECT_EQ(base::Value(true), result_third.value); } // Test cosmetic filtering ignores content determined to be 1st party @@ -885,8 +912,17 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringHide1pContent) { content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_EQ(true, EvalJs(contents, - "checkSelector('.fpsponsored', 'display', 'none')")); + auto result = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('.fpsponsored', 'display', 'none')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result.error.empty()); + EXPECT_EQ(base::Value(true), result.value); } // Test cosmetic filtering on elements added dynamically @@ -902,12 +938,29 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringDynamic) { content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_EQ(true, EvalJs(contents, - "addElementsDynamically();\n" - "checkSelector('.blockme', 'display', 'none')")); - - ASSERT_EQ(true, EvalJs(contents, - "checkSelector('.dontblockme', 'display', 'block')")); + auto result_first = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('.blockme', 'display', 'none')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result_first.error.empty()); + EXPECT_EQ(base::Value(true), result_first.value); + + auto result_second = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('.dontblockme', 'display', 'block')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result_second.error.empty()); + EXPECT_EQ(base::Value(true), result_second.value); } // Test cosmetic filtering ignores generic cosmetic rules in the presence of a @@ -952,8 +1005,17 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_EQ(true, - EvalJs(contents, "checkSelector('.ad', 'padding-bottom', '10px')")); + auto result = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('.ad', 'padding-bottom', '10px')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result.error.empty()); + EXPECT_EQ(base::Value(true), result.value); } // Test rules overridden by hostname-specific exception rules @@ -973,10 +1035,29 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringUnhide) { content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_EQ(true, EvalJs(contents, "checkSelector('.ad', 'display', 'block')")); - - ASSERT_EQ(true, - EvalJs(contents, "checkSelector('#ad-banner', 'display', 'none')")); + auto result_first = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('.ad', 'display', 'block')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result_first.error.empty()); + EXPECT_EQ(base::Value(true), result_first.value); + + auto result_second = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('#ad-banner', 'display', 'none')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result_second.error.empty()); + EXPECT_EQ(base::Value(true), result_second.value); } // Test scriptlet injection that modifies window attributes @@ -1010,8 +1091,17 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, content::WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_EQ(true, EvalJs(contents, - "checkSelector('.ad', 'color', 'Impossible value')")); + auto result = EvalJsWithManualReply(contents, + R"(function waitCSSSelector() { + if (checkSelector('.ad', 'color', 'Impossible value')) { + window.domAutomationController.send(true); + } else { + console.log('still waiting for css selector'); + setTimeout(waitCSSSelector, 200); + } + } waitCSSSelector())"); + ASSERT_TRUE(result.error.empty()); + EXPECT_EQ(base::Value(true), result.value); } // Test scriptlet injection that modifies window attributes diff --git a/browser/brave_tab_helpers.cc b/browser/brave_tab_helpers.cc index bbeeb955c799..ab760347fb69 100644 --- a/browser/brave_tab_helpers.cc +++ b/browser/brave_tab_helpers.cc @@ -31,7 +31,6 @@ #endif #if defined(OS_ANDROID) -#include "brave/browser/android/brave_cosmetic_resources_tab_helper.h" #include "brave/browser/android/preferences/background_video_playback_tab_helper.h" #include "brave/browser/android/preferences/website/desktop_mode_tab_helper.h" #endif @@ -84,7 +83,6 @@ void AttachTabHelpers(content::WebContents* web_contents) { #if defined(OS_ANDROID) DesktopModeTabHelper::CreateForWebContents(web_contents); BackgroundVideoPlaybackTabHelper::CreateForWebContents(web_contents); - BraveCosmeticResourcesTabHelper::CreateForWebContents(web_contents); #else // Add tab helpers here unless they are intended for android too BraveBookmarkTabHelper::CreateForWebContents(web_contents); diff --git a/browser/extensions/api/brave_shields_api.cc b/browser/extensions/api/brave_shields_api.cc index 3a28dea9a93e..cdcf49000748 100644 --- a/browser/extensions/api/brave_shields_api.cc +++ b/browser/extensions/api/brave_shields_api.cc @@ -12,11 +12,6 @@ #include "brave/browser/extensions/api/brave_action_api.h" #include "brave/browser/webcompat_reporter/webcompat_reporter_dialog.h" #include "brave/common/extensions/api/brave_shields.h" -#include "brave/components/brave_shields/browser/ad_block_base_service.h" -#include "brave/components/brave_shields/browser/ad_block_custom_filters_service.h" -#include "brave/components/brave_shields/browser/ad_block_regional_service_manager.h" -#include "brave/components/brave_shields/browser/ad_block_service.h" -#include "brave/components/brave_shields/browser/ad_block_service_helper.h" #include "brave/components/brave_shields/browser/brave_shields_p3a.h" #include "brave/components/brave_shields/browser/brave_shields_util.h" #include "brave/components/brave_shields/browser/brave_shields_web_contents_observer.h" @@ -46,126 +41,6 @@ const char kInvalidControlTypeError[] = "Invalid ControlType."; } // namespace - -ExtensionFunction::ResponseAction -BraveShieldsUrlCosmeticResourcesFunction::Run() { - std::unique_ptr params( - brave_shields::UrlCosmeticResources::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - g_brave_browser_process->ad_block_service()->GetTaskRunner() - ->PostTaskAndReplyWithResult( - FROM_HERE, - base::BindOnce(&BraveShieldsUrlCosmeticResourcesFunction:: - GetUrlCosmeticResourcesOnTaskRunner, - this, params->url), - base::BindOnce(&BraveShieldsUrlCosmeticResourcesFunction:: - GetUrlCosmeticResourcesOnUI, - this)); - return RespondLater(); -} - -std::unique_ptr BraveShieldsUrlCosmeticResourcesFunction:: - GetUrlCosmeticResourcesOnTaskRunner(const std::string& url) { - base::Optional resources = g_brave_browser_process-> - ad_block_service()->UrlCosmeticResources(url); - - if (!resources || !resources->is_dict()) { - return std::unique_ptr(); - } - - base::Optional regional_resources = g_brave_browser_process-> - ad_block_regional_service_manager()->UrlCosmeticResources(url); - - if (regional_resources && regional_resources->is_dict()) { - ::brave_shields::MergeResourcesInto( - std::move(*regional_resources), &*resources, /*force_hide=*/false); - } - - base::Optional custom_resources = g_brave_browser_process-> - ad_block_custom_filters_service()->UrlCosmeticResources(url); - - if (custom_resources && custom_resources->is_dict()) { - ::brave_shields::MergeResourcesInto( - std::move(*custom_resources), &*resources, /*force_hide=*/true); - } - - auto result_list = std::make_unique(); - result_list->Append(std::move(*resources)); - return result_list; -} - -void BraveShieldsUrlCosmeticResourcesFunction::GetUrlCosmeticResourcesOnUI( - std::unique_ptr resources) { - if (!resources) { - Respond(Error("Url-specific cosmetic resources could not be returned")); - return; - } - Respond(ArgumentList(std::move(resources))); -} - -ExtensionFunction::ResponseAction -BraveShieldsHiddenClassIdSelectorsFunction::Run() { - std::unique_ptr params( - brave_shields::HiddenClassIdSelectors::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - g_brave_browser_process->ad_block_service()->GetTaskRunner() - ->PostTaskAndReplyWithResult( - FROM_HERE, - base::BindOnce(&BraveShieldsHiddenClassIdSelectorsFunction:: - GetHiddenClassIdSelectorsOnTaskRunner, - this, params->classes, params->ids, - params->exceptions), - base::BindOnce(&BraveShieldsHiddenClassIdSelectorsFunction:: - GetHiddenClassIdSelectorsOnUI, - this)); - return RespondLater(); -} - -std::unique_ptr BraveShieldsHiddenClassIdSelectorsFunction:: - GetHiddenClassIdSelectorsOnTaskRunner( - const std::vector& classes, - const std::vector& ids, - const std::vector& exceptions) { - base::Optional hide_selectors = g_brave_browser_process-> - ad_block_service()->HiddenClassIdSelectors(classes, ids, exceptions); - - base::Optional regional_selectors = g_brave_browser_process-> - ad_block_regional_service_manager()-> - HiddenClassIdSelectors(classes, ids, exceptions); - - base::Optional custom_selectors = g_brave_browser_process-> - ad_block_custom_filters_service()-> - HiddenClassIdSelectors(classes, ids, exceptions); - - if (hide_selectors && hide_selectors->is_list()) { - if (regional_selectors && regional_selectors->is_list()) { - for (auto i = regional_selectors->GetList().begin(); - i < regional_selectors->GetList().end(); - i++) { - hide_selectors->Append(std::move(*i)); - } - } - } else { - hide_selectors = std::move(regional_selectors); - } - - auto result_list = std::make_unique(); - if (hide_selectors && hide_selectors->is_list()) { - result_list->Append(std::move(*hide_selectors)); - } - if (custom_selectors && custom_selectors->is_list()) { - result_list->Append(std::move(*custom_selectors)); - } - - return result_list; -} - -void BraveShieldsHiddenClassIdSelectorsFunction:: - GetHiddenClassIdSelectorsOnUI(std::unique_ptr selectors) { - Respond(ArgumentList(std::move(selectors))); -} - - ExtensionFunction::ResponseAction BraveShieldsAllowScriptsOnceFunction::Run() { std::unique_ptr params( brave_shields::AllowScriptsOnce::Params::Create(*args_)); diff --git a/browser/extensions/api/brave_shields_api.h b/browser/extensions/api/brave_shields_api.h index a65ce7d2c039..6ed9ecf4491f 100644 --- a/browser/extensions/api/brave_shields_api.h +++ b/browser/extensions/api/brave_shields_api.h @@ -15,39 +15,6 @@ namespace extensions { namespace api { -class BraveShieldsUrlCosmeticResourcesFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveShields.urlCosmeticResources", UNKNOWN) - - protected: - ~BraveShieldsUrlCosmeticResourcesFunction() override {} - - ResponseAction Run() override; - - private: - std::unique_ptr GetUrlCosmeticResourcesOnTaskRunner( - const std::string& url); - void GetUrlCosmeticResourcesOnUI(std::unique_ptr resources); -}; - -class BraveShieldsHiddenClassIdSelectorsFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveShields.hiddenClassIdSelectors", UNKNOWN) - - protected: - ~BraveShieldsHiddenClassIdSelectorsFunction() override {} - - ResponseAction Run() override; - - private: - std::unique_ptr GetHiddenClassIdSelectorsOnTaskRunner( - const std::vector& classes, - const std::vector& ids, - const std::vector& exceptions); - void GetHiddenClassIdSelectorsOnUI( - std::unique_ptr selectors); -}; - class BraveShieldsAllowScriptsOnceFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("braveShields.allowScriptsOnce", UNKNOWN) diff --git a/browser/resources/resource_ids b/browser/resources/resource_ids index 580fd3ac51ec..27b87469a3c3 100644 --- a/browser/resources/resource_ids +++ b/browser/resources/resource_ids @@ -100,4 +100,7 @@ "<(ROOT_GEN_DIR)/brave/web-ui-ipfs/ipfs.grd": { "includes": [45500], }, + "<(ROOT_GEN_DIR)/brave/web-ui-cosmetic_filters/cosmetic_filters.grd": { + "includes": [46000] + }, } diff --git a/browser/ui/BUILD.gn b/browser/ui/BUILD.gn index 73c543954974..ac757d2a83a9 100644 --- a/browser/ui/BUILD.gn +++ b/browser/ui/BUILD.gn @@ -221,6 +221,7 @@ source_set("ui") { "//brave/components/brave_shields/browser", "//brave/components/brave_wallet/buildflags:buildflags", "//brave/components/brave_wayback_machine:buildflags", + "//brave/components/cosmetic_filters/resources/data:generated_resources", "//brave/components/crypto_dot_com/browser/buildflags:buildflags", "//brave/components/l10n/browser", "//brave/components/l10n/common", diff --git a/common/extensions/api/brave_shields.json b/common/extensions/api/brave_shields.json index ed203834322f..5c6eb6008b07 100644 --- a/common/extensions/api/brave_shields.json +++ b/common/extensions/api/brave_shields.json @@ -73,73 +73,6 @@ "description": "Notifies the browser about the fact of showing the panel", "parameters": [] }, - { - "name": "urlCosmeticResources", - "type": "function", - "description": "Get a cosmetic adblocking stylesheet, generic style exceptions, script injections, and whether or not a generichide rule was present, all specific for the given URL", - "parameters": [ - { - "name": "url", - "type": "string" - }, - { - "type": "function", - "name": "callback", - "parameters": [ - { - "name": "urlSpecificResources", - "type": "object", - "properties": { - "hide_selectors": {"type": "array", "items": {"type": "string"}, "description": "URL-specific CSS selectors that should be hidden from the page if they are determined to not include 1st party content"}, - "style_selectors": {"type": "object", "additionalProperties": {"type": "array", "items": {"type": "string"}}, "description": "URL-specific CSS selectors that should be restyled, with their associated CSS style rules"}, - "exceptions": {"type": "array", "items": {"type": "string"}, "description": "URL-specific overrides for generic cosmetic blocking selectors"}, - "injected_script": {"type": "string", "description": "A script to inject as the page is loading"}, - "force_hide_selectors": {"type": "array", "items": {"type": "string"}, "description": "URL-specific CSS selectors that should be hidden from the page"}, - "generichide": {"type": "boolean", "description": "Indicates whether or not the URL matched a generichide exception rule"} - } - } - ] - } - ] - }, - { - "name": "hiddenClassIdSelectors", - "type": "function", - "description": "Get a stylesheet of generic rules that may apply to the given set of classes and ids without any of the given excepted selectors", - "parameters": [ - { - "name": "classes", - "type": "array", - "items": {"type": "string"} - }, - { - "name": "ids", - "type": "array", - "items": {"type": "string"} - }, - { - "name": "exceptions", - "type": "array", - "items": {"type": "string"} - }, - { - "type": "function", - "name": "callback", - "parameters": [ - { - "name": "selectors", - "type": "array", - "items": {"type": "string"} - }, - { - "name": "forceHideSelectors", - "type": "array", - "items": {"type": "string"} - } - ] - } - ] - }, { "name": "getBraveShieldsEnabled", "type": "function", diff --git a/components/brave_extension/extension/brave_extension/BUILD.gn b/components/brave_extension/extension/brave_extension/BUILD.gn index 2e0e84155c1e..083279ab6b59 100644 --- a/components/brave_extension/extension/brave_extension/BUILD.gn +++ b/components/brave_extension/extension/brave_extension/BUILD.gn @@ -5,18 +5,31 @@ transpile_web_ui("brave_extension") { # Need this to fire re-pack if changed, nevertheless extension is repacked on each 2nd build # what is the output bundle called and what is the entry point file entry_points = [ - ["brave_extension", rebase_path("braveShieldsPanel.tsx")], - ["brave_extension_background", rebase_path("background.ts")], - ["content", rebase_path("content.ts")], - ["content_dapps", rebase_path("content_dapps.ts")], - ["content_cosmetic", rebase_path("content_cosmetic.ts")], - ["webstore", rebase_path("webstore.ts")], + [ + "brave_extension", + rebase_path("braveShieldsPanel.tsx"), + ], + [ + "brave_extension_background", + rebase_path("background.ts"), + ], + [ + "content", + rebase_path("content.ts"), + ], + [ + "content_dapps", + rebase_path("content_dapps.ts"), + ], + [ + "webstore", + rebase_path("webstore.ts"), + ], ] # what is the directory / pack name resource_name = "brave_extension" - # Must match the relative path from the static GRD to the manifest.json # plus any other relative path we want these files to live in the extension extra_relative_path = "/brave_extension/out" @@ -28,7 +41,7 @@ if (is_mac) { group("brave_extension_framework_bundle_data") { deps = [] foreach(locale, locales) { - deps += [":brave_extension_framework_bundle_data_${locale}"] + deps += [ ":brave_extension_framework_bundle_data_${locale}" ] } } @@ -36,31 +49,23 @@ if (is_mac) { bundle_data("brave_extension_framework_bundle_data_${locale}") { locale = string_replace(locale, "-", "_") source_locale = string_replace(locale, "nb", "no") - sources = [ - "//brave/components/brave_extension/extension/brave_extension/_locales/$source_locale/messages.json" - ] - outputs = [ - "{{bundle_resources_dir}}/brave_extension/_locales/$locale/{{source_file_part}}" - ] + sources = [ "//brave/components/brave_extension/extension/brave_extension/_locales/$source_locale/messages.json" ] + outputs = [ "{{bundle_resources_dir}}/brave_extension/_locales/$locale/{{source_file_part}}" ] } } } else { group("locales") { deps = [] foreach(locale, locales) { - deps += [":locales_${locale}"] + deps += [ ":locales_${locale}" ] } } foreach(locale, locales) { copy("locales_${locale}") { locale = string_replace(locale, "-", "_") source_locale = string_replace(locale, "nb", "no") - sources = [ - "//brave/components/brave_extension/extension/brave_extension/_locales/${source_locale}/messages.json", - ] - outputs = [ - "$root_out_dir/resources/brave_extension/_locales/${locale}/{{source_file_part}}", - ] + sources = [ "//brave/components/brave_extension/extension/brave_extension/_locales/${source_locale}/messages.json" ] + outputs = [ "$root_out_dir/resources/brave_extension/_locales/${locale}/{{source_file_part}}" ] } } } diff --git a/components/brave_extension/extension/brave_extension/actions/shieldsPanelActions.ts b/components/brave_extension/extension/brave_extension/actions/shieldsPanelActions.ts index 4abebb1c715d..b94877e136ce 100644 --- a/components/brave_extension/extension/brave_extension/actions/shieldsPanelActions.ts +++ b/components/brave_extension/extension/brave_extension/actions/shieldsPanelActions.ts @@ -147,17 +147,6 @@ export const generateClassIdStylesheet = (tabId: number, classes: string[], ids: } } -export const cosmeticFilterRuleExceptions: actions.CosmeticFilterRuleExceptions = (tabId: number, frameId: number, exceptions: string[], scriptlet: string, generichide: boolean) => { - return { - type: types.COSMETIC_FILTER_RULE_EXCEPTIONS, - tabId, - frameId, - exceptions, - scriptlet, - generichide - } -} - export const contentScriptsLoaded: actions.ContentScriptsLoaded = (tabId: number, frameId: number, url: string) => { return { type: types.CONTENT_SCRIPTS_LOADED, diff --git a/components/brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI.ts b/components/brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI.ts index 95c2700055df..261692c302b5 100644 --- a/components/brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI.ts +++ b/components/brave_extension/extension/brave_extension/background/api/cosmeticFilterAPI.ts @@ -2,75 +2,6 @@ * 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/. */ -import shieldsPanelActions from '../actions/shieldsPanelActions' - -const informTabOfCosmeticRulesToConsider = (tabId: number, selectors: string[]) => { - if (selectors.length !== 0) { - const message = { - type: 'cosmeticFilterConsiderNewSelectors', - selectors - } - const options = { - frameId: 0 - } - chrome.tabs.sendMessage(tabId, message, options) - } -} - -// Fires when content-script calls hiddenClassIdSelectors -export const injectClassIdStylesheet = (tabId: number, classes: string[], ids: string[], exceptions: string[], hide1pContent: boolean) => { - chrome.braveShields.hiddenClassIdSelectors(classes, ids, exceptions, (selectors, forceHideSelectors) => { - if (hide1pContent) { - forceHideSelectors.push(...selectors) - } else { - informTabOfCosmeticRulesToConsider(tabId, selectors) - } - - if (forceHideSelectors.length > 0) { - const forceHideStylesheet = forceHideSelectors.join(',') + '{display:none!important;}\n' - - chrome.tabs.insertCSS(tabId, { - code: forceHideStylesheet, - cssOrigin: 'user', - runAt: 'document_start' - }) - } - }) -} - -// Fires on content-script loaded -export const applyAdblockCosmeticFilters = (tabId: number, frameId: number, url: string, hide1pContent: boolean) => { - chrome.braveShields.urlCosmeticResources(url, async (resources) => { - if (chrome.runtime.lastError) { - console.warn('Unable to get cosmetic filter data for the current host', chrome.runtime.lastError) - return - } - - if (frameId === 0) { - if (hide1pContent) { - resources.force_hide_selectors.push(...resources.hide_selectors) - } else { - informTabOfCosmeticRulesToConsider(tabId, resources.hide_selectors) - } - - let styledStylesheet = '' - if (resources.force_hide_selectors.length > 0) { - styledStylesheet += resources.force_hide_selectors.join(',') + '{display:none!important;}\n' - } - for (const selector in resources.style_selectors) { - styledStylesheet += selector + '{' + resources.style_selectors[selector].join(';') + ';}\n' - } - chrome.tabs.insertCSS(tabId, { - code: styledStylesheet, - cssOrigin: 'user', - runAt: 'document_start' - }) - } - - shieldsPanelActions.cosmeticFilterRuleExceptions(tabId, frameId, resources.exceptions, resources.injected_script || '', resources.generichide) - }) -} - // User generated cosmetic filtering below export const applyCSSCosmeticFilters = (tabId: number, hostname: string) => { chrome.storage.local.get('cosmeticFilterList', (storeData = {}) => { diff --git a/components/brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents.ts b/components/brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents.ts index 4e6f934f11b5..30f3f4555898 100644 --- a/components/brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents.ts +++ b/components/brave_extension/extension/brave_extension/background/events/cosmeticFilterEvents.ts @@ -125,7 +125,7 @@ export async function onSelectorReturned (response: any) { code: `${rule.selector} {display: none !important;}`, cssOrigin: 'user' }) - - await addSiteCosmeticFilter(rule.host, rule.selector) } + + await addSiteCosmeticFilter(rule.host, rule.selector) } diff --git a/components/brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer.ts b/components/brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer.ts index 06b417eadf9f..c82c94b2678d 100644 --- a/components/brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer.ts +++ b/components/brave_extension/extension/brave_extension/background/reducers/shieldsPanelReducer.ts @@ -36,16 +36,10 @@ import { setAllowScriptOriginsOnce } from '../api/shieldsAPI' import { reloadTab } from '../api/tabsAPI' -import { - injectClassIdStylesheet, - applyAdblockCosmeticFilters, - applyCSSCosmeticFilters -} from '../api/cosmeticFilterAPI' // Helpers import { getAllowedScriptsOrigins } from '../../helpers/noScriptUtils' import { areObjectsEqual } from '../../helpers/objectUtils' -import { getHostname } from '../../helpers/urlUtils' export default function shieldsPanelReducer ( state: State = { @@ -66,7 +60,6 @@ export default function shieldsPanelReducer ( state = shieldsPanelState.resetBlockingResources(state, action.tabId) state = noScriptState.resetNoScriptInfo(state, action.tabId, new window.URL(action.url).origin) } - applyCSSCosmeticFilters(action.tabId, getHostname(action.url)) break } case windowTypes.WINDOW_REMOVED: { @@ -362,61 +355,6 @@ export default function shieldsPanelReducer ( }) break } - case shieldsPanelTypes.GENERATE_CLASS_ID_STYLESHEET: { - const tabData = state.tabs[action.tabId] - if (!tabData) { - console.error('Active tab not found') - break - } - const exceptions = tabData.cosmeticFilters.ruleExceptions - const hide1pContent = tabData.firstPartyCosmeticFiltering - - // setTimeout is used to prevent injectClassIdStylesheet from calling - // another Redux function immediately - setTimeout(() => injectClassIdStylesheet(action.tabId, action.classes, action.ids, exceptions, hide1pContent), 0) - break - } - case shieldsPanelTypes.COSMETIC_FILTER_RULE_EXCEPTIONS: { - const tabData = state.tabs[action.tabId] - if (!tabData) { - console.error('Active tab not found') - break - } - let message: { type: string, scriptlet: string, hideOptions?: { hide1pContent: boolean, generichide: boolean } } = { - type: 'cosmeticFilteringBackgroundReady', - scriptlet: action.scriptlet, - hideOptions: undefined - } - if (action.frameId === 0) { - // Non-scriptlet cosmetic filters are only applied on the top-level frame - state = shieldsPanelState.saveCosmeticFilterRuleExceptions(state, action.tabId, action.exceptions) - message.hideOptions = { - hide1pContent: tabData.firstPartyCosmeticFiltering, - generichide: action.generichide - } - } - chrome.tabs.sendMessage(action.tabId, message, { - frameId: action.frameId - }) - break - } - case shieldsPanelTypes.CONTENT_SCRIPTS_LOADED: { - const tabData = state.tabs[action.tabId] - if (!tabData) { - console.error('Active tab not found') - break - } - Promise.all([chrome.braveShields.shouldDoCosmeticFilteringAsync(action.url), chrome.braveShields.isFirstPartyCosmeticFilteringEnabledAsync(action.url)]) - .then(([doCosmeticBlocking, hide1pContent]: [boolean, boolean]) => { - if (doCosmeticBlocking) { - applyAdblockCosmeticFilters(action.tabId, action.frameId, action.url, hide1pContent) - } - }) - .catch(() => { - console.error('Could not apply cosmetic blocking') - }) - break - } } if (!areObjectsEqual(state.persistentData, initialPersistentData)) { diff --git a/components/brave_extension/extension/brave_extension/constants/shieldsPanelTypes.ts b/components/brave_extension/extension/brave_extension/constants/shieldsPanelTypes.ts index faea8d578819..e755eeb5df01 100644 --- a/components/brave_extension/extension/brave_extension/constants/shieldsPanelTypes.ts +++ b/components/brave_extension/extension/brave_extension/constants/shieldsPanelTypes.ts @@ -21,5 +21,4 @@ export const SET_ADVANCED_VIEW_FIRST_ACCESS = 'SET_ADVANCED_VIEW_FIRST_ACCESS' export const TOGGLE_ADVANCED_VIEW = 'TOGGLE_ADVANCED_VIEW' export const SHIELDS_READY = 'SHIELDS_READY' export const GENERATE_CLASS_ID_STYLESHEET = 'GENERATE_CLASS_ID_STYLESHEET' -export const COSMETIC_FILTER_RULE_EXCEPTIONS = 'COSMETIC_FILTER_RULE_EXCEPTIONS' export const CONTENT_SCRIPTS_LOADED = 'CONTENT_SCRIPTS_LOADED' diff --git a/components/brave_extension/extension/brave_extension/manifest.json b/components/brave_extension/extension/brave_extension/manifest.json index c4ef7a09e57f..b8f6bfedc61c 100644 --- a/components/brave_extension/extension/brave_extension/manifest.json +++ b/components/brave_extension/extension/brave_extension/manifest.json @@ -31,17 +31,6 @@ ] }, "content_scripts": [ - { - "matches": [ - "http://*/*", - "https://*/*" - ], - "js": [ - "out/content_cosmetic.bundle.js" - ], - "run_at": "document_start", - "all_frames": true - }, { "matches": [ "http://*/*", diff --git a/components/brave_extension/extension/brave_extension/types/actions/shieldsPanelActions.ts b/components/brave_extension/extension/brave_extension/types/actions/shieldsPanelActions.ts index 39c6a8c4c87b..3ca1a73aa7de 100644 --- a/components/brave_extension/extension/brave_extension/types/actions/shieldsPanelActions.ts +++ b/components/brave_extension/extension/brave_extension/types/actions/shieldsPanelActions.ts @@ -184,19 +184,6 @@ export interface GenerateClassIdStylesheet { (tabId: number, classes: string[], ids: string[]): GenerateClassIdStylesheetReturn } -interface CosmeticFilterRuleExceptionsReturn { - type: types.COSMETIC_FILTER_RULE_EXCEPTIONS, - tabId: number, - frameId: number, - exceptions: string[], - scriptlet: string, - generichide: boolean -} - -export interface CosmeticFilterRuleExceptions { - (tabId: number, frameId: number, exceptions: string[], scriptlet: string, generichide: boolean): CosmeticFilterRuleExceptionsReturn -} - interface ContentScriptsLoadedReturn { type: types.CONTENT_SCRIPTS_LOADED, tabId: number, @@ -227,5 +214,4 @@ export type shieldPanelActions = SetAdvancedViewFirstAccessReturn | ShieldsReadyReturn | GenerateClassIdStylesheetReturn | - CosmeticFilterRuleExceptionsReturn | ContentScriptsLoadedReturn diff --git a/components/brave_extension/extension/brave_extension/types/constants/shieldsPanelTypes.ts b/components/brave_extension/extension/brave_extension/types/constants/shieldsPanelTypes.ts index 05c2568a3cbb..b5b46459a757 100644 --- a/components/brave_extension/extension/brave_extension/types/constants/shieldsPanelTypes.ts +++ b/components/brave_extension/extension/brave_extension/types/constants/shieldsPanelTypes.ts @@ -23,5 +23,4 @@ export type SET_ADVANCED_VIEW_FIRST_ACCESS = typeof types.SET_ADVANCED_VIEW_FIRS export type TOGGLE_ADVANCED_VIEW = typeof types.TOGGLE_ADVANCED_VIEW export type SHIELDS_READY = typeof types.SHIELDS_READY export type GENERATE_CLASS_ID_STYLESHEET = typeof types.GENERATE_CLASS_ID_STYLESHEET -export type COSMETIC_FILTER_RULE_EXCEPTIONS = typeof types.COSMETIC_FILTER_RULE_EXCEPTIONS export type CONTENT_SCRIPTS_LOADED = typeof types.CONTENT_SCRIPTS_LOADED diff --git a/components/brave_shields/browser/ad_block_base_service.h b/components/brave_shields/browser/ad_block_base_service.h index bcb632726e3e..fab347e28d9a 100644 --- a/components/brave_shields/browser/ad_block_base_service.h +++ b/components/brave_shields/browser/ad_block_base_service.h @@ -49,12 +49,12 @@ class AdBlockBaseService : public BaseBraveShieldsService { void EnableTag(const std::string& tag, bool enabled); bool TagExists(const std::string& tag); - base::Optional UrlCosmeticResources( - const std::string& url); - base::Optional HiddenClassIdSelectors( - const std::vector& classes, - const std::vector& ids, - const std::vector& exceptions); + virtual base::Optional UrlCosmeticResources( + const std::string& url); + virtual base::Optional HiddenClassIdSelectors( + const std::vector& classes, + const std::vector& ids, + const std::vector& exceptions); protected: friend class ::AdBlockServiceTest; diff --git a/components/brave_shields/browser/ad_block_service.cc b/components/brave_shields/browser/ad_block_service.cc index 42ed5cc0f96a..dc8ff1308560 100644 --- a/components/brave_shields/browser/ad_block_service.cc +++ b/components/brave_shields/browser/ad_block_service.cc @@ -50,8 +50,9 @@ std::string GetTagFromPrefName(const std::string& pref_name) { // Extracts the start and end characters of a domain from a hostname. // Required for correct functionality of adblock-rust. -void AdBlockServiceDomainResolver(const char* host, uint32_t* start, - uint32_t* end) { +void AdBlockServiceDomainResolver(const char* host, + uint32_t* start, + uint32_t* end) { const auto host_str = std::string(host); const auto domain = net::registry_controlled_domains::GetDomainAndRegistry( host_str, @@ -78,7 +79,6 @@ bool AdBlockService::ShouldStartRequest( const std::string& tab_host, bool* did_match_exception, std::string* mock_data_url) { - if (!AdBlockBaseService::ShouldStartRequest( url, resource_type, tab_host, did_match_exception, mock_data_url)) { return false; @@ -96,7 +96,7 @@ bool AdBlockService::ShouldStartRequest( } if (!custom_filters_service()->ShouldStartRequest( - url, resource_type, tab_host, did_match_exception, mock_data_url)) { + url, resource_type, tab_host, did_match_exception, mock_data_url)) { return false; } if (did_match_exception && *did_match_exception) { @@ -106,6 +106,69 @@ bool AdBlockService::ShouldStartRequest( return true; } +base::Optional AdBlockService::UrlCosmeticResources( + const std::string& url) { + base::Optional resources = + AdBlockBaseService::UrlCosmeticResources(url); + + if (!resources || !resources->is_dict()) { + return resources; + } + + base::Optional regional_resources = + regional_service_manager()->UrlCosmeticResources(url); + + if (regional_resources && regional_resources->is_dict()) { + MergeResourcesInto(std::move(*regional_resources), &*resources, + /*force_hide=*/false); + } + + base::Optional custom_resources = + custom_filters_service()->UrlCosmeticResources(url); + + if (custom_resources && custom_resources->is_dict()) { + MergeResourcesInto(std::move(*custom_resources), &*resources, + /*force_hide=*/true); + } + + return resources; +} + +base::Optional AdBlockService::HiddenClassIdSelectors( + const std::vector& classes, + const std::vector& ids, + const std::vector& exceptions) { + base::Optional hide_selectors = + AdBlockBaseService::HiddenClassIdSelectors(classes, ids, exceptions); + + base::Optional regional_selectors = + regional_service_manager()->HiddenClassIdSelectors(classes, ids, + exceptions); + + base::Optional custom_selectors = + custom_filters_service()->HiddenClassIdSelectors(classes, ids, + exceptions); + + if (hide_selectors && hide_selectors->is_list()) { + if (regional_selectors && regional_selectors->is_list()) { + for (auto i = regional_selectors->GetList().begin(); + i < regional_selectors->GetList().end(); i++) { + hide_selectors->Append(std::move(*i)); + } + } + } else { + hide_selectors = std::move(regional_selectors); + } + + if (!hide_selectors || !hide_selectors->is_list()) + hide_selectors = base::ListValue(); + + if (custom_selectors && custom_selectors->is_list()) + hide_selectors->Append(std::move(*custom_selectors)); + + return hide_selectors; +} + AdBlockRegionalServiceManager* AdBlockService::regional_service_manager() { if (!regional_service_manager_) regional_service_manager_ = @@ -118,16 +181,13 @@ brave_shields::AdBlockCustomFiltersService* AdBlockService::custom_filters_service() { if (!custom_filters_service_) custom_filters_service_ = - brave_shields::AdBlockCustomFiltersServiceFactory( - component_delegate_); + brave_shields::AdBlockCustomFiltersServiceFactory(component_delegate_); return custom_filters_service_.get(); } AdBlockService::AdBlockService( brave_component_updater::BraveComponent::Delegate* delegate) - : AdBlockBaseService(delegate), - component_delegate_(delegate) { -} + : AdBlockBaseService(delegate), component_delegate_(delegate) {} AdBlockService::~AdBlockService() {} @@ -178,8 +238,8 @@ void AdBlockService::OnResourcesFileDataReady(const std::string& resources) { void AdBlockService::OnRegionalCatalogFileDataReady( const std::string& catalog_json) { - regional_service_manager() - ->SetRegionalCatalog(RegionalCatalogFromJSON(catalog_json)); + regional_service_manager()->SetRegionalCatalog( + RegionalCatalogFromJSON(catalog_json)); regional_service_manager()->Start(); } diff --git a/components/brave_shields/browser/ad_block_service.h b/components/brave_shields/browser/ad_block_service.h index d00572ab7caa..1f158c260dcb 100644 --- a/components/brave_shields/browser/ad_block_service.h +++ b/components/brave_shields/browser/ad_block_service.h @@ -12,6 +12,8 @@ #include #include +#include "base/optional.h" +#include "base/values.h" #include "brave/components/brave_shields/browser/ad_block_base_service.h" #include "components/keyed_service/core/keyed_service.h" #include "components/prefs/pref_registry_simple.h" @@ -51,6 +53,12 @@ class AdBlockService : public AdBlockBaseService { const std::string& tab_host, bool* did_match_exception, std::string* mock_data_url) override; + base::Optional UrlCosmeticResources( + const std::string& url) override; + base::Optional HiddenClassIdSelectors( + const std::vector& classes, + const std::vector& ids, + const std::vector& exceptions) override; AdBlockRegionalServiceManager* regional_service_manager(); AdBlockCustomFiltersService* custom_filters_service(); diff --git a/components/cosmetic_filters/browser/BUILD.gn b/components/cosmetic_filters/browser/BUILD.gn new file mode 100644 index 000000000000..a402627aa501 --- /dev/null +++ b/components/cosmetic_filters/browser/BUILD.gn @@ -0,0 +1,15 @@ +import("//build/config/features.gni") + +source_set("browser") { + sources = [ + "cosmetic_filters_resources.cc", + "cosmetic_filters_resources.h", + ] + + deps = [ + "//base", + "//brave/components/brave_shields/browser", + "//brave/components/cosmetic_filters/common:mojom", + "//components/content_settings/core/browser", + ] +} diff --git a/components/cosmetic_filters/browser/cosmetic_filters_resources.cc b/components/cosmetic_filters/browser/cosmetic_filters_resources.cc new file mode 100644 index 000000000000..de1c55c61dde --- /dev/null +++ b/components/cosmetic_filters/browser/cosmetic_filters_resources.cc @@ -0,0 +1,111 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 3.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/components/cosmetic_filters/browser/cosmetic_filters_resources.h" + +#include + +#include "base/json/json_reader.h" +#include "base/optional.h" +#include "base/values.h" +#include "brave/components/brave_shields/browser/ad_block_service.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" + +namespace cosmetic_filters { + +CosmeticFiltersResources::CosmeticFiltersResources( + HostContentSettingsMap* settings_map, + brave_shields::AdBlockService* ad_block_service) + : settings_map_(settings_map), + ad_block_service_(ad_block_service), + weak_factory_(this) {} + +CosmeticFiltersResources::~CosmeticFiltersResources() {} + +void CosmeticFiltersResources::HiddenClassIdSelectors( + const std::string& input, + const std::vector& exceptions, + HiddenClassIdSelectorsCallback callback) { + base::Optional input_value = base::JSONReader::Read(input); + if (!input_value || !input_value->is_dict()) { + // Nothing to work with + std::move(callback).Run(base::Value()); + + return; + } + base::DictionaryValue* input_dict; + if (!input_value->GetAsDictionary(&input_dict)) { + std::move(callback).Run(base::Value()); + + return; + } + std::vector classes; + base::ListValue* classes_list; + if (input_dict->GetList("classes", &classes_list)) { + for (size_t i = 0; i < classes_list->GetSize(); i++) { + if (!classes_list->GetList()[i].is_string()) { + continue; + } + classes.push_back(classes_list->GetList()[i].GetString()); + } + } + std::vector ids; + base::ListValue* ids_list; + if (input_dict->GetList("ids", &ids_list)) { + for (size_t i = 0; i < ids_list->GetSize(); i++) { + if (!ids_list->GetList()[i].is_string()) { + continue; + } + ids.push_back(ids_list->GetList()[i].GetString()); + } + } + + ad_block_service_->GetTaskRunner()->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&brave_shields::AdBlockService::HiddenClassIdSelectors, + base::Unretained(ad_block_service_), classes, ids, + exceptions), + base::BindOnce(&CosmeticFiltersResources::HiddenClassIdSelectorsOnUI, + weak_factory_.GetWeakPtr(), std::move(callback))); +} + +void CosmeticFiltersResources::HiddenClassIdSelectorsOnUI( + HiddenClassIdSelectorsCallback callback, + base::Optional resources) { + std::move(callback).Run(resources ? std::move(resources.value()) + : base::Value()); +} + +void CosmeticFiltersResources::UrlCosmeticResourcesOnUI( + UrlCosmeticResourcesCallback callback, + base::Optional resources) { + std::move(callback).Run(resources ? std::move(resources.value()) + : base::Value()); +} + +void CosmeticFiltersResources::ShouldDoCosmeticFiltering( + const std::string& url, + ShouldDoCosmeticFilteringCallback callback) { + bool enabled = + brave_shields::ShouldDoCosmeticFiltering(settings_map_, GURL(url)); + bool first_party_enabled = + brave_shields::IsFirstPartyCosmeticFilteringEnabled(settings_map_, + GURL(url)); + std::move(callback).Run(enabled, first_party_enabled); +} + +void CosmeticFiltersResources::UrlCosmeticResources( + const std::string& url, + UrlCosmeticResourcesCallback callback) { + ad_block_service_->GetTaskRunner()->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&brave_shields::AdBlockService::UrlCosmeticResources, + base::Unretained(ad_block_service_), url), + base::BindOnce(&CosmeticFiltersResources::UrlCosmeticResourcesOnUI, + weak_factory_.GetWeakPtr(), std::move(callback))); +} + +} // namespace cosmetic_filters diff --git a/components/cosmetic_filters/browser/cosmetic_filters_resources.h b/components/cosmetic_filters/browser/cosmetic_filters_resources.h new file mode 100644 index 000000000000..2b3b2050f199 --- /dev/null +++ b/components/cosmetic_filters/browser/cosmetic_filters_resources.h @@ -0,0 +1,70 @@ +/* Copyright (c) 2021 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 3.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_COMPONENTS_COSMETIC_FILTERS_BROWSER_COSMETIC_FILTERS_RESOURCES_H_ +#define BRAVE_COMPONENTS_COSMETIC_FILTERS_BROWSER_COSMETIC_FILTERS_RESOURCES_H_ + +#include +#include +#include + +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "base/values.h" +#include "brave/components/cosmetic_filters/common/cosmetic_filters.mojom.h" + +class HostContentSettingsMap; + +namespace brave_shields { +class AdBlockService; +} + +namespace cosmetic_filters { + +// CosmeticFiltersResources is a class that is responsible for interaction +// between CosmeticFiltersJSHandler class that lives inside renderer process. + +class CosmeticFiltersResources final + : public cosmetic_filters::mojom::CosmeticFiltersResources { + public: + CosmeticFiltersResources(const CosmeticFiltersResources&) = delete; + CosmeticFiltersResources& operator=(const CosmeticFiltersResources&) = delete; + CosmeticFiltersResources(HostContentSettingsMap* settings_map, + brave_shields::AdBlockService* ad_block_service); + ~CosmeticFiltersResources() override; + + // Sends back to renderer a response: do we need to apply cosmetic filters + // for the url. + void ShouldDoCosmeticFiltering( + const std::string& url, + ShouldDoCosmeticFilteringCallback callback) override; + + // Sends back to renderer a response about rules that has to be applied + // for the specified selectors. + void HiddenClassIdSelectors(const std::string& input, + const std::vector& exceptions, + HiddenClassIdSelectorsCallback callback) override; + + // Sends back to renderer a response what rules and scripts has to be + // applied for the specified url. + void UrlCosmeticResources(const std::string& url, + UrlCosmeticResourcesCallback callback) override; + + private: + void HiddenClassIdSelectorsOnUI(HiddenClassIdSelectorsCallback callback, + base::Optional resources); + + void UrlCosmeticResourcesOnUI(UrlCosmeticResourcesCallback callback, + base::Optional resources); + + HostContentSettingsMap* settings_map_; // Not owned + brave_shields::AdBlockService* ad_block_service_; // Not owned + + base::WeakPtrFactory weak_factory_; +}; + +} // namespace cosmetic_filters + +#endif // BRAVE_COMPONENTS_COSMETIC_FILTERS_BROWSER_COSMETIC_FILTERS_RESOURCES_H_ diff --git a/components/cosmetic_filters/common/BUILD.gn b/components/cosmetic_filters/common/BUILD.gn new file mode 100644 index 000000000000..e1e1c24ae303 --- /dev/null +++ b/components/cosmetic_filters/common/BUILD.gn @@ -0,0 +1,7 @@ +import("//mojo/public/tools/bindings/mojom.gni") + +mojom("mojom") { + sources = [ "cosmetic_filters.mojom" ] + + deps = [ "//mojo/public/mojom/base" ] +} diff --git a/components/cosmetic_filters/common/cosmetic_filters.mojom b/components/cosmetic_filters/common/cosmetic_filters.mojom new file mode 100644 index 000000000000..7827c00a1de8 --- /dev/null +++ b/components/cosmetic_filters/common/cosmetic_filters.mojom @@ -0,0 +1,12 @@ +module cosmetic_filters.mojom; + +import "mojo/public/mojom/base/values.mojom"; + +interface CosmeticFiltersResources { + ShouldDoCosmeticFiltering(string url) => (bool enabled, + bool first_party_enabled); + UrlCosmeticResources(string url) => (mojo_base.mojom.Value result); + // Receives an input string which is JSON object. + HiddenClassIdSelectors(string input, array exceptions) => ( + mojo_base.mojom.Value result); +}; diff --git a/components/cosmetic_filters/renderer/BUILD.gn b/components/cosmetic_filters/renderer/BUILD.gn new file mode 100644 index 000000000000..f3d6ebe5c0f2 --- /dev/null +++ b/components/cosmetic_filters/renderer/BUILD.gn @@ -0,0 +1,29 @@ +import("//build/config/features.gni") + +source_set("renderer") { + visibility = [ + "//brave:child_dependencies", + "//brave/renderer/*", + "//chrome/renderer/*", + "//components/content_settings/renderer/*", + ] + + sources = [ + "cosmetic_filters_js_handler.cc", + "cosmetic_filters_js_handler.h", + "cosmetic_filters_js_render_frame_observer.cc", + "cosmetic_filters_js_render_frame_observer.h", + ] + + deps = [ + "//base", + "//brave/components/cosmetic_filters/common:mojom", + "//brave/components/cosmetic_filters/resources/data:generated_resources", + "//content/public/renderer", + "//gin", + "//mojo/public/cpp/bindings", + "//third_party/blink/public:blink", + "//third_party/blink/public/common", + "//v8", + ] +} diff --git a/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.cc b/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.cc new file mode 100644 index 000000000000..2edd0e2d0f70 --- /dev/null +++ b/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.cc @@ -0,0 +1,371 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h" + +#include "base/bind.h" +#include "base/json/json_writer.h" +#include "base/no_destructor.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "brave/components/cosmetic_filters/resources/grit/cosmetic_filters_generated_map.h" +#include "content/public/renderer/render_frame.h" +#include "gin/arguments.h" +#include "gin/function_template.h" +#include "third_party/blink/public/common/browser_interface_broker_proxy.h" +#include "third_party/blink/public/web/blink.h" +#include "third_party/blink/public/web/web_local_frame.h" +#include "third_party/blink/public/web/web_script_source.h" +#include "ui/base/resource/resource_bundle.h" +#include "v8/include/v8.h" + +namespace { + +static base::NoDestructor g_observing_script(""); + +static base::NoDestructor> g_vetted_search_engines( + {"duckduckgo", "qwant", "bing", "startpage", "google", "yandex", "ecosia"}); + +const char kPreInitScript[] = + R"((function() { + if (window.content_cosmetic == undefined) { + window.content_cosmetic = {}; + } + %s + %s + })();)"; + +const char kScriptletInitScript[] = + R"(if (window.content_cosmetic.scriptlet == undefined) { + let text = %s; + window.content_cosmetic.scriptlet = `${text}`; + })"; + +const char kNonScriptletInitScript[] = + R"(if (window.content_cosmetic.hide1pContent === undefined) { + window.content_cosmetic.hide1pContent = %s; + } + if (window.content_cosmetic.generichide === undefined) { + window.content_cosmetic.generichide = %s; + })"; + +const char kSelectorsInjectScript[] = + R"((function() { + let nextIndex = + window.content_cosmetic.cosmeticStyleSheet.rules.length; + const selectors = %s; + selectors.forEach(selector => { + if ((typeof selector === 'string') && + !window.content_cosmetic.allSelectorsToRules.has(selector)) { + let rule = selector + '{display:none !important;}'; + window.content_cosmetic.cosmeticStyleSheet.insertRule( + `${rule}`, nextIndex); + window.content_cosmetic.allSelectorsToRules.set( + selector, nextIndex); + nextIndex++; + window.content_cosmetic.firstRunQueue.add(selector); + } + }); + if (!document.adoptedStyleSheets.includes( + window.content_cosmetic.cosmeticStyleSheet)) { + document.adoptedStyleSheets = + [window.content_cosmetic.cosmeticStyleSheet]; + }; + })();)"; + +const char kStyleSelectorsInjectScript[] = + R"((function() { + let nextIndex = + window.content_cosmetic.cosmeticStyleSheet.rules.length; + const selectors = %s; + for (let selector in selectors) { + if (!window.content_cosmetic.allSelectorsToRules.has(selector)) { + let rule = selector + '{'; + selectors[selector].forEach(prop => { + if (!rule.endsWith('{')) { + rule += ';'; + } + rule += prop; + }); + rule += '}'; + window.content_cosmetic.cosmeticStyleSheet.insertRule( + `${rule}`, nextIndex); + window.content_cosmetic.allSelectorsToRules.set( + selector, nextIndex); + nextIndex++; + }; + }; + if (!document.adoptedStyleSheets.includes( + window.content_cosmetic.cosmeticStyleSheet)){ + document.adoptedStyleSheets = + [window.content_cosmetic.cosmeticStyleSheet]; + }; + })();)"; + +std::string LoadDataResource(const int id) { + auto& resource_bundle = ui::ResourceBundle::GetSharedInstance(); + if (resource_bundle.IsGzipped(id)) { + return resource_bundle.LoadDataResourceString(id); + } + + return resource_bundle.GetRawDataResource(id).as_string(); +} + +bool IsVettedSearchEngine(const std::string& host) { + for (size_t i = 0; i < g_vetted_search_engines->size(); i++) { + size_t found_pos = host.find((*g_vetted_search_engines)[i]); + if (found_pos != std::string::npos) { + size_t last_dot_pos = host.find(".", found_pos + 1); + if (last_dot_pos == std::string::npos) { + return false; + } + if (host.find(".", last_dot_pos + 1) == std::string::npos) { + return true; + } + } + } + + return false; +} + +} // namespace + +namespace cosmetic_filters { + +CosmeticFiltersJSHandler::CosmeticFiltersJSHandler( + content::RenderFrame* render_frame, + const int32_t isolated_world_id) + : render_frame_(render_frame), + isolated_world_id_(isolated_world_id), + enabled_1st_party_cf_filtering_(false) { + if (g_observing_script->empty()) { + *g_observing_script = LoadDataResource(kCosmeticFiltersGenerated[0].value); + } + EnsureConnected(); +} + +CosmeticFiltersJSHandler::~CosmeticFiltersJSHandler() = default; + +void CosmeticFiltersJSHandler::HiddenClassIdSelectors( + const std::string& input) { + if (!EnsureConnected()) + return; + + cosmetic_filters_resources_->HiddenClassIdSelectors( + input, exceptions_, + base::BindOnce(&CosmeticFiltersJSHandler::OnHiddenClassIdSelectors, + base::Unretained(this))); +} + +void CosmeticFiltersJSHandler::AddJavaScriptObjectToFrame( + v8::Local context) { + v8::Isolate* isolate = blink::MainThreadIsolate(); + v8::HandleScope handle_scope(isolate); + if (context.IsEmpty()) + return; + + v8::Context::Scope context_scope(context); + + CreateWorkerObject(isolate, context); +} + +void CosmeticFiltersJSHandler::CreateWorkerObject( + v8::Isolate* isolate, + v8::Local context) { + v8::Local global = context->Global(); + v8::Local cosmetic_filters_obj; + v8::Local cosmetic_filters_value; + if (!global->Get(context, gin::StringToV8(isolate, "cf_worker")) + .ToLocal(&cosmetic_filters_value) || + !cosmetic_filters_value->IsObject()) { + cosmetic_filters_obj = v8::Object::New(isolate); + global + ->Set(context, gin::StringToSymbol(isolate, "cf_worker"), + cosmetic_filters_obj) + .Check(); + BindFunctionsToObject(isolate, cosmetic_filters_obj); + } +} + +void CosmeticFiltersJSHandler::BindFunctionsToObject( + v8::Isolate* isolate, + v8::Local javascript_object) { + BindFunctionToObject( + isolate, javascript_object, "hiddenClassIdSelectors", + base::BindRepeating(&CosmeticFiltersJSHandler::HiddenClassIdSelectors, + base::Unretained(this))); +} + +template +void CosmeticFiltersJSHandler::BindFunctionToObject( + v8::Isolate* isolate, + v8::Local javascript_object, + const std::string& name, + const base::RepeatingCallback& callback) { + v8::Local context = isolate->GetCurrentContext(); + // Get the isolate associated with this object. + javascript_object + ->Set(context, gin::StringToSymbol(isolate, name), + gin::CreateFunctionTemplate(isolate, callback) + ->GetFunction(context) + .ToLocalChecked()) + .Check(); +} + +bool CosmeticFiltersJSHandler::EnsureConnected() { + if (!cosmetic_filters_resources_.is_bound()) { + render_frame_->GetBrowserInterfaceBroker()->GetInterface( + cosmetic_filters_resources_.BindNewPipeAndPassReceiver()); + } + + return cosmetic_filters_resources_.is_bound(); +} + +void CosmeticFiltersJSHandler::ProcessURL(const GURL& url) { + if (!EnsureConnected()) + return; + + url_ = url; + cosmetic_filters_resources_->ShouldDoCosmeticFiltering( + url_.spec(), + base::BindOnce(&CosmeticFiltersJSHandler::OnShouldDoCosmeticFiltering, + base::Unretained(this))); +} + +void CosmeticFiltersJSHandler::OnShouldDoCosmeticFiltering( + bool enabled, + bool first_party_enabled) { + if (!enabled || !EnsureConnected()) + return; + + enabled_1st_party_cf_filtering_ = first_party_enabled; + cosmetic_filters_resources_->UrlCosmeticResources( + url_.spec(), + base::BindOnce(&CosmeticFiltersJSHandler::OnUrlCosmeticResources, + base::Unretained(this))); +} + +void CosmeticFiltersJSHandler::OnUrlCosmeticResources(base::Value result) { + base::DictionaryValue* resources_dict; + if (!result.GetAsDictionary(&resources_dict)) { + return; + } + + std::string pre_init_script; + std::string scriptlet_init_script; + std::string non_scriptlet_init_script; + std::string json_to_inject; + base::Value* injected_script = resources_dict->FindPath("injected_script"); + if (injected_script && + base::JSONWriter::Write(*injected_script, &json_to_inject) && + json_to_inject.length() > 1) { + scriptlet_init_script = + base::StringPrintf(kScriptletInitScript, json_to_inject.c_str()); + } + if (render_frame_->IsMainFrame()) { + bool generichide = false; + resources_dict->GetBoolean("generichide", &generichide); + non_scriptlet_init_script = + base::StringPrintf(kNonScriptletInitScript, + enabled_1st_party_cf_filtering_ ? "true" : "false", + generichide ? "true" : "false"); + } + pre_init_script = + base::StringPrintf(kPreInitScript, scriptlet_init_script.c_str(), + non_scriptlet_init_script.c_str()); + blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); + if (web_frame->IsProvisional()) + return; + web_frame->ExecuteScriptInIsolatedWorld( + isolated_world_id_, blink::WebString::FromUTF8(pre_init_script)); + if (!render_frame_->IsMainFrame()) + return; + + // Working on css rules, we do that on a main frame only + web_frame->ExecuteScriptInIsolatedWorld( + isolated_world_id_, blink::WebString::FromUTF8(*g_observing_script)); + + CSSRulesRoutine(resources_dict); +} + +void CosmeticFiltersJSHandler::CSSRulesRoutine( + base::DictionaryValue* resources_dict) { + if (url_.is_empty() || !url_.is_valid() || + IsVettedSearchEngine(url_.host())) { + return; + } + + blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); + base::ListValue* cf_exceptions_list; + if (resources_dict->GetList("exceptions", &cf_exceptions_list)) { + for (size_t i = 0; i < cf_exceptions_list->GetSize(); i++) { + exceptions_.push_back(cf_exceptions_list->GetList()[i].GetString()); + } + } + base::ListValue* hide_selectors_list; + if (resources_dict->GetList("hide_selectors", &hide_selectors_list)) { + std::string json_selectors; + if (!base::JSONWriter::Write(*hide_selectors_list, &json_selectors) || + json_selectors.empty()) { + json_selectors = "[]"; + } + // Building a script for stylesheet modifications + std::string new_selectors_script = + base::StringPrintf(kSelectorsInjectScript, json_selectors.c_str()); + if (hide_selectors_list->GetSize() != 0) { + web_frame->ExecuteScriptInIsolatedWorld( + isolated_world_id_, blink::WebString::FromUTF8(new_selectors_script)); + } + } + + base::DictionaryValue* style_selectors_dictionary = nullptr; + if (resources_dict->GetDictionary("style_selectors", + &style_selectors_dictionary)) { + std::string json_selectors; + if (!base::JSONWriter::Write(*style_selectors_dictionary, + &json_selectors) || + json_selectors.empty()) { + json_selectors = "[]"; + } + std::string new_selectors_script = + base::StringPrintf(kStyleSelectorsInjectScript, json_selectors.c_str()); + if (!json_selectors.empty()) { + web_frame->ExecuteScriptInIsolatedWorld( + isolated_world_id_, blink::WebString::FromUTF8(new_selectors_script)); + } + } + + if (!enabled_1st_party_cf_filtering_) { + web_frame->ExecuteScriptInIsolatedWorld( + isolated_world_id_, blink::WebString::FromUTF8(*g_observing_script)); + } +} + +void CosmeticFiltersJSHandler::OnHiddenClassIdSelectors(base::Value result) { + base::ListValue* selectors_list; + if (IsVettedSearchEngine(url_.host()) || !result.GetAsList(&selectors_list)) { + return; + } + blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame(); + std::string json_selectors; + if (!base::JSONWriter::Write(*selectors_list, &json_selectors) || + json_selectors.empty()) { + json_selectors = "[]"; + } + // Building a script for stylesheet modifications + std::string new_selectors_script = + base::StringPrintf(kSelectorsInjectScript, json_selectors.c_str()); + if (selectors_list->GetSize() != 0) { + web_frame->ExecuteScriptInIsolatedWorld( + isolated_world_id_, blink::WebString::FromUTF8(new_selectors_script)); + } + + if (!enabled_1st_party_cf_filtering_) { + web_frame->ExecuteScriptInIsolatedWorld( + isolated_world_id_, blink::WebString::FromUTF8(*g_observing_script)); + } +} + +} // namespace cosmetic_filters diff --git a/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h b/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h new file mode 100644 index 000000000000..f3b54f45854d --- /dev/null +++ b/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h @@ -0,0 +1,73 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_COSMETIC_FILTERS_RENDERER_COSMETIC_FILTERS_JS_HANDLER_H_ +#define BRAVE_COMPONENTS_COSMETIC_FILTERS_RENDERER_COSMETIC_FILTERS_JS_HANDLER_H_ + +#include +#include + +#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/remote.h" +#include "url/gurl.h" +#include "v8/include/v8.h" + +namespace cosmetic_filters { + +// CosmeticFiltersJSHandler class is responsible for JS execution inside a +// a given render_frame. It also does interactions with CosmeticFiltersResources +// class that lives in the main process. + +class CosmeticFiltersJSHandler { + public: + CosmeticFiltersJSHandler(content::RenderFrame* render_frame, + const int32_t isolated_world_id); + ~CosmeticFiltersJSHandler(); + + // Adds the "cs_worker" JavaScript object and its functions to the current + // render_frame_. + void AddJavaScriptObjectToFrame(v8::Local context); + void ProcessURL(const GURL& url); + + private: + void BindFunctionsToObject(v8::Isolate* isolate, + v8::Local javascript_object); + + // Adds a function to the provided object. + template + void BindFunctionToObject(v8::Isolate* isolate, + v8::Local javascript_object, + const std::string& name, + const base::RepeatingCallback& callback); + bool EnsureConnected(); + + void CreateWorkerObject(v8::Isolate* isolate, v8::Local context); + + // A function to be called from JS + void HiddenClassIdSelectors(const std::string& input); + + void OnShouldDoCosmeticFiltering(bool enabled, bool first_party_enabled); + void OnUrlCosmeticResources(base::Value result); + void CSSRulesRoutine(base::DictionaryValue* resources_dict); + void OnHiddenClassIdSelectors(base::Value result); + + content::RenderFrame* render_frame_; + mojo::Remote + cosmetic_filters_resources_; + int32_t isolated_world_id_; + bool enabled_1st_party_cf_filtering_; + std::vector exceptions_; + GURL url_; +}; + +// static +v8::Local GetOrCreateWorkerObject(v8::Isolate* isolate, + v8::Local context); + +} // namespace cosmetic_filters + +#endif // BRAVE_COMPONENTS_COSMETIC_FILTERS_RENDERER_COSMETIC_FILTERS_JS_HANDLER_H_ diff --git a/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.cc b/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.cc new file mode 100644 index 000000000000..aeeec6834a27 --- /dev/null +++ b/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.cc @@ -0,0 +1,59 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h" + +#include "base/bind.h" +#include "content/public/renderer/render_frame.h" +#include "third_party/blink/public/web/web_local_frame.h" + +namespace cosmetic_filters { + +CosmeticFiltersJsRenderFrameObserver::CosmeticFiltersJsRenderFrameObserver( + content::RenderFrame* render_frame, + const int32_t isolated_world_id) + : RenderFrameObserver(render_frame), + isolated_world_id_(isolated_world_id) {} + +CosmeticFiltersJsRenderFrameObserver::~CosmeticFiltersJsRenderFrameObserver() {} + +void CosmeticFiltersJsRenderFrameObserver::DidStartNavigation( + const GURL& url, + base::Optional navigation_type) { + url_ = url; +} + +void CosmeticFiltersJsRenderFrameObserver::DidCreateScriptContext( + v8::Local context, + int32_t world_id) { + if (!render_frame()->IsMainFrame() || world_id != isolated_world_id_ || + !native_javascript_handle_) + return; + + native_javascript_handle_->AddJavaScriptObjectToFrame(context); +} + +void CosmeticFiltersJsRenderFrameObserver::DidCreateNewDocument() { + // There could be empty and "about:blank" URLs, empty URLs are duplicated + // with DidCreateDocumentElement, so we just skip them, "about:blank" + // should fallback to the main frame rules + if (url_.is_empty()) + return; + if (url_.spec() == "about:blank") { + url_ = url::Origin(render_frame()->GetWebFrame()->GetSecurityOrigin()) + .GetURL(); + } + if (!native_javascript_handle_) { + native_javascript_handle_.reset( + new CosmeticFiltersJSHandler(render_frame(), isolated_world_id_)); + } + native_javascript_handle_->ProcessURL(url_); +} + +void CosmeticFiltersJsRenderFrameObserver::OnDestruct() { + delete this; +} + +} // namespace cosmetic_filters diff --git a/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h b/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h new file mode 100644 index 000000000000..7b2e08d66179 --- /dev/null +++ b/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h @@ -0,0 +1,53 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_COSMETIC_FILTERS_RENDERER_COSMETIC_FILTERS_JS_RENDER_FRAME_OBSERVER_H_ +#define BRAVE_COMPONENTS_COSMETIC_FILTERS_RENDERER_COSMETIC_FILTERS_JS_RENDER_FRAME_OBSERVER_H_ + +#include + +#include "base/optional.h" +#include "brave/components/cosmetic_filters/renderer/cosmetic_filters_js_handler.h" +#include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_frame_observer.h" +#include "third_party/blink/public/web/web_navigation_type.h" +#include "url/gurl.h" +#include "v8/include/v8.h" + +namespace cosmetic_filters { + +// CosmeticFiltersJsRenderFrame observer waits for a page to be loaded and then +// adds the Javascript worker object. +class CosmeticFiltersJsRenderFrameObserver + : public content::RenderFrameObserver { + public: + CosmeticFiltersJsRenderFrameObserver(content::RenderFrame* render_frame, + const int32_t isolated_world_id); + ~CosmeticFiltersJsRenderFrameObserver() override; + + // RenderFrameObserver implementation. + void DidStartNavigation( + const GURL& url, + base::Optional navigation_type) override; + void DidCreateScriptContext(v8::Local context, + int32_t world_id) override; + void DidCreateNewDocument() override; + + private: + // RenderFrameObserver implementation. + void OnDestruct() override; + + // The isolated world that the cosmetic filters object should be written to. + int32_t isolated_world_id_; + + // Handle to "handler" JavaScript object functionality. + std::unique_ptr native_javascript_handle_; + + GURL url_; +}; + +} // namespace cosmetic_filters + +#endif // BRAVE_COMPONENTS_COSMETIC_FILTERS_RENDERER_COSMETIC_FILTERS_JS_RENDER_FRAME_OBSERVER_H_ diff --git a/components/cosmetic_filters/resources/data/BUILD.gn b/components/cosmetic_filters/resources/data/BUILD.gn new file mode 100644 index 000000000000..c623b7c11c95 --- /dev/null +++ b/components/cosmetic_filters/resources/data/BUILD.gn @@ -0,0 +1,18 @@ +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"), + ] ] + + resource_name = "cosmetic_filters" +} + +pack_web_resources("generated_resources") { + resource_name = "cosmetic_filters" + output_dir = "$root_gen_dir/brave/components/cosmetic_filters/resources" + deps = [ ":cosmetic_filters_resources" ] +} diff --git a/components/brave_extension/extension/brave_extension/content_cosmetic.ts b/components/cosmetic_filters/resources/data/content_cosmetic.ts similarity index 81% rename from components/brave_extension/extension/brave_extension/content_cosmetic.ts rename to components/cosmetic_filters/resources/data/content_cosmetic.ts index 6362df00ca69..67e1e3ce20ba 100644 --- a/components/brave_extension/extension/brave_extension/content_cosmetic.ts +++ b/components/cosmetic_filters/resources/data/content_cosmetic.ts @@ -3,19 +3,14 @@ // 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/. -// Notify the background script as soon as the content script has loaded. -// chrome.tabs.insertCSS may sometimes fail to inject CSS in a newly navigated -// page when using the chrome.webNavigation API. -// See: https://bugs.chromium.org/p/chromium/issues/detail?id=331654#c15 -// The RenderView should always be ready when the content script begins, so -// this message is used to trigger CSS insertion instead. -chrome.runtime.sendMessage({ - type: 'contentScriptsLoaded', - location: window.location -}) +// That script is executed from +// components/cosmetic_filters/content/renderer/cosmetic_filters_js_handler.cc +// several times: +// - for scriptlets injection; +// - for cosmetic filters work with CSS and stylesheet. That work itself +// could call the script several times. const { parseDomain, ParseResultType } = require('parse-domain') - // Start looking for things to unhide before at most this long after // the backend script is up and connected (eg backgroundReady = true), // or sooner if the thread is idle. @@ -35,10 +30,39 @@ let notYetQueriedClasses: string[] let notYetQueriedIds: string[] let cosmeticObserver: MutationObserver | undefined = undefined -const allSelectorsToRules = new Map() -const cosmeticStyleSheet = new CSSStyleSheet() - -const injectScriptlet = (text: string) => { +window.content_cosmetic = window.content_cosmetic || {} +window.content_cosmetic.cosmeticStyleSheet = + window.content_cosmetic.cosmeticStyleSheet || new CSSStyleSheet() +window.content_cosmetic.allSelectorsToRules = + window.content_cosmetic.allSelectorsToRules || new Map() +window.content_cosmetic.observingHasStarted = + window.content_cosmetic.observingHasStarted || false +// All new selectors go in `firstRunQueue` +window.content_cosmetic.firstRunQueue = + window.content_cosmetic.firstRunQueue || new Set() +// Third party matches go in the second and third queues. +window.content_cosmetic.secondRunQueue = + window.content_cosmetic.secondRunQueue || new Set() +// Once a selector gets in to this queue, it's only evaluated for 1p content one +// more time. +window.content_cosmetic.finalRunQueue = + window.content_cosmetic.finalRunQueue || new Set() +window.content_cosmetic.allQueues = window.content_cosmetic.allQueues || + [window.content_cosmetic.firstRunQueue, + window.content_cosmetic.secondRunQueue, + window.content_cosmetic.finalRunQueue] +window.content_cosmetic.numQueues = + window.content_cosmetic.numQueues || window.content_cosmetic.allQueues.length +window.content_cosmetic.alreadyUnhiddenSelectors = + window.content_cosmetic.alreadyUnhiddenSelectors || new Set() +window.content_cosmetic.alreadyKnownFirstPartySubtrees = + window.content_cosmetic.alreadyKnownFirstPartySubtrees || new WeakSet() +window.content_cosmetic._hasDelayOcurred = + window.content_cosmetic._hasDelayOcurred || false +window.content_cosmetic._startCheckingId = + window.content_cosmetic._startCheckingId || undefined + +function injectScriptlet (text: string) { let script try { script = document.createElement('script') @@ -104,11 +128,12 @@ const fetchNewClassIdRules = () => { (!notYetQueriedIds || notYetQueriedIds.length === 0)) { return } - chrome.runtime.sendMessage({ - type: 'hiddenClassIdSelectors', - classes: notYetQueriedClasses || [], - ids: notYetQueriedIds || [] - }) + // Callback to c++ renderer process + // @ts-ignore + cf_worker.hiddenClassIdSelectors( + JSON.stringify({ + classes: notYetQueriedClasses, ids: notYetQueriedIds + })) notYetQueriedClasses = [] notYetQueriedIds = [] } @@ -344,26 +369,26 @@ const unhideSelectors = (selectors: Set) => { } // Find selectors we have a rule index for const rulesToRemove = Array.from(selectors) - .map(selector => allSelectorsToRules.get(selector)) + .map(selector => window.content_cosmetic.allSelectorsToRules.get(selector)) .filter(i => i !== undefined) .sort() .reverse() // Delete the rules - let lastIdx: number = allSelectorsToRules.size - 1 + let lastIdx: number = window.content_cosmetic.allSelectorsToRules.size - 1 for (const ruleIdx of rulesToRemove) { // Safe to asset ruleIdx is a number because we've already filtered out // any `undefined` instances with the filter call above. - cosmeticStyleSheet.deleteRule(ruleIdx as number) + window.content_cosmetic.cosmeticStyleSheet.deleteRule(ruleIdx as number) } // Re-sync the indexes // TODO: Sync is hard, just re-build by iterating through the StyleSheet rules. - const ruleLookup = Array.from(allSelectorsToRules.entries()) + const ruleLookup = Array.from(window.content_cosmetic.allSelectorsToRules.entries()) let countAtLastHighest = rulesToRemove.length for (let i = lastIdx; i > 0; i--) { const [selector, oldIdx] = ruleLookup[i] // Is this one we removed? if (rulesToRemove.includes(i)) { - allSelectorsToRules.delete(selector) + window.content_cosmetic.allSelectorsToRules.delete(selector) countAtLastHighest-- if (countAtLastHighest === 0) { break @@ -374,21 +399,10 @@ const unhideSelectors = (selectors: Set) => { // Probably out of sync console.error('Cosmetic Filters: old index did not match lookup index', { selector, oldIdx, i }) } - allSelectorsToRules.set(selector, oldIdx - countAtLastHighest) + window.content_cosmetic.allSelectorsToRules.set(selector, oldIdx - countAtLastHighest) } } -const alreadyUnhiddenSelectors = new Set() -const alreadyKnownFirstPartySubtrees = new WeakSet() -// All new selectors go in `firstRunQueue` -const firstRunQueue = new Set() -// Third party matches go in the second and third queues. -const secondRunQueue = new Set() -// Once a selector gets in to this queue, it's only evaluated for 1p content one -// more time. -const finalRunQueue = new Set() -const allQueues = [firstRunQueue, secondRunQueue, finalRunQueue] -const numQueues = allQueues.length const pumpIntervalMinMs = 40 const pumpIntervalMaxMs = 1000 const maxWorkSize = 60 @@ -411,9 +425,9 @@ const pumpCosmeticFilterQueues = () => { // For each "pump", walk through each queue until we find selectors // to evaluate. This means that nothing in queue N+1 will be evaluated // until queue N is completely empty. - for (let queueIndex = 0; queueIndex < numQueues; queueIndex += 1) { - const currentQueue = allQueues[queueIndex] - const nextQueue = allQueues[queueIndex + 1] + for (let queueIndex = 0; queueIndex < window.content_cosmetic.numQueues; queueIndex += 1) { + const currentQueue = window.content_cosmetic.allQueues[queueIndex] + const nextQueue = window.content_cosmetic.allQueues[queueIndex + 1] if (currentQueue.size === 0) { continue } @@ -430,7 +444,7 @@ const pumpCosmeticFilterQueues = () => { // Don't recheck elements / subtrees we already know are first party. // Once we know something is third party, we never need to evaluate it // again. - if (alreadyKnownFirstPartySubtrees.has(aMatchingElm)) { + if (window.content_cosmetic.alreadyKnownFirstPartySubtrees.has(aMatchingElm)) { continue } @@ -453,14 +467,14 @@ const pumpCosmeticFilterQueues = () => { // Similarly, if we already know a selector matches 1p content, // there is no need to notify the background script again, so // we don't need to consider further. - if (alreadyUnhiddenSelectors.has(selector) === true) { + if (window.content_cosmetic.alreadyUnhiddenSelectors.has(selector) === true) { continue } newlyIdentifiedFirstPartySelectors.add(selector) - alreadyUnhiddenSelectors.add(selector) + window.content_cosmetic.alreadyUnhiddenSelectors.add(selector) } - alreadyKnownFirstPartySubtrees.add(aMatchingElm) + window.content_cosmetic.alreadyKnownFirstPartySubtrees.add(aMatchingElm) } unhideSelectors(newlyIdentifiedFirstPartySelectors) @@ -526,24 +540,22 @@ const startObserving = () => { cosmeticObserver.observe(document.documentElement, observerConfig) } -let _hasDelayOcurred: boolean = false -let _startCheckingId: number | undefined = undefined const scheduleQueuePump = (hide1pContent: boolean, generichide: boolean) => { // Three states possible here. First, the delay has already occurred. If so, // pass through to pumpCosmeticFilterQueues immediately. - if (_hasDelayOcurred === true) { + if (window.content_cosmetic._hasDelayOcurred === true) { pumpCosmeticFilterQueuesOnIdle() return } // Second possibility is that we're already waiting for the delay to pass / // occur. In this case, do nothing. - if (_startCheckingId !== undefined) { + if (window.content_cosmetic._startCheckingId !== undefined) { return } // Third / final possibility, this is this the first time this has been // called, in which case set up a timmer and quit - _startCheckingId = window.requestIdleCallback(function ({ didTimeout }) { - _hasDelayOcurred = true + window.content_cosmetic._startCheckingId = window.requestIdleCallback(function ({ didTimeout }) { + window.content_cosmetic._hasDelayOcurred = true if (!generichide) { startObserving() } @@ -553,45 +565,14 @@ const scheduleQueuePump = (hide1pContent: boolean, generichide: boolean) => { }, { timeout: maxTimeMSBeforeStart }) } -const vettedSearchEngines = ['duckduckgo', 'qwant', 'bing', 'startpage', 'google', 'yandex', 'ecosia'] - -chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { - const action = typeof msg === 'string' ? msg : msg.type - switch (action) { - case 'cosmeticFilteringBackgroundReady': { - if (msg.hideOptions !== undefined) { - scheduleQueuePump(msg.hideOptions.hide1pContent, msg.hideOptions.generichide) - } - injectScriptlet(msg.scriptlet) - break - } - case 'cosmeticFilterConsiderNewSelectors': { - const { selectors } = msg - let nextIndex = cosmeticStyleSheet.rules.length - for (const selector of selectors) { - if (_parsedCurrentDomain.type === ParseResultType.Listed && vettedSearchEngines.includes(_parsedCurrentDomain.icann.domain)) { - continue - } - if (allSelectorsToRules.has(selector)) { - continue - } - // insertRule always adds to index 0, - // so we always add to end of list manually. - cosmeticStyleSheet.insertRule( - `${selector}{display:none !important;}`, - nextIndex - ) - allSelectorsToRules.set(selector, nextIndex) - nextIndex++ - firstRunQueue.add(selector) - } - // @ts-ignore - if (!document.adoptedStyleSheets.includes(cosmeticStyleSheet)) { - // @ts-ignore - document.adoptedStyleSheets = [cosmeticStyleSheet] - } - scheduleQueuePump(false, false) - break - } - } -}) +if (!window.content_cosmetic.observingHasStarted) { + window.content_cosmetic.observingHasStarted = true + scheduleQueuePump(window.content_cosmetic.hide1pContent, + window.content_cosmetic.generichide) +} else if (window.content_cosmetic.scriptlet) { + let scriptlet = window.content_cosmetic.scriptlet + window.content_cosmetic.scriptlet = '' + injectScriptlet(scriptlet) +} else { + scheduleQueuePump(false, false) +} diff --git a/components/definitions/global.d.ts b/components/definitions/global.d.ts index 2e626536c36a..5e01edf5061b 100644 --- a/components/definitions/global.d.ts +++ b/components/definitions/global.d.ts @@ -72,5 +72,22 @@ declare global { } alreadyInserted: boolean web3: any + content_cosmetic: { + cosmeticStyleSheet: CSSStyleSheet + allSelectorsToRules: Map + observingHasStarted: boolean + hide1pContent: boolean + generichide: boolean + firstRunQueue: Set + secondRunQueue: Set + finalRunQueue: Set + allQueues: Set[] + numQueues: any + alreadyUnhiddenSelectors: Set + alreadyKnownFirstPartySubtrees: WeakSet + _hasDelayOcurred: boolean + _startCheckingId: number | undefined + scriptlet: string + } } } diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn index b629e19c155f..aaf851647ab3 100644 --- a/components/resources/BUILD.gn +++ b/components/resources/BUILD.gn @@ -1,4 +1,5 @@ -import("//brave/components/brave_perf_predictor/browser/buildflags/buildflags.gni") +import( + "//brave/components/brave_perf_predictor/browser/buildflags/buildflags.gni") import("//brave/components/brave_rewards/browser/buildflags/buildflags.gni") import("//brave/components/brave_wallet/buildflags/buildflags.gni") import("//brave/components/crypto_dot_com/browser/buildflags/buildflags.gni") @@ -19,23 +20,16 @@ grit("static_resources") { "grit/brave_components_resources.h", "brave_components_static.pak", ] - deps = [ - ":strings", - ] + deps = [ ":strings" ] if (enable_extensions) { - deps += [ - "//brave/components/brave_extension/extension/brave_extension", - ] + deps += [ "//brave/components/brave_extension/extension/brave_extension" ] } if (brave_rewards_enabled) { - deps += [ - "//brave/components/brave_rewards/resources", - ] + deps += [ "//brave/components/brave_rewards/resources" ] if (enable_extensions) { - deps += [ - "//brave/components/brave_rewards/resources/extension:resources", - ] + deps += + [ "//brave/components/brave_rewards/resources/extension:resources" ] } } @@ -59,35 +53,31 @@ repack("resources") { deps = [ ":static_resources", "//brave/components/brave_adblock_ui:generated_resources", - "//brave/components/webcompat_reporter/ui:generated_resources", "//brave/components/brave_new_tab_ui:generated_resources", "//brave/components/brave_welcome_ui:generated_resources", + "//brave/components/cosmetic_filters/resources/data:generated_resources", + "//brave/components/webcompat_reporter/ui:generated_resources", ] sources = [ - "$root_gen_dir/components/brave_components_static.pak", "$root_gen_dir/brave/components/brave_adblock/resources/brave_adblock_generated.pak", - "$root_gen_dir/brave/components/webcompat_reporter/resources/webcompat_reporter_generated.pak", "$root_gen_dir/brave/components/brave_new_tab/resources/brave_new_tab_generated.pak", "$root_gen_dir/brave/components/brave_welcome/resources/brave_welcome_generated.pak", + "$root_gen_dir/brave/components/cosmetic_filters/resources/cosmetic_filters_generated.pak", + "$root_gen_dir/brave/components/webcompat_reporter/resources/webcompat_reporter_generated.pak", + "$root_gen_dir/components/brave_components_static.pak", ] if (ipfs_enabled) { - deps += [ - "//brave/components/ipfs_ui:generated_resources", - ] + deps += [ "//brave/components/ipfs_ui:generated_resources" ] sources += [ "$root_gen_dir/brave/components/ipfs_ui/resources/ipfs_generated.pak", ] } if (brave_wallet_enabled) { - deps += [ - "//brave/components/brave_wallet_ui:generated_resources", - ] - sources += [ - "$root_gen_dir/brave/components/brave_wallet/resources/brave_wallet_generated.pak", - ] + deps += [ "//brave/components/brave_wallet_ui:generated_resources" ] + sources += [ "$root_gen_dir/brave/components/brave_wallet/resources/brave_wallet_generated.pak" ] } output = "$root_gen_dir/components/brave_components_resources.pak" @@ -96,9 +86,7 @@ repack("resources") { grit("strings") { source = "brave_components_strings.grd" - outputs = [ - "grit/brave_components_strings.h", - ] + outputs = [ "grit/brave_components_strings.h" ] defines = [ "enable_speedreader=$enable_speedreader", @@ -122,7 +110,5 @@ action("about_credits") { outputs = [ "$root_gen_dir/npm_licenses.checked" ] - args = [ - rebase_path("$root_gen_dir/npm_licenses.checked") - ] + args = [ rebase_path("$root_gen_dir/npm_licenses.checked") ] } diff --git a/renderer/BUILD.gn b/renderer/BUILD.gn index 40e8d9b4f948..75a7921f7090 100644 --- a/renderer/BUILD.gn +++ b/renderer/BUILD.gn @@ -2,13 +2,13 @@ import("//build/config/features.gni") source_set("renderer") { visibility = [ - "//chrome/renderer/*", - "//brave/renderer/*", "//brave:child_dependencies", - "//brave/test:*" + "//brave/renderer/*", + "//brave/test:*", + "//chrome/renderer/*", ] - public_deps = [ - "//chrome/renderer", - ] + public_deps = [ "//chrome/renderer" ] + + deps = [ "//brave/components/cosmetic_filters/renderer" ] } diff --git a/renderer/brave_content_renderer_client.cc b/renderer/brave_content_renderer_client.cc index 57d2952ebb02..ce59ae3a121e 100644 --- a/renderer/brave_content_renderer_client.cc +++ b/renderer/brave_content_renderer_client.cc @@ -4,6 +4,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "brave/renderer/brave_content_renderer_client.h" + +#include "brave/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h" +#include "chrome/common/chrome_isolated_world_ids.h" #include "third_party/blink/public/platform/web_runtime_features.h" BraveContentRendererClient::BraveContentRendererClient() @@ -17,3 +20,11 @@ SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() { blink::WebRuntimeFeatures::EnableSharedArrayBuffer(false); } BraveContentRendererClient::~BraveContentRendererClient() = default; + +void BraveContentRendererClient::RenderFrameCreated( + content::RenderFrame* render_frame) { + ChromeContentRendererClient::RenderFrameCreated(render_frame); + + new cosmetic_filters::CosmeticFiltersJsRenderFrameObserver( + render_frame, ISOLATED_WORLD_ID_CHROME_INTERNAL); +} diff --git a/renderer/brave_content_renderer_client.h b/renderer/brave_content_renderer_client.h index 5f3d4b5f18c2..2b90ef85fb73 100644 --- a/renderer/brave_content_renderer_client.h +++ b/renderer/brave_content_renderer_client.h @@ -1,4 +1,5 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public +/* 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/. */ @@ -12,6 +13,7 @@ class BraveContentRendererClient : public ChromeContentRendererClient { BraveContentRendererClient(); ~BraveContentRendererClient() override; void SetRuntimeFeaturesDefaultsBeforeBlinkInitialization() override; + void RenderFrameCreated(content::RenderFrame* render_frame) override; private: DISALLOW_COPY_AND_ASSIGN(BraveContentRendererClient); diff --git a/renderer/sources.gni b/renderer/sources.gni index 00cb3d91dd12..e1db95611bb6 100644 --- a/renderer/sources.gni +++ b/renderer/sources.gni @@ -7,7 +7,8 @@ brave_chrome_renderer_sources = [ "//brave/renderer/brave_content_renderer_client.cc", "//brave/renderer/brave_content_renderer_client.h", ] + brave_chrome_renderer_public_deps = [ "//brave/components/content_settings/renderer", - "//third_party/blink/public:blink" + "//brave/components/cosmetic_filters/renderer", ]