From: chromium-sdk Date: Thu, 12 Oct 2023 14:46:02 +0200 Subject: eyeo Browser Ad filtering Solution: Chrome Integration Module Based on Chromium 118.0.5993.48 Pre-requisites: eyeo Browser Ad filtering Solution: Base Module --- chrome/app/chrome_main_delegate.cc | 7 +- chrome/browser/BUILD.gn | 29 ++ chrome/browser/adblock/README.md | 3 + .../adblock/adblock_content_browser_client.cc | 320 ++++++++++++ .../adblock/adblock_content_browser_client.h | 100 ++++ ...adblock_content_browser_client_unittest.cc | 203 ++++++++ .../adblock/adblock_controller_factory.cc | 67 +++ .../adblock/adblock_controller_factory.h | 49 ++ .../adblock_telemetry_service_factory.cc | 63 +++ .../adblock_telemetry_service_factory.h | 51 ++ chrome/browser/adblock/android/BUILD.gn | 68 +++ .../adblock/AdblockControllerTest.java | 43 ++ .../adblock/FilteringConfigurationTest.java | 418 ++++++++++++++++ .../ResourceClassificationNotifierTest.java | 135 +++++ .../adblock/TestPagesCircumventionTest.java | 51 ++ .../browser/adblock/TestPagesCspTest.java | 51 ++ .../adblock/TestPagesElemhideEmuInvTest.java | 48 ++ .../adblock/TestPagesElemhideEmuTest.java | 48 ++ .../adblock/TestPagesElemhideTest.java | 48 ++ .../adblock/TestPagesExceptionTest.java | 100 ++++ .../browser/adblock/TestPagesFilterTest.java | 101 ++++ .../adblock/TestPagesHeaderFilterTest.java | 51 ++ .../browser/adblock/TestPagesHelper.java | 121 +++++ .../browser/adblock/TestPagesRewriteTest.java | 51 ++ .../browser/adblock/TestPagesSiteKeyTest.java | 51 ++ .../adblock/TestPagesSnippetsTest.java | 51 ++ .../adblock/TestPagesWebsocketTest.java | 51 ++ ...ontent_security_policy_injector_factory.cc | 69 +++ ...content_security_policy_injector_factory.h | 49 ++ .../browser/adblock/element_hider_factory.cc | 63 +++ .../browser/adblock/element_hider_factory.h | 47 ++ .../resource_classification_runner_factory.cc | 71 +++ .../resource_classification_runner_factory.h | 49 ++ .../browser/adblock/session_stats_factory.cc | 64 +++ .../browser/adblock/session_stats_factory.h | 47 ++ .../adblock/sitekey_storage_factory.cc | 59 +++ .../browser/adblock/sitekey_storage_factory.h | 47 ++ ...ubscription_persistent_metadata_factory.cc | 63 +++ ...subscription_persistent_metadata_factory.h | 49 ++ .../adblock/subscription_service_factory.cc | 66 +++ .../adblock/subscription_service_factory.h | 50 ++ ...lock_content_browser_client_browsertest.cc | 125 +++++ .../test/adblock_debug_url_browsertest.cc | 314 ++++++++++++ .../test/adblock_filter_list_browsertest.cc | 260 ++++++++++ ...ck_filtering_configurations_browsertest.cc | 459 +++++++++++++++++ ...ock_frame_hierarchy_builder_browsertest.cc | 464 ++++++++++++++++++ .../test/adblock_multiple_tabs_browsertest.cc | 160 ++++++ .../test/adblock_non_ascii_browsertest.cc | 78 +++ .../adblock/test/adblock_popup_browsertest.cc | 463 +++++++++++++++++ .../test/adblock_snippets_browsertest.cc | 80 +++ ...dblock_subscription_service_browsertest.cc | 201 ++++++++ .../adblock_telemetry_service_browsertest.cc | 256 ++++++++++ .../test/adblock_web_bundle_browsertest.cc | 427 ++++++++++++++++ .../chrome_browser_interface_binders.cc | 9 + .../client_hints/client_hints_browsertest.cc | 9 +- chrome/browser/net/errorpage_browsertest.cc | 8 + ..._page_load_metrics_observer_browsertest.cc | 7 +- chrome/browser/preferences/BUILD.gn | 5 + .../prefs/chrome_pref_service_factory.cc | 11 + ...hrome_browser_main_extra_parts_profiles.cc | 26 + .../profile_keyed_service_browsertest.cc | 42 +- chrome/browser/resources/BUILD.gn | 5 + .../resources/adblock_internals/BUILD.gn | 29 ++ .../adblock_internals/adblock_internals.html | 38 ++ .../adblock_internals/adblock_internals.ts | 43 ++ .../safe_browsing_blocking_page_test.cc | 8 +- ...subresource_filter_browser_test_harness.cc | 8 +- chrome/browser/ui/BUILD.gn | 12 +- chrome/browser/ui/prefs/pref_watcher.cc | 13 + chrome/browser/ui/tab_helpers.cc | 18 + .../ui/webui/adblock_internals/BUILD.gn | 23 + .../adblock_internals/adblock_internals.mojom | 20 + .../adblock_internals_page_handler_impl.cc | 115 +++++ .../adblock_internals_page_handler_impl.h | 51 ++ .../adblock_internals/adblock_internals_ui.cc | 47 ++ .../adblock_internals/adblock_internals_ui.h | 48 ++ .../webui/chrome_web_ui_controller_factory.cc | 11 +- chrome/common/BUILD.gn | 3 + chrome/common/webui_url_constants.cc | 5 + chrome/common/webui_url_constants.h | 5 + chrome/test/BUILD.gn | 27 + chrome/test/base/chrome_test_launcher.cc | 7 +- chrome/test/base/in_process_browser_test.cc | 21 +- .../filterlist_that_allows_resource.txt | 7 + .../filterlist_that_blocks_resource.txt | 6 + .../filterlist_that_hides_resource.txt | 6 + chrome/test/data/adblock/innermost_frame.html | 39 ++ chrome/test/data/adblock/middle_frame.html | 25 + chrome/test/data/adblock/non-ascii.html | 26 + chrome/test/data/adblock/outermost_frame.html | 37 ++ .../outermost_frame_with_about_blank.html | 37 ++ chrome/test/data/adblock/popup.html | 25 + chrome/test/data/adblock/popup_opener.html | 26 + chrome/test/data/adblock/popup_parent.html | 25 + chrome/test/data/adblock/resource.png | Bin 0 -> 47293 bytes chrome/test/data/adblock/tab-restore.html | 33 ++ chrome/test/data/adblock/wbn/LICENSE | 14 + .../data/adblock/wbn/blocked_bundle/LICENSE | 14 + .../fetch_result_1_blocked_bundle.json | 1 + .../fetch_result_2_blocked_bundle.json | 1 + .../data/adblock/wbn/by_bundle_file/LICENSE | 14 + .../fetch_result_2_subresource_loading.json | 1 + .../green_subresource_loading.css | 1 + .../green_subresource_loading.png | Bin 0 -> 772 bytes .../purple_subresource_loading.css | 1 + .../purple_subresource_loading.png | Bin 0 -> 773 bytes .../xhr_result_2_subresource_loading.json | 1 + .../test/data/adblock/wbn/by_resource/LICENSE | 14 + .../by_resource/blue_subresource_loading.css | 1 + .../by_resource/blue_subresource_loading.png | Bin 0 -> 773 bytes .../fetch_result_1_subresource_loading.json | 1 + .../by_resource/red_subresource_loading.css | 1 + .../by_resource/red_subresource_loading.png | Bin 0 -> 772 bytes .../xhr_result_1_subresource_loading.json | 1 + chrome/test/data/adblock/wbn/by_scope/LICENSE | 14 + .../fetch_result_3_subresource_loading.json | 1 + .../by_scope/orange_subresource_loading.css | 1 + .../by_scope/orange_subresource_loading.png | Bin 0 -> 774 bytes .../wbn/by_scope/pink_subresource_loading.css | 1 + .../wbn/by_scope/pink_subresource_loading.png | Bin 0 -> 775 bytes .../xhr_result_3_subresource_loading.json | 1 + .../test/data/adblock/wbn/index.html.mustache | 157 ++++++ chrome/test/data/adblock/xpath3.html | 23 + 123 files changed, 7677 insertions(+), 12 deletions(-) create mode 100644 chrome/browser/adblock/README.md create mode 100644 chrome/browser/adblock/adblock_content_browser_client.cc create mode 100644 chrome/browser/adblock/adblock_content_browser_client.h create mode 100644 chrome/browser/adblock/adblock_content_browser_client_unittest.cc create mode 100644 chrome/browser/adblock/adblock_controller_factory.cc create mode 100644 chrome/browser/adblock/adblock_controller_factory.h create mode 100644 chrome/browser/adblock/adblock_telemetry_service_factory.cc create mode 100644 chrome/browser/adblock/adblock_telemetry_service_factory.h create mode 100644 chrome/browser/adblock/android/BUILD.gn create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/AdblockControllerTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/FilteringConfigurationTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/ResourceClassificationNotifierTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesCircumventionTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesCspTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuInvTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesExceptionTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesFilterTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesHeaderFilterTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesHelper.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesRewriteTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesSiteKeyTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesSnippetsTest.java create mode 100644 chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesWebsocketTest.java create mode 100644 chrome/browser/adblock/content_security_policy_injector_factory.cc create mode 100644 chrome/browser/adblock/content_security_policy_injector_factory.h create mode 100644 chrome/browser/adblock/element_hider_factory.cc create mode 100644 chrome/browser/adblock/element_hider_factory.h create mode 100644 chrome/browser/adblock/resource_classification_runner_factory.cc create mode 100644 chrome/browser/adblock/resource_classification_runner_factory.h create mode 100644 chrome/browser/adblock/session_stats_factory.cc create mode 100644 chrome/browser/adblock/session_stats_factory.h create mode 100644 chrome/browser/adblock/sitekey_storage_factory.cc create mode 100644 chrome/browser/adblock/sitekey_storage_factory.h create mode 100644 chrome/browser/adblock/subscription_persistent_metadata_factory.cc create mode 100644 chrome/browser/adblock/subscription_persistent_metadata_factory.h create mode 100644 chrome/browser/adblock/subscription_service_factory.cc create mode 100644 chrome/browser/adblock/subscription_service_factory.h create mode 100644 chrome/browser/adblock/test/adblock_content_browser_client_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_debug_url_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_filter_list_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_filtering_configurations_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_frame_hierarchy_builder_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_multiple_tabs_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_non_ascii_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_popup_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_snippets_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_subscription_service_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_telemetry_service_browsertest.cc create mode 100644 chrome/browser/adblock/test/adblock_web_bundle_browsertest.cc create mode 100644 chrome/browser/resources/adblock_internals/BUILD.gn create mode 100644 chrome/browser/resources/adblock_internals/adblock_internals.html create mode 100644 chrome/browser/resources/adblock_internals/adblock_internals.ts create mode 100644 chrome/browser/ui/webui/adblock_internals/BUILD.gn create mode 100644 chrome/browser/ui/webui/adblock_internals/adblock_internals.mojom create mode 100644 chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.cc create mode 100644 chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.h create mode 100644 chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.cc create mode 100644 chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.h create mode 100644 chrome/test/data/adblock/filterlist_that_allows_resource.txt create mode 100644 chrome/test/data/adblock/filterlist_that_blocks_resource.txt create mode 100644 chrome/test/data/adblock/filterlist_that_hides_resource.txt create mode 100644 chrome/test/data/adblock/innermost_frame.html create mode 100644 chrome/test/data/adblock/middle_frame.html create mode 100644 chrome/test/data/adblock/non-ascii.html create mode 100644 chrome/test/data/adblock/outermost_frame.html create mode 100644 chrome/test/data/adblock/outermost_frame_with_about_blank.html create mode 100644 chrome/test/data/adblock/popup.html create mode 100644 chrome/test/data/adblock/popup_opener.html create mode 100644 chrome/test/data/adblock/popup_parent.html create mode 100644 chrome/test/data/adblock/resource.png create mode 100644 chrome/test/data/adblock/tab-restore.html create mode 100644 chrome/test/data/adblock/wbn/LICENSE create mode 100644 chrome/test/data/adblock/wbn/blocked_bundle/LICENSE create mode 100644 chrome/test/data/adblock/wbn/blocked_bundle/fetch_result_1_blocked_bundle.json create mode 100644 chrome/test/data/adblock/wbn/blocked_bundle/fetch_result_2_blocked_bundle.json create mode 100644 chrome/test/data/adblock/wbn/by_bundle_file/LICENSE create mode 100644 chrome/test/data/adblock/wbn/by_bundle_file/fetch_result_2_subresource_loading.json create mode 100644 chrome/test/data/adblock/wbn/by_bundle_file/green_subresource_loading.css create mode 100644 chrome/test/data/adblock/wbn/by_bundle_file/green_subresource_loading.png create mode 100644 chrome/test/data/adblock/wbn/by_bundle_file/purple_subresource_loading.css create mode 100644 chrome/test/data/adblock/wbn/by_bundle_file/purple_subresource_loading.png create mode 100644 chrome/test/data/adblock/wbn/by_bundle_file/xhr_result_2_subresource_loading.json create mode 100644 chrome/test/data/adblock/wbn/by_resource/LICENSE create mode 100644 chrome/test/data/adblock/wbn/by_resource/blue_subresource_loading.css create mode 100644 chrome/test/data/adblock/wbn/by_resource/blue_subresource_loading.png create mode 100644 chrome/test/data/adblock/wbn/by_resource/fetch_result_1_subresource_loading.json create mode 100644 chrome/test/data/adblock/wbn/by_resource/red_subresource_loading.css create mode 100644 chrome/test/data/adblock/wbn/by_resource/red_subresource_loading.png create mode 100644 chrome/test/data/adblock/wbn/by_resource/xhr_result_1_subresource_loading.json create mode 100644 chrome/test/data/adblock/wbn/by_scope/LICENSE create mode 100644 chrome/test/data/adblock/wbn/by_scope/fetch_result_3_subresource_loading.json create mode 100644 chrome/test/data/adblock/wbn/by_scope/orange_subresource_loading.css create mode 100644 chrome/test/data/adblock/wbn/by_scope/orange_subresource_loading.png create mode 100644 chrome/test/data/adblock/wbn/by_scope/pink_subresource_loading.css create mode 100644 chrome/test/data/adblock/wbn/by_scope/pink_subresource_loading.png create mode 100644 chrome/test/data/adblock/wbn/by_scope/xhr_result_3_subresource_loading.json create mode 100644 chrome/test/data/adblock/wbn/index.html.mustache create mode 100644 chrome/test/data/adblock/xpath3.html diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc --- a/chrome/app/chrome_main_delegate.cc +++ b/chrome/app/chrome_main_delegate.cc @@ -1,6 +1,10 @@ // Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the +// components/adblock/LICENSE file. #include "chrome/app/chrome_main_delegate.h" @@ -41,6 +45,7 @@ #include "base/trace_event/trace_event_impl.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" +#include "chrome/browser/adblock/adblock_content_browser_client.h" #include "chrome/browser/buildflags.h" #include "chrome/browser/chrome_content_browser_client.h" #include "chrome/browser/chrome_resource_bundle_helper.h" @@ -1783,7 +1788,7 @@ content::ContentClient* ChromeMainDelegate::CreateContentClient() { content::ContentBrowserClient* ChromeMainDelegate::CreateContentBrowserClient() { chrome_content_browser_client_ = - std::make_unique(); + std::make_unique(); return chrome_content_browser_client_.get(); } diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -1,6 +1,9 @@ # Copyright 2014 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# +# This source code is a part of eyeo Chromium SDK. +# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file. import("//base/allocator/allocator.gni") import("//build/buildflag_header.gni") @@ -158,6 +161,26 @@ static_library("browser") { "accessibility/page_colors.h", "accessibility/page_colors_factory.cc", "accessibility/page_colors_factory.h", + "adblock/adblock_content_browser_client.cc", + "adblock/adblock_content_browser_client.h", + "adblock/adblock_controller_factory.cc", + "adblock/adblock_controller_factory.h", + "adblock/adblock_telemetry_service_factory.cc", + "adblock/adblock_telemetry_service_factory.h", + "adblock/content_security_policy_injector_factory.cc", + "adblock/content_security_policy_injector_factory.h", + "adblock/element_hider_factory.cc", + "adblock/element_hider_factory.h", + "adblock/resource_classification_runner_factory.cc", + "adblock/resource_classification_runner_factory.h", + "adblock/session_stats_factory.cc", + "adblock/session_stats_factory.h", + "adblock/sitekey_storage_factory.cc", + "adblock/sitekey_storage_factory.h", + "adblock/subscription_persistent_metadata_factory.cc", + "adblock/subscription_persistent_metadata_factory.h", + "adblock/subscription_service_factory.cc", + "adblock/subscription_service_factory.h", "after_startup_task_utils.cc", "after_startup_task_utils.h", "app_mode/app_mode_utils.cc", @@ -2112,6 +2135,7 @@ static_library("browser") { "//chrome/browser/ui/color:color_headers", "//chrome/browser/ui/color:mixers", "//chrome/browser/ui/webui:configs", + "//chrome/browser/ui/webui/adblock_internals:mojo_bindings", "//chrome/browser/ui/webui/app_service_internals:mojo_bindings", "//chrome/browser/ui/webui/feed:mojo_bindings", "//chrome/browser/ui/webui/internals/user_education:mojo_bindings", @@ -2134,6 +2158,8 @@ static_library("browser") { "//chrome/common/notifications", "//chrome/installer/util:with_no_strings", "//chrome/services/speech/buildflags", + "//components/adblock/content:browser", + "//components/adblock/core/converter", "//components/assist_ranker", "//components/autofill/content/browser", "//components/autofill/core/browser", @@ -3411,6 +3437,8 @@ static_library("browser") { "webauthn/android/webauthn_request_delegate_android.cc", "webauthn/android/webauthn_request_delegate_android.h", ] + + public_deps += [ "//chrome/android/features/dev_ui:buildflags", "//components/image_fetcher/core", @@ -3599,6 +3627,7 @@ static_library("browser") { "//url:gurl_android", ] + if (safe_browsing_mode == 2) { sources += [ "component_updater/real_time_url_checks_allowlist_component_installer.cc", diff --git a/chrome/browser/adblock/README.md b/chrome/browser/adblock/README.md new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/README.md @@ -0,0 +1,3 @@ +This folder contains the OS-agnostic, Chrome-specific source code of eyeo Chromium SDK. + +For the full documentation, refer to [components/adblock](/components/adblock). diff --git a/chrome/browser/adblock/adblock_content_browser_client.cc b/chrome/browser/adblock/adblock_content_browser_client.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/adblock_content_browser_client.cc @@ -0,0 +1,320 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/adblock_content_browser_client.h" + +#include "base/containers/unique_ptr_adapters.h" +#include "base/ranges/algorithm.h" +#include "chrome/browser/adblock/content_security_policy_injector_factory.h" +#include "chrome/browser/adblock/element_hider_factory.h" +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/adblock/sitekey_storage_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser_navigator_params.h" +#include "components/adblock/content/browser/adblock_url_loader_factory.h" +#include "components/adblock/content/browser/resource_classification_runner.h" +#include "components/adblock/core/common/adblock_prefs.h" +#include "components/adblock/core/configuration/filtering_configuration.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/embedder_support/user_agent_utils.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "services/network/public/mojom/websocket.mojom.h" +#include "services/service_manager/public/cpp/binder_registry.h" +#include "third_party/blink/public/common/loader/url_loader_throttle.h" + +#ifdef EYEO_INTERCEPT_DEBUG_URL +#include "components/adblock/content/browser/adblock_url_loader_factory_for_test.h" +#endif + +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "chrome/browser/extensions/extension_util.h" +#endif + +namespace { + +bool IsFilteringNeeded(content::RenderFrameHost* frame) { + if (frame) { + auto* profile = + Profile::FromBrowserContext(frame->GetProcess()->GetBrowserContext()); + if (profile) { + // Filtering may be needed if there's at least one enabled + // FilteringConfiguration. + return base::ranges::any_of( + adblock::SubscriptionServiceFactory::GetForBrowserContext(profile) + ->GetInstalledFilteringConfigurations(), + &adblock::FilteringConfiguration::IsEnabled); + } + } + return false; +} + +// Owns all of the AdblockURLLoaderFactory for a given Profile. +class AdblockContextData : public base::SupportsUserData::Data { + public: + AdblockContextData(const AdblockContextData&) = delete; + AdblockContextData& operator=(const AdblockContextData&) = delete; + ~AdblockContextData() override = default; + + static void StartProxying( + Profile* profile, + content::RenderFrameHost* frame, + int render_process_id, + mojo::PendingReceiver receiver, + mojo::PendingRemote target_factory, + bool use_test_loader) { + const void* const kAdblockContextUserDataKey = &kAdblockContextUserDataKey; + auto* self = static_cast( + profile->GetUserData(kAdblockContextUserDataKey)); + if (!self) { + self = new AdblockContextData(); + profile->SetUserData(kAdblockContextUserDataKey, base::WrapUnique(self)); + } + auto* browser_context = + content::WebContents::FromRenderFrameHost(frame)->GetBrowserContext(); + adblock::AdblockURLLoaderFactoryConfig config{ + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context), + adblock::ResourceClassificationRunnerFactory::GetForBrowserContext( + browser_context), + adblock::ElementHiderFactory::GetForBrowserContext(browser_context), + adblock::SitekeyStorageFactory::GetForBrowserContext(browser_context), + adblock::ContentSecurityPolicyInjectorFactory::GetForBrowserContext( + browser_context)}; +#ifdef EYEO_INTERCEPT_DEBUG_URL + if (use_test_loader) { + auto proxy = std::make_unique( + std::move(config), + content::GlobalRenderFrameHostId(render_process_id, + frame->GetRoutingID()), + std::move(receiver), std::move(target_factory), + embedder_support::GetUserAgent(), + base::BindOnce(&AdblockContextData::RemoveProxy, + self->weak_factory_.GetWeakPtr()), + adblock::SubscriptionServiceFactory::GetForBrowserContext( + Profile::FromBrowserContext( + frame->GetProcess()->GetBrowserContext()))); + self->proxies_.emplace(std::move(proxy)); + return; + } +#endif + auto proxy = std::make_unique( + std::move(config), + content::GlobalRenderFrameHostId(render_process_id, + frame->GetRoutingID()), + std::move(receiver), std::move(target_factory), + embedder_support::GetUserAgent(), + base::BindOnce(&AdblockContextData::RemoveProxy, + self->weak_factory_.GetWeakPtr())); + self->proxies_.emplace(std::move(proxy)); + } + + private: + void RemoveProxy(adblock::AdblockURLLoaderFactory* proxy) { + auto it = proxies_.find(proxy); + DCHECK(it != proxies_.end()); + proxies_.erase(it); + } + + AdblockContextData() = default; + + std::set, + base::UniquePtrComparator> + proxies_; + + base::WeakPtrFactory weak_factory_{this}; +}; + +} // namespace + +AdblockContentBrowserClient::AdblockContentBrowserClient() = default; + +AdblockContentBrowserClient::~AdblockContentBrowserClient() = default; + +#if BUILDFLAG(ENABLE_EXTENSIONS) +// static +bool AdblockContentBrowserClient::force_adblock_proxy_for_testing_ = false; + +// static +void AdblockContentBrowserClient::ForceAdblockProxyForTesting() { + force_adblock_proxy_for_testing_ = true; +} +#endif + +bool AdblockContentBrowserClient::WillInterceptWebSocket( + content::RenderFrameHost* frame) { + if (IsFilteringNeeded(frame)) { + return true; + } + + return ChromeContentBrowserClient::WillInterceptWebSocket(frame); +} + +void AdblockContentBrowserClient::CreateWebSocket( + content::RenderFrameHost* frame, + WebSocketFactory factory, + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const absl::optional& user_agent, + mojo::PendingRemote + handshake_client) { + if (IsFilteringNeeded(frame)) { + CreateWebSocketInternal(frame->GetGlobalId(), std::move(factory), url, + site_for_cookies, user_agent, + std::move(handshake_client)); + } else { + DCHECK(ChromeContentBrowserClient::WillInterceptWebSocket(frame)); + ChromeContentBrowserClient::CreateWebSocket(frame, std::move(factory), url, + site_for_cookies, user_agent, + std::move(handshake_client)); + } +} + +void AdblockContentBrowserClient::CreateWebSocketInternal( + content::GlobalRenderFrameHostId render_frame_host_id, + WebSocketFactory factory, + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const absl::optional& user_agent, + mojo::PendingRemote + handshake_client) { + auto* frame = content::RenderFrameHost::FromID(render_frame_host_id); + if (!frame) { + return; + } + auto* browser_context = frame->GetProcess()->GetBrowserContext(); + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context); + auto* classification_runner = + adblock::ResourceClassificationRunnerFactory::GetForBrowserContext( + browser_context); + classification_runner->CheckRequestFilterMatchForWebSocket( + subscription_service->GetCurrentSnapshot(), url, render_frame_host_id, + base::BindOnce( + &AdblockContentBrowserClient::OnWebSocketFilterCheckCompleted, + weak_factory_.GetWeakPtr(), render_frame_host_id, std::move(factory), + url, site_for_cookies, user_agent, std::move(handshake_client))); +} + +void AdblockContentBrowserClient::OnWebSocketFilterCheckCompleted( + content::GlobalRenderFrameHostId render_frame_host_id, + ChromeContentBrowserClient::WebSocketFactory factory, + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const absl::optional& user_agent, + mojo::PendingRemote + handshake_client, + adblock::FilterMatchResult result) { + auto* frame = content::RenderFrameHost::FromID(render_frame_host_id); + if (!frame) { + return; + } + const bool has_blocking_filter = + result == adblock::FilterMatchResult::kBlockRule; + if (!has_blocking_filter) { + VLOG(1) << "[eyeo] Web socket allowed for " << url; + if (ChromeContentBrowserClient::WillInterceptWebSocket(frame)) { + ChromeContentBrowserClient::CreateWebSocket( + frame, std::move(factory), url, site_for_cookies, user_agent, + std::move(handshake_client)); + return; + } + + std::vector headers; + if (user_agent) { + headers.push_back(network::mojom::HttpHeader::New( + net::HttpRequestHeaders::kUserAgent, *user_agent)); + } + std::move(factory).Run(url, std::move(headers), std::move(handshake_client), + mojo::NullRemote(), mojo::NullRemote()); + } + + VLOG(1) << "[eyeo] Web socket blocked for " << url; +} + +bool AdblockContentBrowserClient::WillCreateURLLoaderFactory( + content::BrowserContext* browser_context, + content::RenderFrameHost* frame, + int render_process_id, + URLLoaderFactoryType type, + const url::Origin& request_initiator, + absl::optional navigation_id, + ukm::SourceIdObj ukm_source_id, + mojo::PendingReceiver* factory_receiver, + mojo::PendingRemote* + header_client, + bool* bypass_redirect_checks, + bool* disable_secure_dns, + network::mojom::URLLoaderFactoryOverridePtr* factory_override, + scoped_refptr navigation_response_task_runner) { + // Create Chromium proxy first as WebRequestProxyingURLLoaderFactory logic + // depends on being first proxy + bool use_chrome_proxy = + ChromeContentBrowserClient::WillCreateURLLoaderFactory( + browser_context, frame, render_process_id, type, request_initiator, + navigation_id, ukm_source_id, factory_receiver, header_client, + bypass_redirect_checks, disable_secure_dns, factory_override, + navigation_response_task_runner); + auto* profile = frame ? Profile::FromBrowserContext( + frame->GetProcess()->GetBrowserContext()) + : nullptr; + +#if BUILDFLAG(ENABLE_EXTENSIONS) + if (!force_adblock_proxy_for_testing_ && + request_initiator.scheme() == extensions::kExtensionScheme) { + VLOG(1) << "[eyeo] Do not use adblock proxy for extensions requests " + "[extension id:" + << request_initiator.host() << "]."; + return use_chrome_proxy; + } +#endif + + bool use_adblock_proxy = + (type == URLLoaderFactoryType::kDocumentSubResource || + type == URLLoaderFactoryType::kNavigation) && + IsFilteringNeeded(frame); + + bool use_test_loader = false; +#ifdef EYEO_INTERCEPT_DEBUG_URL + content::WebContents* wc = content::WebContents::FromRenderFrameHost(frame); + use_test_loader = + (type == + content::ContentBrowserClient::URLLoaderFactoryType::kNavigation) && + wc->GetVisibleURL().is_valid() && + wc->GetVisibleURL().host() == + adblock::AdblockURLLoaderFactoryForTest::kAdblockDebugDataHostName; + use_adblock_proxy |= use_test_loader; +#endif + + if (use_adblock_proxy) { + auto proxied_receiver = std::move(*factory_receiver); + mojo::PendingRemote target_factory_remote; + *factory_receiver = target_factory_remote.InitWithNewPipeAndPassReceiver(); + AdblockContextData::StartProxying( + profile, frame, render_process_id, std::move(proxied_receiver), + std::move(target_factory_remote), use_test_loader); + } + return use_adblock_proxy || use_chrome_proxy; +} diff --git a/chrome/browser/adblock/adblock_content_browser_client.h b/chrome/browser/adblock/adblock_content_browser_client.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/adblock_content_browser_client.h @@ -0,0 +1,100 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_ADBLOCK_CONTENT_BROWSER_CLIENT_H_ +#define CHROME_BROWSER_ADBLOCK_ADBLOCK_CONTENT_BROWSER_CLIENT_H_ + +#include "build/buildflag.h" +#include "chrome/browser/chrome_content_browser_client.h" + +namespace adblock { +enum class FilterMatchResult; +} // namespace adblock + +/** + * @brief Intercepts network and UI events to inject ad-filtering. + * Provides ad-filtering implementations of URLLoaderThrottles. + * Binds a mojo connection between Renderer processes and the + * Browser-process-based ResourceClassificationRunner. + * Lives in browser process UI thread. + */ +class AdblockContentBrowserClient : public ChromeContentBrowserClient { + public: + AdblockContentBrowserClient(); + ~AdblockContentBrowserClient() override; + +#if BUILDFLAG(ENABLE_EXTENSIONS) + // Enable ad filtering also for requests initiated by extensions. + // This allows implementing extension-driven browser tests. + // In production code, requests from extensions are not blocked. + static void ForceAdblockProxyForTesting(); +#endif + + bool WillInterceptWebSocket(content::RenderFrameHost* frame) override; + void CreateWebSocket( + content::RenderFrameHost* frame, + WebSocketFactory factory, + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const absl::optional& user_agent, + mojo::PendingRemote + handshake_client) override; + + bool WillCreateURLLoaderFactory( + content::BrowserContext* browser_context, + content::RenderFrameHost* frame, + int render_process_id, + URLLoaderFactoryType type, + const url::Origin& request_initiator, + absl::optional navigation_id, + ukm::SourceIdObj ukm_source_id, + mojo::PendingReceiver* factory_receiver, + mojo::PendingRemote* + header_client, + bool* bypass_redirect_checks, + bool* disable_secure_dns, + network::mojom::URLLoaderFactoryOverridePtr* factory_override, + scoped_refptr navigation_response_task_runner) + override; + + private: + void CreateWebSocketInternal( + content::GlobalRenderFrameHostId render_frame_host_id, + WebSocketFactory factory, + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const absl::optional& user_agent, + mojo::PendingRemote + handshake_client); + void OnWebSocketFilterCheckCompleted( + content::GlobalRenderFrameHostId render_frame_host_id, + ChromeContentBrowserClient::WebSocketFactory factory, + const GURL& url, + const net::SiteForCookies& site_for_cookies, + const absl::optional& user_agent, + mojo::PendingRemote + handshake_client, + adblock::FilterMatchResult result); + + base::WeakPtrFactory weak_factory_{this}; + +#if BUILDFLAG(ENABLE_EXTENSIONS) + static bool force_adblock_proxy_for_testing_; +#endif +}; + +#endif // CHROME_BROWSER_ADBLOCK_ADBLOCK_CONTENT_BROWSER_CLIENT_H_ diff --git a/chrome/browser/adblock/adblock_content_browser_client_unittest.cc b/chrome/browser/adblock/adblock_content_browser_client_unittest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/adblock_content_browser_client_unittest.cc @@ -0,0 +1,203 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/adblock_content_browser_client.h" + +#include "base/callback_list.h" +#include "base/memory/raw_ptr.h" +#include "base/run_loop.h" +#include "base/test/gmock_move_support.h" +#include "base/test/mock_callback.h" +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "components/adblock/content/browser/test/mock_resource_classification_runner.h" +#include "components/adblock/core/common/adblock_prefs.h" +#include "components/adblock/core/common/content_type.h" +#include "components/adblock/core/subscription/installed_subscription.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/adblock/core/subscription/test/mock_subscription_collection.h" +#include "components/adblock/core/subscription/test/mock_subscription_service.h" +#include "components/keyed_service/core/keyed_service.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/test/mock_render_process_host.h" +#include "content/public/test/test_renderer_host.h" +#include "gmock/gmock.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/public/mojom/websocket.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::Return; + +namespace adblock { + +class AdblockContentBrowserClientUnitTest + : public ChromeRenderViewHostTestHarness { + public: + TestingProfile::TestingFactories GetTestingFactories() const override { + return {std::make_pair( + SubscriptionServiceFactory::GetInstance(), + base::BindRepeating([](content::BrowserContext* bc) + -> std::unique_ptr { + return std::make_unique(); + })), + std::make_pair( + ResourceClassificationRunnerFactory::GetInstance(), + base::BindRepeating([](content::BrowserContext* bc) + -> std::unique_ptr { + return std::make_unique(); + }))}; + } + + void SetUp() override { + ChromeRenderViewHostTestHarness::SetUp(); + + subscription_service_ = static_cast( + SubscriptionServiceFactory::GetForBrowserContext(profile())); + resource_classification_runner_ = + static_cast( + ResourceClassificationRunnerFactory::GetForBrowserContext( + profile())); + } + + void TearDown() override { + subscription_service_ = nullptr; + resource_classification_runner_ = nullptr; + ChromeRenderViewHostTestHarness::TearDown(); + } + + raw_ptr subscription_service_; + raw_ptr resource_classification_runner_; +}; + +TEST_F(AdblockContentBrowserClientUnitTest, + WillInterceptWebSocketWhenFilteringEnabled) { + AdblockContentBrowserClient content_client; + subscription_service_->WillRequireFiltering(true); + EXPECT_TRUE(content_client.WillInterceptWebSocket(main_rfh())); +} + +TEST_F(AdblockContentBrowserClientUnitTest, + WillNotInterceptWebSocketWhenFilteringDisabled) { + AdblockContentBrowserClient content_client; + subscription_service_->WillRequireFiltering(false); + EXPECT_FALSE(content_client.WillInterceptWebSocket(main_rfh())); +} + +TEST_F(AdblockContentBrowserClientUnitTest, + RenderFrameHostDiesBeforeClassificationFinished) { + const auto kSocketUrl = GURL("wss://domain.com/test"); + subscription_service_->WillRequireFiltering(true); + EXPECT_CALL(*subscription_service_, GetCurrentSnapshot()).WillOnce([]() { + SubscriptionService::Snapshot snapshot; + snapshot.push_back(std::make_unique()); + return snapshot; + }); + CheckFilterMatchCallback classification_callback; + EXPECT_CALL(*resource_classification_runner_, + CheckRequestFilterMatchForWebSocket(_, kSocketUrl, + main_rfh()->GetGlobalId(), _)) + .WillOnce(MoveArg<3>(&classification_callback)); + + AdblockContentBrowserClient content_client; + base::MockCallback + web_socket_factory; + // The web_socket_factory callback will never be called because the + // associated RenderFrameHost will be dead. + EXPECT_CALL(web_socket_factory, Run(_, _, _, _, _)).Times(0); + + const net::SiteForCookies site_for_cookies; + content_client.CreateWebSocket(main_rfh(), web_socket_factory.Get(), + kSocketUrl, site_for_cookies, absl::nullopt, + {}); + // Tab is closed. + DeleteContents(); + + // Classification finishes now. It will not trigger a call to + // |web_socket_factory| because the RFH is dead. + std::move(classification_callback).Run(FilterMatchResult::kBlockRule); + + task_environment()->RunUntilIdle(); +} + +TEST_F(AdblockContentBrowserClientUnitTest, WebSocketAllowed) { + subscription_service_->WillRequireFiltering(true); + const auto kSocketUrl = GURL("wss://domain.com/test"); + EXPECT_CALL(*subscription_service_, GetCurrentSnapshot()).WillOnce([]() { + SubscriptionService::Snapshot snapshot; + snapshot.push_back(std::make_unique()); + return snapshot; + }); + CheckFilterMatchCallback classification_callback; + EXPECT_CALL(*resource_classification_runner_, + CheckRequestFilterMatchForWebSocket(_, kSocketUrl, + main_rfh()->GetGlobalId(), _)) + .WillOnce(MoveArg<3>(&classification_callback)); + + AdblockContentBrowserClient content_client; + base::MockCallback + web_socket_factory; + // The web_socket_factory callback will be called to let the web socket + // continue connecting. + EXPECT_CALL(web_socket_factory, Run(kSocketUrl, _, _, _, _)); + + const net::SiteForCookies site_for_cookies; + content_client.CreateWebSocket(main_rfh(), web_socket_factory.Get(), + kSocketUrl, site_for_cookies, absl::nullopt, + {}); + + // Classification finishes now. It will trigger a call to |web_socket_factory| + std::move(classification_callback).Run(FilterMatchResult::kAllowRule); + + task_environment()->RunUntilIdle(); +} + +TEST_F(AdblockContentBrowserClientUnitTest, WebSocketBlocked) { + subscription_service_->WillRequireFiltering(true); + const auto kSocketUrl = GURL("wss://domain.com/test"); + EXPECT_CALL(*subscription_service_, GetCurrentSnapshot()).WillOnce([]() { + SubscriptionService::Snapshot snapshot; + snapshot.push_back(std::make_unique()); + return snapshot; + }); + CheckFilterMatchCallback classification_callback; + EXPECT_CALL(*resource_classification_runner_, + CheckRequestFilterMatchForWebSocket(_, kSocketUrl, + main_rfh()->GetGlobalId(), _)) + .WillOnce(MoveArg<3>(&classification_callback)); + + AdblockContentBrowserClient content_client; + base::MockCallback + web_socket_factory; + // The web_socket_factory callback will not be called as to disallow + // connection. + EXPECT_CALL(web_socket_factory, Run(kSocketUrl, _, _, _, _)).Times(0); + + const net::SiteForCookies site_for_cookies; + content_client.CreateWebSocket(main_rfh(), web_socket_factory.Get(), + kSocketUrl, site_for_cookies, absl::nullopt, + {}); + + // Classification finishes now. + std::move(classification_callback).Run(FilterMatchResult::kBlockRule); + + task_environment()->RunUntilIdle(); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/adblock_controller_factory.cc b/chrome/browser/adblock/adblock_controller_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/adblock_controller_factory.cc @@ -0,0 +1,67 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/adblock_controller_factory.h" + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/core/adblock_controller.h" + +namespace adblock { + +// static +AdblockController* AdblockControllerFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +AdblockControllerFactory* AdblockControllerFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +AdblockControllerFactory::AdblockControllerFactory() + : AdblockControllerFactoryBase() { + DependsOn(SubscriptionServiceFactory::GetInstance()); +} + +AdblockControllerFactory::~AdblockControllerFactory() = default; + +SubscriptionService* AdblockControllerFactory::GetSubscriptionService( + content::BrowserContext* context) const { + return SubscriptionServiceFactory::GetForBrowserContext(context); +} + +PrefService* AdblockControllerFactory::GetPrefs( + content::BrowserContext* context) const { + return Profile::FromBrowserContext(context)->GetOriginalProfile()->GetPrefs(); +} + +const std::string& AdblockControllerFactory::GetLocale() const { + return g_browser_process->GetApplicationLocale(); +} + +content::BrowserContext* AdblockControllerFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/adblock_controller_factory.h b/chrome/browser/adblock/adblock_controller_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/adblock_controller_factory.h @@ -0,0 +1,49 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_ADBLOCK_CONTROLLER_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_ADBLOCK_CONTROLLER_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/adblock/content/browser/adblock_controller_factory_base.h" + +namespace adblock { + +class AdblockController; +class AdblockControllerFactory : public AdblockControllerFactoryBase { + public: + static AdblockController* GetForBrowserContext( + content::BrowserContext* context); + static AdblockControllerFactory* GetInstance(); + + protected: + PrefService* GetPrefs(content::BrowserContext* context) const override; + const std::string& GetLocale() const override; + SubscriptionService* GetSubscriptionService( + content::BrowserContext* context) const override; + + private: + friend class base::NoDestructor; + AdblockControllerFactory(); + ~AdblockControllerFactory() override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_ADBLOCK_CONTROLLER_FACTORY_H_ diff --git a/chrome/browser/adblock/adblock_telemetry_service_factory.cc b/chrome/browser/adblock/adblock_telemetry_service_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/adblock_telemetry_service_factory.cc @@ -0,0 +1,63 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/adblock_telemetry_service_factory.h" + +#include "base/no_destructor.h" +#include "chrome/browser/adblock/adblock_controller_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/core/adblock_telemetry_service.h" + +namespace adblock { + +// static +AdblockTelemetryService* AdblockTelemetryServiceFactory::GetForProfile( + Profile* profile) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(profile, true)); +} +// static +AdblockTelemetryServiceFactory* AdblockTelemetryServiceFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +AdblockTelemetryServiceFactory::AdblockTelemetryServiceFactory() + : AdblockTelemetryServiceFactoryBase() { + DependsOn(AdblockControllerFactory::GetInstance()); +} + +AdblockTelemetryServiceFactory::~AdblockTelemetryServiceFactory() = default; + +SubscriptionService* AdblockTelemetryServiceFactory::GetSubscriptionService( + content::BrowserContext* context) const { + return SubscriptionServiceFactory::GetForBrowserContext(context); +} + +PrefService* AdblockTelemetryServiceFactory::GetPrefs( + content::BrowserContext* context) const { + return Profile::FromBrowserContext(context)->GetOriginalProfile()->GetPrefs(); +} + +content::BrowserContext* AdblockTelemetryServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/adblock_telemetry_service_factory.h b/chrome/browser/adblock/adblock_telemetry_service_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/adblock_telemetry_service_factory.h @@ -0,0 +1,51 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_ADBLOCK_TELEMETRY_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_ADBLOCK_TELEMETRY_SERVICE_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/adblock/content/browser/adblock_telemetry_service_factory_base.h" + +class Profile; + +namespace adblock { +class AdblockTelemetryService; +class AdblockTelemetryServiceFactory + : public AdblockTelemetryServiceFactoryBase { + public: + static AdblockTelemetryService* GetForProfile(Profile* profile); + static AdblockTelemetryServiceFactory* GetInstance(); + + protected: + PrefService* GetPrefs(content::BrowserContext* context) const override; + SubscriptionService* GetSubscriptionService( + content::BrowserContext* context) const override; + + private: + friend class base::NoDestructor; + AdblockTelemetryServiceFactory(); + ~AdblockTelemetryServiceFactory() override; + + // BrowserContextKeyedServiceFactory: + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_ADBLOCK_TELEMETRY_SERVICE_FACTORY_H_ diff --git a/chrome/browser/adblock/android/BUILD.gn b/chrome/browser/adblock/android/BUILD.gn new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/BUILD.gn @@ -0,0 +1,68 @@ +# This file is part of eyeo Chromium SDK, +# Copyright (C) 2006-present eyeo GmbH +# eyeo Chromium SDK is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3 as +# published by the Free Software Foundation. +# eyeo Chromium SDK is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# You should have received a copy of the GNU General Public License +# along with eyeo Chromium SDK. If not, see . + + +import("//build/config/android/rules.gni") + +android_library("adblock_java_tests") { + testonly = true + + sources = [ + "javatests/src/org/chromium/chrome/browser/adblock/AdblockControllerTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/FilteringConfigurationTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/ResourceClassificationNotifierTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesCircumventionTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesCspTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuInvTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesExceptionTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesFilterTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesHeaderFilterTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesHelper.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesRewriteTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesSiteKeyTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesSnippetsTest.java", + "javatests/src/org/chromium/chrome/browser/adblock/TestPagesWebsocketTest.java", + ] + + + deps = [ + "//base:base_java", + "//base:base_java_test_support", + "//chrome/android:chrome_java", + "//chrome/browser/flags:java", + "//chrome/browser/settings:test_support_java", + "//chrome/browser/tab:java", + "//chrome/browser/tabmodel:java", + "//chrome/test/android:chrome_java_integration_test_support", + "//chrome/test/android:chrome_java_test_support_common", + "//components/adblock/android:adblock_controller_java", + "//components/adblock/android:adblock_java_tests_base", + "//components/infobars/android:java", + "//components/infobars/core:infobar_enums_java", + "//components/messages/android/test:test_support_java", + "//content/public/android:content_full_java", + "//content/public/android:content_main_dex_java", + "//content/public/test/android:content_java_test_support", + "//net/android:net_java_test_support", + "//third_party/androidx:androidx_fragment_fragment_java", + "//third_party/androidx:androidx_test_monitor_java", + "//third_party/androidx:androidx_test_runner_java", + "//third_party/hamcrest:hamcrest_core_java", + "//third_party/hamcrest:hamcrest_library_java", + "//third_party/junit:junit", + "//ui/android:ui_no_recycler_view_java", + "//url:gurl_java", + ] + +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/AdblockControllerTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/AdblockControllerTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/AdblockControllerTest.java @@ -0,0 +1,43 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.AdblockControllerTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class AdblockControllerTest extends AdblockControllerTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + + @Before + public void setUp() { + mActivityTestRule.startMainActivityOnBlankPage(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/FilteringConfigurationTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/FilteringConfigurationTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/FilteringConfigurationTest.java @@ -0,0 +1,418 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.IntegrationTest; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.FilteringConfiguration; +import org.chromium.components.adblock.TestVerificationUtils; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.content_public.common.ContentSwitches; +import org.chromium.net.test.EmbeddedTestServer; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, + ContentSwitches.HOST_RESOLVER_RULES + "=MAP * 127.0.0.1"}) +public class FilteringConfigurationTest { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final CallbackHelper mCallbackHelper = new CallbackHelper(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + public FilteringConfiguration mConfigurationA; + public FilteringConfiguration mConfigurationB; + private EmbeddedTestServer mTestServer; + private String mTestUrl; + + private static class TestConfigurationChangeObserver + implements FilteringConfiguration.ConfigurationChangeObserver { + public volatile boolean mOnEnabledStateChangedCalled; + public volatile boolean mOnFilterListsChanged; + public volatile boolean mOnAllowedDomainsChanged; + public volatile boolean mOnCustomFiltersChanged; + + public TestConfigurationChangeObserver() { + mOnEnabledStateChangedCalled = false; + mOnFilterListsChanged = false; + mOnAllowedDomainsChanged = false; + mOnCustomFiltersChanged = false; + } + + @Override + public void onEnabledStateChanged() { + mOnEnabledStateChangedCalled = true; + } + @Override + public void onFilterListsChanged() { + mOnFilterListsChanged = true; + } + + @Override + public void onAllowedDomainsChanged() { + mOnAllowedDomainsChanged = true; + } + + @Override + public void onCustomFiltersChanged() { + mOnCustomFiltersChanged = true; + } + } + + public void loadTestUrl() throws InterruptedException { + mActivityTestRule.loadUrl(mTestUrl, 5); + } + + @Before + public void setUp() throws TimeoutException { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfigurationA = FilteringConfiguration.createConfiguration("a"); + mConfigurationB = FilteringConfiguration.createConfiguration("b"); + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + mHelper.setActivityTestRule(mActivityTestRule); + mActivityTestRule.startMainActivityOnBlankPage(); + mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext()); + mTestUrl = mTestServer.getURLWithHostName( + "test.org", "/chrome/test/data/adblock/innermost_frame.html"); + } + + @After + public void tearDown() { + mTestServer.stopAndDestroyServer(); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void addingAllowedDomains() throws Exception { + final List allowedDomainsA = new ArrayList<>(); + final List allowedDomainsB = new ArrayList<>(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfigurationA.addAllowedDomain("foobar.com"); + mConfigurationA.addAllowedDomain("domain.com/path/to/page.html"); + mConfigurationA.addAllowedDomain("domain.com/duplicate.html"); + allowedDomainsA.addAll(mConfigurationA.getAllowedDomains()); + + mConfigurationB.addAllowedDomain("https://scheme.com/path.html"); + mConfigurationB.addAllowedDomain("https://second.com"); + mConfigurationB.removeAllowedDomain("https://second.com"); + mConfigurationB.addAllowedDomain("gibberish"); + allowedDomainsB.addAll(mConfigurationB.getAllowedDomains()); + + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + // We expect to see a sorted collection of domains (not URLs) without duplicates. + ArrayList expectedAllowedDomainsA = new ArrayList(); + expectedAllowedDomainsA.add("domain.com"); + expectedAllowedDomainsA.add("foobar.com"); + Assert.assertEquals(expectedAllowedDomainsA, allowedDomainsA); + + // We expect not to see second.com because it was removed after being added. + ArrayList expectedAllowedDomainsB = new ArrayList(); + expectedAllowedDomainsB.add("scheme.com"); + expectedAllowedDomainsB.add("www.gibberish.com"); + Assert.assertEquals(expectedAllowedDomainsB, allowedDomainsB); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void addingCustomFilters() throws Exception { + final List customFiltersA = new ArrayList<>(); + final List customFiltersB = new ArrayList<>(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfigurationA.addCustomFilter("foobar.com"); + mConfigurationA.addCustomFilter("foobar.com"); + mConfigurationA.addCustomFilter("abc"); + customFiltersA.addAll(mConfigurationA.getCustomFilters()); + + mConfigurationB.addCustomFilter("https://scheme.com/path.html"); + mConfigurationB.addCustomFilter("https://second.com"); + mConfigurationB.removeCustomFilter("https://second.com"); + customFiltersB.addAll(mConfigurationB.getCustomFilters()); + + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + // We expect to see a collection of custom filters without duplicates. + // The order represents order of addition. + ArrayList expectedCustomFiltersA = new ArrayList(); + expectedCustomFiltersA.add("foobar.com"); + expectedCustomFiltersA.add("abc"); + Assert.assertEquals(expectedCustomFiltersA, customFiltersA); + + // We expect not to see https://second.com because it was removed after being added. + ArrayList expectedCustomFiltersB = new ArrayList(); + expectedCustomFiltersB.add("https://scheme.com/path.html"); + Assert.assertEquals(expectedCustomFiltersB, customFiltersB); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void addingFilterLists() throws Exception { + final URL filterList1 = new URL("http://filters.com/list1.txt"); + final URL filterList2 = new URL("http://filters.com/list2.txt"); + final List filterListsA = new ArrayList(); + final List filterListsB = new ArrayList(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfigurationA.addFilterList(filterList1); + mConfigurationA.addFilterList(filterList2); + mConfigurationA.addFilterList(filterList1); + filterListsA.addAll(mConfigurationA.getFilterLists()); + + mConfigurationB.addFilterList(filterList1); + mConfigurationB.addFilterList(filterList2); + mConfigurationB.removeFilterList(filterList1); + filterListsB.addAll(mConfigurationB.getFilterLists()); + + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + ArrayList expectedFilterListsA = new ArrayList(); + expectedFilterListsA.add(filterList1); + expectedFilterListsA.add(filterList2); + Assert.assertEquals(expectedFilterListsA, filterListsA); + + ArrayList expectedFilterListsB = new ArrayList(); + expectedFilterListsB.add(filterList2); + Assert.assertEquals(expectedFilterListsB, filterListsB); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void aliasedConfigurations() throws Exception { + final URL filterList1 = new URL("http://filters.com/list1.txt"); + final URL filterList2 = new URL("http://filters.com/list2.txt"); + final List filterListsA = new ArrayList(); + final List filterListsB = new ArrayList(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + // Create a new FilteringConfiguration with a name of one that + // already exist. + FilteringConfiguration aliasedConfiguration = + FilteringConfiguration.createConfiguration("a"); + + // We add filter lists only to the original configuration instance. + mConfigurationA.addFilterList(filterList1); + mConfigurationA.addFilterList(filterList2); + + // We check what filter lists are present in the original and in the aliased instance. + filterListsA.addAll(mConfigurationA.getFilterLists()); + filterListsB.addAll(aliasedConfiguration.getFilterLists()); + + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + ArrayList expectedFilterLists = new ArrayList(); + expectedFilterLists.add(filterList1); + expectedFilterLists.add(filterList2); + Assert.assertEquals(expectedFilterLists, filterListsA); + Assert.assertEquals(expectedFilterLists, filterListsB); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void configurationChangeObserverNotified() throws Exception { + final URL filterList1 = new URL("http://filters.com/list1.txt"); + final TestConfigurationChangeObserver observer = new TestConfigurationChangeObserver(); + Assert.assertFalse(observer.mOnEnabledStateChangedCalled); + Assert.assertFalse(observer.mOnAllowedDomainsChanged); + Assert.assertFalse(observer.mOnCustomFiltersChanged); + Assert.assertFalse(observer.mOnFilterListsChanged); + + TestThreadUtils.runOnUiThreadBlocking(() -> { + // We'll create an aliased instance which will receive notifications triggered by + // changes made to the original instance. + FilteringConfiguration aliasedConfiguration = + FilteringConfiguration.createConfiguration("a"); + aliasedConfiguration.addObserver(observer); + + mConfigurationA.addFilterList(filterList1); + mConfigurationA.addAllowedDomain("test.com"); + mConfigurationA.addCustomFilter("test.com"); + mConfigurationA.setEnabled(false); + + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + Assert.assertTrue(observer.mOnEnabledStateChangedCalled); + Assert.assertTrue(observer.mOnAllowedDomainsChanged); + Assert.assertTrue(observer.mOnCustomFiltersChanged); + Assert.assertTrue(observer.mOnFilterListsChanged); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void resourceBlockedByFilter() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfigurationA.addCustomFilter("resource.png"); + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + loadTestUrl(); + TestVerificationUtils.expectResourceBlocked(mHelper, "subresource"); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void resourceAllowedByFilter() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfigurationA.addCustomFilter("resource.png"); + // Allowing filter for the mocked test.org domain that mTestServer hosts. + mConfigurationA.addCustomFilter("@@test.org$document"); + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + loadTestUrl(); + TestVerificationUtils.expectResourceShown(mHelper, "subresource"); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void resourceAllowedByAllowedDomain() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfigurationA.addCustomFilter("resource.png"); + mConfigurationA.addAllowedDomain("test.org"); + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + loadTestUrl(); + TestVerificationUtils.expectResourceShown(mHelper, "subresource"); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void resourceBlockedBySecondConfiguration() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + // ConfigurationA allows the resource. + // Allowing rules take precedence within a FilteringConfiguration. + mConfigurationA.addCustomFilter("resource.png"); + mConfigurationA.addAllowedDomain("test.org"); + // But ConfigurationB blocks the resource. + // Blocking takes precedence across FilteringConfigurations. + mConfigurationB.addCustomFilter("resource.png"); + mCallbackHelper.notifyCalled(); + }); + mCallbackHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + loadTestUrl(); + TestVerificationUtils.expectResourceBlocked(mHelper, "subresource"); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void noBlockingWithoutFilters() throws Exception { + loadTestUrl(); + TestVerificationUtils.expectResourceShown(mHelper, "subresource"); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void createAndRemoveConfiguration() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + // Check initial state. + FilteringConfiguration.removeConfiguration("adblock"); + ArrayList expectedConfigurations = + new ArrayList(); + expectedConfigurations.add(mConfigurationA); + expectedConfigurations.add(mConfigurationB); + Assert.assertEquals(expectedConfigurations, FilteringConfiguration.getConfigurations()); + // Add new configuration (twice) and check. + FilteringConfiguration configurationC = FilteringConfiguration.createConfiguration("c"); + FilteringConfiguration configurationC2 = + FilteringConfiguration.createConfiguration("c"); + // Confirm this is the same reference. + Assert.assertEquals(configurationC, configurationC2); + expectedConfigurations.add(configurationC); + Assert.assertEquals(expectedConfigurations, FilteringConfiguration.getConfigurations()); + // Remove configuration "c" twice (2nd attempt has no effect) and check. + FilteringConfiguration.removeConfiguration("c"); + FilteringConfiguration.removeConfiguration("c"); + expectedConfigurations.remove(configurationC); + Assert.assertEquals(expectedConfigurations, FilteringConfiguration.getConfigurations()); + }); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void cannotUseRemovedConfiguration() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + FilteringConfiguration configurationC = FilteringConfiguration.createConfiguration("c"); + configurationC.setEnabled(false); + Assert.assertFalse(configurationC.isEnabled()); + FilteringConfiguration.removeConfiguration("c"); + try { + configurationC.setEnabled(true); + Assert.fail(); + } catch (final IllegalStateException e) { + // Expected + } + // Recreate. + configurationC = FilteringConfiguration.createConfiguration("c"); + Assert.assertTrue(configurationC.isEnabled()); + configurationC.setEnabled(false); + Assert.assertFalse(configurationC.isEnabled()); + }); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/ResourceClassificationNotifierTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/ResourceClassificationNotifierTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/ResourceClassificationNotifierTest.java @@ -0,0 +1,135 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.LargeTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.IntegrationTest; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.FilteringConfiguration; +import org.chromium.components.adblock.ResourceClassificationNotifier; +import org.chromium.components.adblock.TestAdBlockedObserver; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.content_public.common.ContentSwitches; +import org.chromium.net.test.EmbeddedTestServer; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, + ContentSwitches.HOST_RESOLVER_RULES + "=MAP * 127.0.0.1"}) +public class ResourceClassificationNotifierTest { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final CallbackHelper mHelper = new CallbackHelper(); + public FilteringConfiguration mConfiguration; + public TestAdBlockedObserver mAdBlockedObserver = new TestAdBlockedObserver(); + + private EmbeddedTestServer mTestServer; + private String mTestUrl; + + public void loadTestUrl() { + mActivityTestRule.loadUrl(mTestUrl, 5); + } + + @Before + public void setUp() throws TimeoutException { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfiguration = FilteringConfiguration.createConfiguration("a"); + ResourceClassificationNotifier.getInstance().addOnAdBlockedObserver(mAdBlockedObserver); + mHelper.notifyCalled(); + }); + mHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + mActivityTestRule.startMainActivityOnBlankPage(); + mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext()); + mTestUrl = mTestServer.getURLWithHostName( + "test.org", "/chrome/test/data/adblock/innermost_frame.html"); + } + + @After + public void tearDown() { + mTestServer.stopAndDestroyServer(); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void noNotificationWithoutBlocking() throws Exception { + loadTestUrl(); + + Assert.assertTrue(mAdBlockedObserver.blockedInfos.isEmpty()); + Assert.assertTrue(mAdBlockedObserver.allowedInfos.isEmpty()); + Assert.assertTrue(mAdBlockedObserver.allowedPageInfos.isEmpty()); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void resourceBlockedByFilter() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfiguration.addCustomFilter("resource.png"); + mHelper.notifyCalled(); + }); + mHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + loadTestUrl(); + // Observer was notified about the blocking + Assert.assertTrue(mAdBlockedObserver.isBlocked("resource.png")); + Assert.assertTrue(mAdBlockedObserver.allowedInfos.isEmpty()); + Assert.assertTrue(mAdBlockedObserver.allowedPageInfos.isEmpty()); + } + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void pageAllowed() throws Exception { + TestThreadUtils.runOnUiThreadBlocking(() -> { + mConfiguration.addCustomFilter("resource.png"); + mConfiguration.addAllowedDomain("test.org"); + mHelper.notifyCalled(); + }); + mHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + loadTestUrl(); + // Observer was notified about the allowed resource + Assert.assertTrue(mAdBlockedObserver.blockedInfos.isEmpty()); + Assert.assertTrue(mAdBlockedObserver.isAllowed("resource.png")); + + // TODO(mpawlowski): The observer could also be notified about the entire domain being + // allowed: Assert.assertTrue(mAdBlockedObserver.isPageAllowed("test.org")); However this is + // currently broken with multiple FilteringConfigurations (DPD-1729). + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesCircumventionTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesCircumventionTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesCircumventionTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesCircumventionTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesCircumventionTest extends TestPagesCircumventionTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesCspTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesCspTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesCspTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesCspTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesCspTest extends TestPagesCspTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuInvTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuInvTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuInvTest.java @@ -0,0 +1,48 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesElemhideEmuInvTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesElemhideEmuInvTest extends TestPagesElemhideEmuInvTestBase { + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideEmuTest.java @@ -0,0 +1,48 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesElemhideEmuTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesElemhideEmuTest extends TestPagesElemhideEmuTestBase { + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesElemhideTest.java @@ -0,0 +1,48 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesElemhideTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesElemhideTest extends TestPagesElemhideTestBase { + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesExceptionTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesExceptionTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesExceptionTest.java @@ -0,0 +1,100 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.task.PostTask; +import org.chromium.base.task.TaskTraits; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Feature; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesExceptionTestBase; +import org.chromium.content_public.browser.test.util.JavaScriptUtils; +import org.chromium.content_public.common.ContentSwitches; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesExceptionTest extends TestPagesExceptionTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @Test + @LargeTest + @Feature({"adblock"}) + @CommandLineFlags.Add(ContentSwitches.DISABLE_POPUP_BLOCKING) + public void testVerifyPopupException() throws Exception { + final String POPUP_TESTACE_URL = + TestPagesHelper.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "popup"; + mHelper.loadUrl(POPUP_TESTACE_URL); + Assert.assertEquals(1, mHelper.getTabCount()); + final CallbackHelper tabsLoadedWaiter = mHelper.getTabsOpenedAndLoadedWaiter(); + PostTask.postTask(TaskTraits.BEST_EFFORT_MAY_BLOCK, () -> { + try { + String numElements = JavaScriptUtils.executeJavaScriptAndWaitForResult( + mHelper.getWebContents(), + "var elements = document.getElementsByClassName(\"testcase-trigger\");" + + "for (let i = 0; i < elements.length; ++i) {" + + " elements[i].click();" + + "}" + + "elements.length;"); + Assert.assertEquals("3", numElements); + } catch (TimeoutException e) { + Assert.assertEquals("Popups were triggered", "Popups were NOT triggered"); + } + }); + // Wait for three tab loaded events + tabsLoadedWaiter.waitForCallback(0, 3, TestPagesHelper.TEST_TIMEOUT_SEC, TimeUnit.SECONDS); + Assert.assertEquals(4, mHelper.getTabCount()); + Assert.assertEquals(3, mHelper.numAllowedPopups()); + Assert.assertTrue(mHelper.isPopupAllowed( + TestPagesHelper.TESTPAGES_RESOURCES_ROOT + "popup_exception/link.html")); + Assert.assertTrue(mHelper.isPopupAllowed( + TestPagesHelper.TESTPAGES_RESOURCES_ROOT + "popup_exception/script-window.html")); + Assert.assertTrue(mHelper.isPopupAllowed( + TestPagesHelper.TESTPAGES_RESOURCES_ROOT + "popup_exception/script-tab.html")); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesFilterTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesFilterTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesFilterTest.java @@ -0,0 +1,101 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.task.PostTask; +import org.chromium.base.task.TaskTraits; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.base.test.util.Feature; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesFilterTestBase; +import org.chromium.content_public.browser.test.util.JavaScriptUtils; +import org.chromium.content_public.common.ContentSwitches; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesFilterTest extends TestPagesFilterTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @Test + @LargeTest + @Feature({"adblock"}) + @CommandLineFlags.Add(ContentSwitches.DISABLE_POPUP_BLOCKING) + public void testVerifyPopupFilters() throws Exception { + final String POPUP_TESTACE_URL = TestPagesHelper.FILTER_TESTPAGES_TESTCASES_ROOT + "popup"; + mHelper.loadUrl(POPUP_TESTACE_URL); + Assert.assertEquals(1, mHelper.getTabCount()); + final CallbackHelper tabsOpenedAndClosedWaiter = mHelper.getTabsOpenedAndClosedWaiter(); + // Trigger popups which open and close when blocked + PostTask.postTask(TaskTraits.BEST_EFFORT_MAY_BLOCK, () -> { + try { + String numElements = JavaScriptUtils.executeJavaScriptAndWaitForResult( + mHelper.getWebContents(), + "var elements = document.getElementsByClassName(\"testcase-trigger\");" + + "for (let i = 0; i < elements.length; ++i) {" + + " elements[i].click();" + + "}" + + "elements.length;"); + Assert.assertEquals("3", numElements); + } catch (TimeoutException e) { + Assert.assertEquals("Popups were triggered", "Popups were NOT triggered"); + } + }); + // Wait for three tab open events and three close tabs events + tabsOpenedAndClosedWaiter.waitForCallback( + 0, 6, TestPagesHelper.TEST_TIMEOUT_SEC, TimeUnit.SECONDS); + Assert.assertEquals(3, mHelper.numBlockedPopups()); + Assert.assertTrue(mHelper.isPopupBlocked( + TestPagesHelper.TESTPAGES_RESOURCES_ROOT + "popup/link.html")); + Assert.assertTrue(mHelper.isPopupBlocked( + TestPagesHelper.TESTPAGES_RESOURCES_ROOT + "popup/script-window.html")); + Assert.assertTrue(mHelper.isPopupBlocked( + TestPagesHelper.TESTPAGES_RESOURCES_ROOT + "popup/script-tab.html")); + Assert.assertEquals(1, mHelper.getTabCount()); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesHeaderFilterTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesHeaderFilterTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesHeaderFilterTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesHeaderFilterTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesHeaderFilterTest extends TestPagesHeaderFilterTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesHelper.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesHelper.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesHelper.java @@ -0,0 +1,121 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.Assert; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.chrome.browser.tab.EmptyTabObserver; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.chrome.browser.tabmodel.TabModelObserver; +import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesHelperBase; +import org.chromium.components.adblock.TestVerificationUtils; +import org.chromium.content_public.browser.WebContents; +import org.chromium.content_public.browser.test.util.TestThreadUtils; +import org.chromium.url.GURL; + +import java.util.concurrent.TimeoutException; + +public class TestPagesHelper extends TestPagesHelperBase { + private ChromeTabbedActivityTestRule mActivityTestRule; + + public void setActivityTestRule(ChromeTabbedActivityTestRule activityTestRule) { + mActivityTestRule = activityTestRule; + } + + public void setUp(final ChromeTabbedActivityTestRule activityRule) { + mActivityTestRule = activityRule; + mActivityTestRule.startMainActivityOnBlankPage(); + super.setUp(); + } + + public CallbackHelper getTabsOpenedAndClosedWaiter() { + final CallbackHelper callbackHelper = new CallbackHelper(); + final TabModelSelector tabModelSelector = + mActivityTestRule.getActivity().getTabModelSelectorSupplier().get(); + Assert.assertNotNull(tabModelSelector); + TestThreadUtils.runOnUiThreadBlocking( + () -> tabModelSelector.getCurrentModel().addObserver(new TabModelObserver() { + @Override + public void onFinishingTabClosure(Tab tab) { + // For some reason TabModelObserver#tabRemoved() is not called. + // Let's wait a bit to make sure tab is indeed closed. + try { + Thread.sleep(100); + } catch (InterruptedException ignored) { + } + callbackHelper.notifyCalled(); + } + + @Override + public void didAddTab( + Tab tab, int type, int creationState, boolean markedForSelection) { + callbackHelper.notifyCalled(); + } + })); + return callbackHelper; + } + + public CallbackHelper getTabsOpenedAndLoadedWaiter() { + final CallbackHelper callbackHelper = new CallbackHelper(); + final TabModelSelector tabModelSelector = + mActivityTestRule.getActivity().getTabModelSelectorSupplier().get(); + Assert.assertNotNull(tabModelSelector); + TestThreadUtils.runOnUiThreadBlocking( + () -> tabModelSelector.getCurrentModel().addObserver(new TabModelObserver() { + @Override + public void didAddTab( + Tab tab, int type, int creationState, boolean markedForSelection) { + tab.addObserver(new EmptyTabObserver() { + @Override + public void onPageLoadFinished(Tab tab, GURL url) { + callbackHelper.notifyCalled(); + } + }); + } + })); + return callbackHelper; + } + + public int getTabCount() { + final TabModelSelector tabModelSelector = + mActivityTestRule.getActivity().getTabModelSelectorSupplier().get(); + Assert.assertNotNull(tabModelSelector); + return tabModelSelector.getTotalTabCount(); + } + + @Override + public WebContents getWebContents() { + return mActivityTestRule.getActivity().getCurrentWebContents(); + } + + @Override + public void loadUrl(final String url) throws InterruptedException, TimeoutException { + mActivityTestRule.loadUrl(url, TEST_TIMEOUT_SEC); + } + + @Override + public void loadUrlWaitForContent(final String url) + throws InterruptedException, TimeoutException { + loadUrl(url); + TestVerificationUtils.verifyCondition( + this, "document.getElementsByClassName('testcase-waiting-content').length == 0"); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesRewriteTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesRewriteTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesRewriteTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesRewriteTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesRewriteTest extends TestPagesRewriteTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesSiteKeyTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesSiteKeyTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesSiteKeyTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesSiteKeyTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesSiteKeyTest extends TestPagesSiteKeyTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesSnippetsTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesSnippetsTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesSnippetsTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesSnippetsTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesSnippetsTest extends TestPagesSnippetsTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesWebsocketTest.java b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesWebsocketTest.java new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/android/javatests/src/org/chromium/chrome/browser/adblock/TestPagesWebsocketTest.java @@ -0,0 +1,51 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +package org.chromium.chrome.browser.adblock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.runner.RunWith; + +import org.chromium.base.test.util.CommandLineFlags; +import org.chromium.chrome.browser.flags.ChromeSwitches; +import org.chromium.chrome.test.ChromeBrowserTestRule; +import org.chromium.chrome.test.ChromeJUnit4ClassRunner; +import org.chromium.chrome.test.ChromeTabbedActivityTestRule; +import org.chromium.components.adblock.TestPagesWebsocketTestBase; + +@RunWith(ChromeJUnit4ClassRunner.class) +@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) +public class TestPagesWebsocketTest extends TestPagesWebsocketTestBase { + @Rule + public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule(); + @Rule + public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule(); + private final TestPagesHelper mHelper = new TestPagesHelper(); + + @Before + public void setUp() { + mHelper.setUp(mActivityTestRule); + super.setUp(mHelper); + } + + @After + public void tearDown() { + mHelper.tearDown(); + } +} diff --git a/chrome/browser/adblock/content_security_policy_injector_factory.cc b/chrome/browser/adblock/content_security_policy_injector_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/content_security_policy_injector_factory.cc @@ -0,0 +1,69 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/content_security_policy_injector_factory.h" + +#include + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/content/browser/content_security_policy_injector_impl.h" +#include "components/adblock/content/browser/frame_hierarchy_builder.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +// static +ContentSecurityPolicyInjector* +ContentSecurityPolicyInjectorFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +ContentSecurityPolicyInjectorFactory* +ContentSecurityPolicyInjectorFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +ContentSecurityPolicyInjectorFactory::ContentSecurityPolicyInjectorFactory() + : BrowserContextKeyedServiceFactory( + "ContentSecurityPolicyInjector", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(SubscriptionServiceFactory::GetInstance()); +} + +ContentSecurityPolicyInjectorFactory::~ContentSecurityPolicyInjectorFactory() = + default; + +KeyedService* ContentSecurityPolicyInjectorFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new ContentSecurityPolicyInjectorImpl( + SubscriptionServiceFactory::GetForBrowserContext(context), + std::make_unique()); +} + +content::BrowserContext* +ContentSecurityPolicyInjectorFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/content_security_policy_injector_factory.h b/chrome/browser/adblock/content_security_policy_injector_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/content_security_policy_injector_factory.h @@ -0,0 +1,49 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_CONTENT_SECURITY_POLICY_INJECTOR_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_CONTENT_SECURITY_POLICY_INJECTOR_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class ContentSecurityPolicyInjector; +class ContentSecurityPolicyInjectorFactory + : public BrowserContextKeyedServiceFactory { + public: + static ContentSecurityPolicyInjector* GetForBrowserContext( + content::BrowserContext* context); + static ContentSecurityPolicyInjectorFactory* GetInstance(); + + private: + friend class base::NoDestructor; + ContentSecurityPolicyInjectorFactory(); + ~ContentSecurityPolicyInjectorFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_CONTENT_SECURITY_POLICY_INJECTOR_FACTORY_H_ diff --git a/chrome/browser/adblock/element_hider_factory.cc b/chrome/browser/adblock/element_hider_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/element_hider_factory.cc @@ -0,0 +1,63 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/element_hider_factory.h" + +#include + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/content/browser/element_hider_impl.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +// static +ElementHider* ElementHiderFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +ElementHiderFactory* ElementHiderFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +ElementHiderFactory::ElementHiderFactory() + : BrowserContextKeyedServiceFactory( + "ElementHider", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(adblock::SubscriptionServiceFactory::GetInstance()); +} + +ElementHiderFactory::~ElementHiderFactory() = default; + +KeyedService* ElementHiderFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new ElementHiderImpl( + adblock::SubscriptionServiceFactory::GetForBrowserContext(context)); +} + +content::BrowserContext* ElementHiderFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/element_hider_factory.h b/chrome/browser/adblock/element_hider_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/element_hider_factory.h @@ -0,0 +1,47 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_ELEMENT_HIDER_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_ELEMENT_HIDER_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class ElementHider; +class ElementHiderFactory : public BrowserContextKeyedServiceFactory { + public: + static ElementHider* GetForBrowserContext(content::BrowserContext* context); + static ElementHiderFactory* GetInstance(); + + private: + friend class base::NoDestructor; + ElementHiderFactory(); + ~ElementHiderFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_ELEMENT_HIDER_FACTORY_H_ diff --git a/chrome/browser/adblock/resource_classification_runner_factory.cc b/chrome/browser/adblock/resource_classification_runner_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/resource_classification_runner_factory.cc @@ -0,0 +1,71 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/resource_classification_runner_factory.h" + +#include + +#include "chrome/browser/adblock/sitekey_storage_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/content/browser/frame_hierarchy_builder.h" +#include "components/adblock/content/browser/resource_classification_runner_impl.h" +#include "components/adblock/core/classifier/resource_classifier_impl.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +// static +ResourceClassificationRunner* +ResourceClassificationRunnerFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +ResourceClassificationRunnerFactory* +ResourceClassificationRunnerFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +ResourceClassificationRunnerFactory::ResourceClassificationRunnerFactory() + : BrowserContextKeyedServiceFactory( + "ResourceClassificationRunner", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(SitekeyStorageFactory::GetInstance()); +} + +ResourceClassificationRunnerFactory::~ResourceClassificationRunnerFactory() = + default; + +KeyedService* ResourceClassificationRunnerFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new ResourceClassificationRunnerImpl( + base::MakeRefCounted(), + std::make_unique(), + SitekeyStorageFactory::GetForBrowserContext(context)); +} + +content::BrowserContext* +ResourceClassificationRunnerFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/resource_classification_runner_factory.h b/chrome/browser/adblock/resource_classification_runner_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/resource_classification_runner_factory.h @@ -0,0 +1,49 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_RESOURCE_CLASSIFICATION_RUNNER_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_RESOURCE_CLASSIFICATION_RUNNER_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class ResourceClassificationRunner; +class ResourceClassificationRunnerFactory + : public BrowserContextKeyedServiceFactory { + public: + static ResourceClassificationRunner* GetForBrowserContext( + content::BrowserContext* context); + static ResourceClassificationRunnerFactory* GetInstance(); + + private: + friend class base::NoDestructor; + ResourceClassificationRunnerFactory(); + ~ResourceClassificationRunnerFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_RESOURCE_CLASSIFICATION_RUNNER_FACTORY_H_ diff --git a/chrome/browser/adblock/session_stats_factory.cc b/chrome/browser/adblock/session_stats_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/session_stats_factory.cc @@ -0,0 +1,64 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/session_stats_factory.h" + +#include + +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/content/browser/session_stats_impl.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +// static +SessionStats* SessionStatsFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +SessionStatsFactory* SessionStatsFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +SessionStatsFactory::SessionStatsFactory() + : BrowserContextKeyedServiceFactory( + "SessionStats", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ResourceClassificationRunnerFactory::GetInstance()); +} + +SessionStatsFactory::~SessionStatsFactory() = default; + +KeyedService* SessionStatsFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new SessionStatsImpl( + ResourceClassificationRunnerFactory::GetForBrowserContext(context)); +} + +content::BrowserContext* SessionStatsFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/session_stats_factory.h b/chrome/browser/adblock/session_stats_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/session_stats_factory.h @@ -0,0 +1,47 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_SESSION_STATS_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_SESSION_STATS_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class SessionStats; +class SessionStatsFactory : public BrowserContextKeyedServiceFactory { + public: + static SessionStats* GetForBrowserContext(content::BrowserContext* context); + static SessionStatsFactory* GetInstance(); + + private: + friend class base::NoDestructor; + SessionStatsFactory(); + ~SessionStatsFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_SESSION_STATS_FACTORY_H_ diff --git a/chrome/browser/adblock/sitekey_storage_factory.cc b/chrome/browser/adblock/sitekey_storage_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/sitekey_storage_factory.cc @@ -0,0 +1,59 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/sitekey_storage_factory.h" + +#include + +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/core/sitekey_storage_impl.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +// static +SitekeyStorage* SitekeyStorageFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +SitekeyStorageFactory* SitekeyStorageFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +SitekeyStorageFactory::SitekeyStorageFactory() + : BrowserContextKeyedServiceFactory( + "SitekeyStorage", + BrowserContextDependencyManager::GetInstance()) {} + +SitekeyStorageFactory::~SitekeyStorageFactory() = default; + +KeyedService* SitekeyStorageFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new SitekeyStorageImpl(); +} + +content::BrowserContext* SitekeyStorageFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/sitekey_storage_factory.h b/chrome/browser/adblock/sitekey_storage_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/sitekey_storage_factory.h @@ -0,0 +1,47 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_SITEKEY_STORAGE_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_SITEKEY_STORAGE_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class SitekeyStorage; +class SitekeyStorageFactory : public BrowserContextKeyedServiceFactory { + public: + static SitekeyStorage* GetForBrowserContext(content::BrowserContext* context); + static SitekeyStorageFactory* GetInstance(); + + private: + friend class base::NoDestructor; + SitekeyStorageFactory(); + ~SitekeyStorageFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_SITEKEY_STORAGE_FACTORY_H_ diff --git a/chrome/browser/adblock/subscription_persistent_metadata_factory.cc b/chrome/browser/adblock/subscription_persistent_metadata_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/subscription_persistent_metadata_factory.cc @@ -0,0 +1,63 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/subscription_persistent_metadata_factory.h" + +#include + +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/core/subscription/subscription_persistent_metadata_impl.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +// static +SubscriptionPersistentMetadata* +SubscriptionPersistentMetadataFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +SubscriptionPersistentMetadataFactory* +SubscriptionPersistentMetadataFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +SubscriptionPersistentMetadataFactory::SubscriptionPersistentMetadataFactory() + : BrowserContextKeyedServiceFactory( + "AdblockSubscriptionPersistentMetadata", + BrowserContextDependencyManager::GetInstance()) {} +SubscriptionPersistentMetadataFactory:: + ~SubscriptionPersistentMetadataFactory() = default; + +KeyedService* SubscriptionPersistentMetadataFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new SubscriptionPersistentMetadataImpl( + Profile::FromBrowserContext(context)->GetOriginalProfile()->GetPrefs()); +} + +content::BrowserContext* +SubscriptionPersistentMetadataFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/subscription_persistent_metadata_factory.h b/chrome/browser/adblock/subscription_persistent_metadata_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/subscription_persistent_metadata_factory.h @@ -0,0 +1,49 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_SUBSCRIPTION_PERSISTENT_METADATA_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_SUBSCRIPTION_PERSISTENT_METADATA_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class SubscriptionPersistentMetadata; +class SubscriptionPersistentMetadataFactory + : public BrowserContextKeyedServiceFactory { + public: + static SubscriptionPersistentMetadata* GetForBrowserContext( + content::BrowserContext* context); + static SubscriptionPersistentMetadataFactory* GetInstance(); + + private: + friend class base::NoDestructor; + SubscriptionPersistentMetadataFactory(); + ~SubscriptionPersistentMetadataFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_SUBSCRIPTION_PERSISTENT_METADATA_FACTORY_H_ diff --git a/chrome/browser/adblock/subscription_service_factory.cc b/chrome/browser/adblock/subscription_service_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/subscription_service_factory.cc @@ -0,0 +1,66 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/subscription_service_factory.h" + +#include + +#include "chrome/browser/adblock/subscription_persistent_metadata_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +// static +SubscriptionService* SubscriptionServiceFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +SubscriptionServiceFactory* SubscriptionServiceFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +SubscriptionServiceFactory::SubscriptionServiceFactory() + : SubscriptionServiceFactoryBase() { + DependsOn(SubscriptionPersistentMetadataFactory::GetInstance()); +} + +SubscriptionServiceFactory::~SubscriptionServiceFactory() = default; + +SubscriptionPersistentMetadata* +SubscriptionServiceFactory::GetSubscriptionPersistentMetadata( + content::BrowserContext* context) const { + return SubscriptionPersistentMetadataFactory::GetForBrowserContext(context); +} + +PrefService* SubscriptionServiceFactory::GetPrefs( + content::BrowserContext* context) const { + return Profile::FromBrowserContext(context)->GetOriginalProfile()->GetPrefs(); +} + +content::BrowserContext* SubscriptionServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/subscription_service_factory.h b/chrome/browser/adblock/subscription_service_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/subscription_service_factory.h @@ -0,0 +1,50 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#ifndef CHROME_BROWSER_ADBLOCK_SUBSCRIPTION_SERVICE_FACTORY_H_ +#define CHROME_BROWSER_ADBLOCK_SUBSCRIPTION_SERVICE_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/adblock/content/browser/subscription_service_factory_base.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class SubscriptionService; +class SubscriptionServiceFactory : public SubscriptionServiceFactoryBase { + public: + static SubscriptionService* GetForBrowserContext( + content::BrowserContext* context); + static SubscriptionServiceFactory* GetInstance(); + + protected: + PrefService* GetPrefs(content::BrowserContext* context) const override; + SubscriptionPersistentMetadata* GetSubscriptionPersistentMetadata( + content::BrowserContext* context) const override; + + private: + friend class base::NoDestructor; + SubscriptionServiceFactory(); + ~SubscriptionServiceFactory() override; + + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ADBLOCK_SUBSCRIPTION_SERVICE_FACTORY_H_ diff --git a/chrome/browser/adblock/test/adblock_content_browser_client_browsertest.cc b/chrome/browser/adblock/test/adblock_content_browser_client_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_content_browser_client_browsertest.cc @@ -0,0 +1,125 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/adblock_content_browser_client.h" + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_commands.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/common/adblock_prefs.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/adblock/core/subscription/test/mock_subscription_service.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/spawned_test_server/spawned_test_server.h" +#include "net/test/test_data_directory.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace adblock { + +class AdblockContentBrowserClientBrowserTest : public InProcessBrowserTest { + public: + void SetUpOnMainThread() override { + watcher_ = std::make_unique( + browser()->tab_strip_model()->GetActiveWebContents(), u"PASS"); + watcher_->AlsoWaitForTitle(u"FAIL"); + embedded_test_server()->ServeFilesFromSourceDirectory( + net::GetWebSocketTestDataDirectory()); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + void TearDownOnMainThread() override { watcher_.reset(); } + + void NavigateToHTTP(const std::string& path) { + // Visit a HTTPS page for testing. + GURL::Replacements replacements; + replacements.SetSchemeStr("http"); + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), ws_server_.GetURL(path).ReplaceComponents(replacements))); + } + + std::string WaitAndGetTitle() { + return base::UTF16ToUTF8(watcher_->WaitAndGetTitle()); + } + + net::SpawnedTestServer ws_server_{net::SpawnedTestServer::TYPE_WS, + net::GetWebSocketTestDataDirectory()}; + + private: + std::unique_ptr watcher_; +}; + +IN_PROC_BROWSER_TEST_F(AdblockContentBrowserClientBrowserTest, + WebSocketConnectionNotInterrupted) { + // Launch a WebSocket server. + ASSERT_TRUE(ws_server_.Start()); + + // Disable ad-filtering. + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->SetEnabled(false); + + NavigateToHTTP("split_packet_check.html"); + + // WebSocket connected. + EXPECT_EQ("PASS", WaitAndGetTitle()); +} + +IN_PROC_BROWSER_TEST_F(AdblockContentBrowserClientBrowserTest, + WebSocketConnectionInterruptedButNotBlocked) { + // Launch a WebSocket server. + ASSERT_TRUE(ws_server_.Start()); + + // Enable ad-filtering. + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->SetEnabled(true); + + NavigateToHTTP("split_packet_check.html"); + + // WebSocket connected, there were no blocking filters. + EXPECT_EQ("PASS", WaitAndGetTitle()); +} + +IN_PROC_BROWSER_TEST_F(AdblockContentBrowserClientBrowserTest, + WebSocketConnectionInterruptedAndBlocked) { + // Launch a WebSocket server. + ASSERT_TRUE(ws_server_.Start()); + + // Intercept WebSocket and block connection via a filter. + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->SetEnabled(true); + adblock_configuration->RemoveCustomFilter(kAllowlistEverythingFilter); + adblock_configuration->AddCustomFilter({"*$websocket"}); + + NavigateToHTTP("split_packet_check.html"); + + // WebSocket did not connect. + EXPECT_EQ("FAIL", WaitAndGetTitle()); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_debug_url_browsertest.cc b/chrome/browser/adblock/test/adblock_debug_url_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_debug_url_browsertest.cc @@ -0,0 +1,314 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/content/browser/adblock_url_loader_factory_for_test.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "content/public/test/browser_test.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::HasSubstr; +using testing::Mock; +using testing::Return; +using testing::StartsWith; + +namespace adblock { + +class AdblockDebugUrlTest : public InProcessBrowserTest { + public: + AdblockDebugUrlTest() {} + ~AdblockDebugUrlTest() override = default; + AdblockDebugUrlTest(const AdblockDebugUrlTest&) = delete; + AdblockDebugUrlTest& operator=(const AdblockDebugUrlTest&) = delete; + + protected: + std::string ExecuteScriptAndExtractString(const std::string& js_code) const { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + return content::EvalJs(web_contents->GetPrimaryMainFrame(), js_code) + .ExtractString(); + } + + bool IsAdblockEnabled() { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) << "Test expects \"adblock\" configuration"; + return adblock_configuration->IsEnabled(); + } + + bool IsAAEnabled() { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) << "Test expects \"adblock\" configuration"; + return base::ranges::any_of( + adblock_configuration->GetFilterLists(), + [&](const auto& url) { return url == AcceptableAdsUrl(); }); + } + + const std::string kReadPageBodyScript = + "document.getElementsByTagName('body')[0].firstChild.innerHTML"; + + const std::string kAdblockDebugUrl = + "http://" + + adblock::AdblockURLLoaderFactoryForTest::kAdblockDebugDataHostName; +}; + +IN_PROC_BROWSER_TEST_F(AdblockDebugUrlTest, TestInvalidUrls) { + GURL no_command1(kAdblockDebugUrl); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), no_command1)); + ASSERT_EQ("INVALID_COMMAND", + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL no_command2(kAdblockDebugUrl + "/"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), no_command2)); + ASSERT_EQ("INVALID_COMMAND", + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL invalid_command_url(kAdblockDebugUrl + "/some_invalid_command"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), invalid_command_url)); + ASSERT_EQ("INVALID_COMMAND", + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL invalid_topic(kAdblockDebugUrl + "/filter/add/%2Fadsponsor."); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), invalid_topic)); + ASSERT_EQ("INVALID_COMMAND", + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL invalid_command(kAdblockDebugUrl + "/filters/ad/%2Fadsponsor."); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), invalid_command)); + ASSERT_EQ("INVALID_COMMAND", + ExecuteScriptAndExtractString(kReadPageBodyScript)); +} + +IN_PROC_BROWSER_TEST_F(AdblockDebugUrlTest, TestFilterCommands) { + GURL clear_filters_url(kAdblockDebugUrl + "/filters/clear"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), clear_filters_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL list_filters_url(kAdblockDebugUrl + "/filters/list"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_filters_url)); + std::string expected_no_filters = "OK"; + ASSERT_EQ(expected_no_filters, + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL add_filters_url(kAdblockDebugUrl + + "/filters/add/%2FadsPlugin%2F%2A%0A%2Fadsponsor."); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), add_filters_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_filters_url)); + auto response = ExecuteScriptAndExtractString(kReadPageBodyScript); + ASSERT_THAT(response, StartsWith("OK\n\n")); + ASSERT_THAT(response, HasSubstr("adsPlugin/*")); + ASSERT_THAT(response, HasSubstr("adsponsor.")); + + GURL remove_filter_url(kAdblockDebugUrl + "/filters/remove/%2Fadsponsor."); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), remove_filter_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_filters_url)); + std::string expected_one_filter = "OK\n\n/adsPlugin/*\n"; + ASSERT_EQ(expected_one_filter, + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), clear_filters_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_filters_url)); + ASSERT_EQ(expected_no_filters, + ExecuteScriptAndExtractString(kReadPageBodyScript)); +} + +IN_PROC_BROWSER_TEST_F(AdblockDebugUrlTest, TestDomainCommands) { + GURL clear_domains_url(kAdblockDebugUrl + "/domains/clear"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), clear_domains_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL list_domains_url(kAdblockDebugUrl + "/domains/list"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_domains_url)); + std::string expected_no_domains = "OK"; + ASSERT_EQ(expected_no_domains, + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL add_domain_url(kAdblockDebugUrl + + "/domains/add/example.com%0Adomain.org"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), add_domain_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_domains_url)); + auto response = ExecuteScriptAndExtractString(kReadPageBodyScript); + ASSERT_THAT(response, StartsWith("OK\n\n")); + ASSERT_THAT(response, HasSubstr("example.com")); + ASSERT_THAT(response, HasSubstr("domain.org")); + + GURL remove_domain_url(kAdblockDebugUrl + "/domains/remove/example.com"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), remove_domain_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_domains_url)); + std::string expected_one_domain = "OK\n\ndomain.org\n"; + ASSERT_EQ(expected_one_domain, + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), clear_domains_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_domains_url)); + ASSERT_EQ(expected_no_domains, + ExecuteScriptAndExtractString(kReadPageBodyScript)); +} + +IN_PROC_BROWSER_TEST_F(AdblockDebugUrlTest, TestSubscriptionCommands) { + GURL clear_subscriptions_url(kAdblockDebugUrl + "/subscriptions/clear"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), clear_subscriptions_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL list_subscriptions_url(kAdblockDebugUrl + "/subscriptions/list"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_subscriptions_url)); + std::string expected_no_subscriptions = "OK"; + ASSERT_EQ(expected_no_subscriptions, + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL add_subscription_url(kAdblockDebugUrl + + "/subscriptions/add/" + "https%3A%2F%2Fexample.com%2Flist1.txt%0Ahttps%3A%" + "2F%2Fwww.domain.org%2Flist2.txt"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), add_subscription_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_subscriptions_url)); + auto response = ExecuteScriptAndExtractString(kReadPageBodyScript); + ASSERT_THAT(response, StartsWith("OK\n\n")); + ASSERT_THAT(response, HasSubstr("https://example.com/list1.txt")); + ASSERT_THAT(response, HasSubstr("https://www.domain.org/list2.txt")); + + GURL remove_subscription_url( + kAdblockDebugUrl + + "/subscriptions/remove/https%3A%2F%2Fwww.domain.org%2Flist2.txt"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), remove_subscription_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_subscriptions_url)); + std::string expected_one_subscription = + "OK\n\nhttps://example.com/list1.txt\n"; + ASSERT_EQ(expected_one_subscription, + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), clear_subscriptions_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_subscriptions_url)); + ASSERT_EQ(expected_no_subscriptions, + ExecuteScriptAndExtractString(kReadPageBodyScript)); +} + +IN_PROC_BROWSER_TEST_F(AdblockDebugUrlTest, TestEnableAdblockCommands) { + GURL enable_adblock__url(kAdblockDebugUrl + "/adblock/enable"); + GURL disable_adblock_url(kAdblockDebugUrl + "/adblock/disable"); + GURL adblock_state_url(kAdblockDebugUrl + "/adblock/state"); + + ASSERT_TRUE(IsAdblockEnabled()); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), adblock_state_url)); + ASSERT_EQ("OK\n\nenabled", + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), disable_adblock_url)); + ASSERT_FALSE(IsAdblockEnabled()); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), adblock_state_url)); + ASSERT_EQ("OK\n\ndisabled", + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), enable_adblock__url)); + ASSERT_TRUE(IsAdblockEnabled()); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), adblock_state_url)); + ASSERT_EQ("OK\n\nenabled", + ExecuteScriptAndExtractString(kReadPageBodyScript)); +} + +IN_PROC_BROWSER_TEST_F(AdblockDebugUrlTest, TestEnableAACommands) { + GURL enable_aa_url(kAdblockDebugUrl + "/aa/enable"); + GURL disable_aa_url(kAdblockDebugUrl + "/aa/disable"); + GURL aa_state_url(kAdblockDebugUrl + "/aa/state"); + + ASSERT_TRUE(IsAAEnabled()); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), aa_state_url)); + ASSERT_EQ("OK\n\nenabled", + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), disable_aa_url)); + ASSERT_FALSE(IsAAEnabled()); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), aa_state_url)); + ASSERT_EQ("OK\n\ndisabled", + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), enable_aa_url)); + ASSERT_TRUE(IsAAEnabled()); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), aa_state_url)); + ASSERT_EQ("OK\n\nenabled", + ExecuteScriptAndExtractString(kReadPageBodyScript)); +} + +IN_PROC_BROWSER_TEST_F(AdblockDebugUrlTest, TestFilterCommandsInOldFormat) { + GURL clear_filters_url(kAdblockDebugUrl + "/filters/clear"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), clear_filters_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL list_filters_url(kAdblockDebugUrl + "/filters/list"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_filters_url)); + std::string expected_no_filters = "OK"; + ASSERT_EQ(expected_no_filters, + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + GURL add_filters_url(kAdblockDebugUrl + + "/add?payload=%2FadsPlugin%2F%2A%0A%2Fadsponsor."); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), add_filters_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_filters_url)); + auto response = ExecuteScriptAndExtractString(kReadPageBodyScript); + ASSERT_THAT(response, StartsWith("OK\n\n")); + ASSERT_THAT(response, HasSubstr("adsPlugin/*")); + ASSERT_THAT(response, HasSubstr("adsponsor.")); + + GURL remove_filter_url(kAdblockDebugUrl + "/remove?payload=%2Fadsponsor."); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), remove_filter_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_filters_url)); + std::string expected_one_filter = "OK\n\n/adsPlugin/*\n"; + ASSERT_EQ(expected_one_filter, + ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), clear_filters_url)); + ASSERT_EQ("OK", ExecuteScriptAndExtractString(kReadPageBodyScript)); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), list_filters_url)); + ASSERT_EQ(expected_no_filters, + ExecuteScriptAndExtractString(kReadPageBodyScript)); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_filter_list_browsertest.cc b/chrome/browser/adblock/test/adblock_filter_list_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_filter_list_browsertest.cc @@ -0,0 +1,260 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "base/check.h" +#include "base/environment.h" +#include "base/functional/callback_forward.h" +#include "base/run_loop.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "components/adblock/core/adblock_switches.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "components/version_info/version_info.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +namespace adblock { + +class AdblockFilterListDownloadTestBase : public InProcessBrowserTest { + public: + AdblockFilterListDownloadTestBase() + : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {} + // We need to set server and request handler asap + void SetUpInProcessBrowserTestFixture() override { + InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); + host_resolver()->AddRule("easylist-downloads.adblockplus.org", "127.0.0.1"); + https_server_.RegisterRequestHandler( + base::BindRepeating(&AdblockFilterListDownloadTestBase::RequestHandler, + base::Unretained(this))); + net::EmbeddedTestServer::ServerCertificateConfig cert_config; + cert_config.dns_names = {"easylist-downloads.adblockplus.org"}; + https_server_.SetSSLConfig(cert_config); + ASSERT_TRUE(https_server_.Start()); + SetFilterListServerPortForTesting(https_server_.port()); + } + + void CheckRequestParams(const net::test_server::HttpRequest& request, + std::string expected_disabled_value) { + std::string os; + base::ReplaceChars(version_info::GetOSType(), base::kWhitespaceASCII, "", + &os); + EXPECT_TRUE(request.relative_url.find("addonName=eyeo-chromium-sdk") != + std::string::npos); + EXPECT_TRUE(request.relative_url.find("addonVersion=1.0") != + std::string::npos); + EXPECT_TRUE(request.relative_url.find("platformVersion=1.0") != + std::string::npos); + EXPECT_TRUE(request.relative_url.find("platform=" + os) != + std::string::npos); + if (RunsOnEyeoCI()) { + // Those two checks below require "eyeo_application_name" and + // "eyeo_application_version" to be set as gn gen args. + EXPECT_TRUE( + request.relative_url.find("application=app_name_from_ci_config") != + std::string::npos) + << "Did you set \"eyeo_application_name\" gn gen arg?"; + EXPECT_TRUE(request.relative_url.find( + "applicationVersion=app_version_from_ci_config") != + std::string::npos) + << "Did you set \"eyeo_application_version\" gn gen arg?"; + } + EXPECT_TRUE( + request.relative_url.find("disabled=" + expected_disabled_value) != + std::string::npos); + } + + virtual std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) { + if (request.method == net::test_server::HttpMethod::METHOD_GET && + (base::StartsWith(request.relative_url, "/abp-filters-anti-cv.txt") || + base::StartsWith(request.relative_url, "/easylist.txt") || + base::StartsWith(request.relative_url, "/exceptionrules.txt"))) { + CheckRequestParams(request, "false"); + default_lists_.insert(request.relative_url.substr( + 1, request.relative_url.find_first_of("?") - 1)); + } + + // Unhandled requests result in the Embedded test server sending a 404. This + // is fine for the purpose of this test. + return nullptr; + } + + void NotifyTestFinished() { + finish_condition_met_ = true; + // If the test is currently waiting for the finish condition to be met, we + // need to quit the run loop. + if (quit_closure_) { + quit_closure_.Run(); + } + } + + void RunUntilTestFinished() { + // If the finish condition is already met, we don't need to run the run + // loop. + if (finish_condition_met_) { + return; + } + // Wait until NotifyTestFinished() gets called. + base::RunLoop run_loop; + quit_closure_ = run_loop.QuitClosure(); + std::move(run_loop).Run(); + } + + bool RunsOnEyeoCI() { + auto env = base::Environment::Create(); + std::string value; + env->GetVar("CI_PROJECT_NAME", &value); + return value == "chromium-sdk"; + } + + protected: + net::EmbeddedTestServer https_server_; + std::set default_lists_; + bool finish_condition_met_ = false; + base::RepeatingClosure quit_closure_; +}; + +class AdblockEnabledFilterListDownloadTest + : public AdblockFilterListDownloadTestBase { + public: + std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) override { + auto result = AdblockFilterListDownloadTestBase::RequestHandler(request); + // If we get all expected requests we simply finish the test by closing + // the browser, otherwise test will fail with a timeout. + if (default_lists_.size() == 3) { + NotifyTestFinished(); + } + + // Unhandled requests result in the Embedded test server sending a 404. + return result; + } +}; + +IN_PROC_BROWSER_TEST_F(AdblockEnabledFilterListDownloadTest, + TestInitialDownloads) { + RunUntilTestFinished(); +} + +class AdblockEnabledAcceptableAdsDisabledFilterListDownloadTest + : public AdblockFilterListDownloadTestBase { + public: + AdblockEnabledAcceptableAdsDisabledFilterListDownloadTest() { + const auto testing_interval = base::Seconds(1); + SubscriptionServiceFactory::SetUpdateCheckAndDelayIntervalsForTesting( + testing_interval, testing_interval); + } + + std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) override { + // If we get expected HEAD request we simply finish the test by closing + // the browser, otherwise test will fail with a timeout. + if (request.method == net::test_server::HttpMethod::METHOD_HEAD && + base::StartsWith(request.relative_url, "/exceptionrules.txt")) { + CheckRequestParams(request, "true"); + NotifyTestFinished(); + } + + return nullptr; + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch(adblock::switches::kDisableAcceptableAds); + } +}; + +IN_PROC_BROWSER_TEST_F( + AdblockEnabledAcceptableAdsDisabledFilterListDownloadTest, + TestInitialDownloads) { + RunUntilTestFinished(); +} + +enum class DisableSwitch { Adblock, Eyeo }; + +class AdblockDisabledFilterListDownloadTest + : public AdblockFilterListDownloadTestBase, + public testing::WithParamInterface { + public: + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch(GetParam() == DisableSwitch::Adblock + ? adblock::switches::kDisableAdblock + : adblock::switches::kDisableEyeoFiltering); + } + + void VerifyNoDownloads() { + ASSERT_EQ(0u, default_lists_.size()); + NotifyTestFinished(); + } +}; + +IN_PROC_BROWSER_TEST_P(AdblockDisabledFilterListDownloadTest, + TestInitialDownloads) { + // This test assumes that inital downloads (for adblock enabled) will happen + // within 10 seconds. When tested locally it always happens within 3 seconds. + base::OneShotTimer timer; + timer.Start( + FROM_HERE, base::Seconds(10), + base::BindOnce(&AdblockDisabledFilterListDownloadTest::VerifyNoDownloads, + base::Unretained(this))); + RunUntilTestFinished(); +} + +INSTANTIATE_TEST_SUITE_P(All, + AdblockDisabledFilterListDownloadTest, + testing::Values(DisableSwitch::Adblock, + DisableSwitch::Eyeo)); + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +class AdblockPtLocaleFilterListDownloadTest + : public AdblockFilterListDownloadTestBase { + public: + AdblockPtLocaleFilterListDownloadTest() { setenv("LANGUAGE", "pt_PT", 1); } + + std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) override { + EXPECT_FALSE(base::StartsWith(request.relative_url, "/easylist.txt")); + if (request.method == net::test_server::HttpMethod::METHOD_GET && + (base::StartsWith(request.relative_url, "/abp-filters-anti-cv.txt") || + base::StartsWith(request.relative_url, + "/easylistportuguese+easylist.txt") || + base::StartsWith(request.relative_url, "/exceptionrules.txt"))) { + CheckRequestParams(request, "false"); + default_lists_.insert(request.relative_url.substr( + 1, request.relative_url.find_first_of("?") - 1)); + } + + // If we get all expected requests we simply finish the test by closing + // the browser, otherwise test will fail with a timeout. + if (default_lists_.size() == 3) { + NotifyTestFinished(); + } + + // Unhandled requests result in the Embedded test server sending a 404. + return nullptr; + } +}; + +IN_PROC_BROWSER_TEST_F(AdblockPtLocaleFilterListDownloadTest, + TestInitialDownloads) { + RunUntilTestFinished(); +} +#endif + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_filtering_configurations_browsertest.cc b/chrome/browser/adblock/test/adblock_filtering_configurations_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_filtering_configurations_browsertest.cc @@ -0,0 +1,459 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include +#include "base/memory/raw_ptr.h" +#include "base/ranges/algorithm.h" +#include "base/run_loop.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/core/adblock_switches.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/configuration/filtering_configuration.h" +#include "components/adblock/core/configuration/persistent_filtering_configuration.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/version_info/version_info.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "gmock/gmock.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "url/gurl.h" + +namespace adblock { + +class SubscriptionInstalledWaiter + : public SubscriptionService::SubscriptionObserver { + public: + explicit SubscriptionInstalledWaiter( + SubscriptionService* subscription_service) + : subscription_service_(subscription_service) { + subscription_service_->AddObserver(this); + } + + ~SubscriptionInstalledWaiter() override { + subscription_service_->RemoveObserver(this); + } + + void WaitUntilSubscriptionsInstalled(std::vector subscriptions) { + awaited_subscriptions_ = std::move(subscriptions); + run_loop_.Run(); + } + + void OnSubscriptionInstalled(const GURL& subscription_url) override { + awaited_subscriptions_.erase( + base::ranges::remove(awaited_subscriptions_, subscription_url), + awaited_subscriptions_.end()); + if (awaited_subscriptions_.empty()) { + run_loop_.Quit(); + } + } + + protected: + raw_ptr subscription_service_; + base::RunLoop run_loop_; + std::vector awaited_subscriptions_; +}; + +class AdblockFilteringConfigurationBrowserTest : public InProcessBrowserTest { + public: + void SetUpInProcessBrowserTestFixture() override { + InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_test_server()->ServeFilesFromSourceDirectory( + "chrome/test/data/adblock"); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + void SetUpOnMainThread() override { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->RemoveCustomFilter(kAllowlistEverythingFilter); + } + + GURL BlockingFilterListUrl() { + return embedded_test_server()->GetURL( + "/filterlist_that_blocks_resource.txt"); + } + + GURL ElementHidingFilterListUrl() { + return embedded_test_server()->GetURL( + "/filterlist_that_hides_resource.txt"); + } + + GURL AllowingFilterListUrl() { + return embedded_test_server()->GetURL( + "/filterlist_that_allows_resource.txt"); + } + + GURL GetPageUrl() { + return embedded_test_server()->GetURL("test.org", "/innermost_frame.html"); + } + + void NavigateToPage() { + ASSERT_TRUE(NavigateToURL( + browser()->tab_strip_model()->GetActiveWebContents(), GetPageUrl())); + } + + std::unique_ptr MakeConfiguration( + std::string name) { + return std::make_unique( + browser()->profile()->GetOriginalProfile()->GetPrefs(), name); + } + + void InstallFilteringConfiguration( + std::unique_ptr configuration) { + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->InstallFilteringConfiguration(std::move(configuration)); + } + + void UninstallFilteringConfiguration(const std::string& configuration_name) { + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->UninstallFilteringConfiguration(configuration_name); + } + + void WaitUntilSubscriptionsInstalled(std::vector subscriptions) { + SubscriptionInstalledWaiter waiter( + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile())); + waiter.WaitUntilSubscriptionsInstalled(std::move(subscriptions)); + } + + std::string GetResourcesComputedStyle() { + const std::string javascript = + "window.getComputedStyle(document.getElementById('subresource'))." + "display"; + return content::EvalJs(browser() + ->tab_strip_model() + ->GetActiveWebContents() + ->GetPrimaryMainFrame(), + javascript) + .ExtractString(); + } + + void ExpectResourceBlocked() { + EXPECT_EQ("none", GetResourcesComputedStyle()); + } + + void ExpectResourceShown() { + EXPECT_EQ("inline", GetResourcesComputedStyle()); + } +}; + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + NoBlockingByDefault) { + auto configuration = MakeConfiguration("config"); + InstallFilteringConfiguration(std::move(configuration)); + + NavigateToPage(); + ExpectResourceShown(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + ResourceBlockedByFilteringConfigurationsList) { + auto configuration = MakeConfiguration("config"); + configuration->AddFilterList(BlockingFilterListUrl()); + + InstallFilteringConfiguration(std::move(configuration)); + + WaitUntilSubscriptionsInstalled({BlockingFilterListUrl()}); + + NavigateToPage(); + ExpectResourceBlocked(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + ResourceHiddenByFilteringConfigurationsList) { + auto configuration = MakeConfiguration("config"); + configuration->AddFilterList(ElementHidingFilterListUrl()); + + InstallFilteringConfiguration(std::move(configuration)); + + WaitUntilSubscriptionsInstalled({ElementHidingFilterListUrl()}); + + NavigateToPage(); + ExpectResourceBlocked(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + ResourceAllowedByFilteringConfigurationsList) { + auto configuration = MakeConfiguration("config"); + configuration->AddFilterList(BlockingFilterListUrl()); + configuration->AddFilterList(ElementHidingFilterListUrl()); + configuration->AddFilterList(AllowingFilterListUrl()); + + InstallFilteringConfiguration(std::move(configuration)); + + WaitUntilSubscriptionsInstalled({BlockingFilterListUrl(), + AllowingFilterListUrl(), + ElementHidingFilterListUrl()}); + + NavigateToPage(); + ExpectResourceShown(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + BlockingTakesPrecedenceBetweenConfigurations) { + auto blocking_configuration = MakeConfiguration("blocking"); + blocking_configuration->AddFilterList(BlockingFilterListUrl()); + + auto allowing_configuration = MakeConfiguration("allowing"); + allowing_configuration->AddFilterList(AllowingFilterListUrl()); + + InstallFilteringConfiguration(std::move(blocking_configuration)); + InstallFilteringConfiguration(std::move(allowing_configuration)); + + WaitUntilSubscriptionsInstalled( + {BlockingFilterListUrl(), AllowingFilterListUrl()}); + + NavigateToPage(); + ExpectResourceBlocked(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + ElementBlockedByCustomFilter) { + auto configuration = MakeConfiguration("config"); + configuration->AddCustomFilter("*resource.png"); + + InstallFilteringConfiguration(std::move(configuration)); + + NavigateToPage(); + ExpectResourceBlocked(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + ElementAllowedByCustomFilter) { + auto configuration = MakeConfiguration("config"); + configuration->AddCustomFilter("*resource.png"); + configuration->AddCustomFilter("@@*resource.png"); + + InstallFilteringConfiguration(std::move(configuration)); + + NavigateToPage(); + ExpectResourceShown(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + ElementAllowedByAllowedDomain) { + auto configuration = MakeConfiguration("config"); + configuration->AddCustomFilter("*resource.png"); + configuration->AddAllowedDomain(GetPageUrl().host()); + + InstallFilteringConfiguration(std::move(configuration)); + + NavigateToPage(); + ExpectResourceShown(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + PRE_CustomFiltersPersist) { + auto configuration = MakeConfiguration("persistent"); + // This custom filter will survive browser restart. + configuration->AddCustomFilter("*resource.png"); + InstallFilteringConfiguration(std::move(configuration)); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + CustomFiltersPersist) { + auto configurations = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetInstalledFilteringConfigurations(); + auto configuration = base::ranges::find(configurations, "persistent", + &FilteringConfiguration::GetName); + ASSERT_TRUE(configuration != configurations.end()); + EXPECT_THAT((*configuration)->GetCustomFilters(), + testing::UnorderedElementsAre("*resource.png")); + + NavigateToPage(); + ExpectResourceBlocked(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + DisabledConfigurationDoesNotBlock) { + auto configuration = MakeConfiguration("config"); + configuration->AddCustomFilter("*resource.png"); + configuration->SetEnabled(false); + + InstallFilteringConfiguration(std::move(configuration)); + + NavigateToPage(); + ExpectResourceShown(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + ConfigurationCanBeUsedAfterInstalling) { + auto configuration = MakeConfiguration("config"); + auto* configuration_ptr = configuration.get(); + + InstallFilteringConfiguration(std::move(configuration)); + + configuration_ptr->AddCustomFilter("*resource.png"); + + NavigateToPage(); + ExpectResourceBlocked(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + ConfigurationCanBeDisabledAfterInstalling) { + auto configuration = MakeConfiguration("config"); + auto* configuration_ptr = configuration.get(); + + InstallFilteringConfiguration(std::move(configuration)); + + configuration_ptr->AddCustomFilter("*resource.png"); + configuration_ptr->SetEnabled(false); + + NavigateToPage(); + ExpectResourceShown(); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + SubscriptionsDownloadedAfterConfigurationEnabled) { + auto configuration = MakeConfiguration("config"); + configuration->SetEnabled(false); + configuration->AddFilterList(BlockingFilterListUrl()); + configuration->AddFilterList(ElementHidingFilterListUrl()); + configuration->AddFilterList(AllowingFilterListUrl()); + auto* configuration_ptr = configuration.get(); + + InstallFilteringConfiguration(std::move(configuration)); + + configuration_ptr->SetEnabled(true); + + WaitUntilSubscriptionsInstalled({BlockingFilterListUrl(), + AllowingFilterListUrl(), + ElementHidingFilterListUrl()}); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + PRE_DownloadedSubscriptionsPersistOnDisk) { + auto configuration = MakeConfiguration("config"); + // This filter list setting will survive browser restart. + configuration->AddFilterList(BlockingFilterListUrl()); + + InstallFilteringConfiguration(std::move(configuration)); + + // This downloaded subscription won't need to be re-downloaded after restart. + WaitUntilSubscriptionsInstalled({BlockingFilterListUrl()}); +} + +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + DownloadedSubscriptionsPersistOnDisk) { + NavigateToPage(); + ExpectResourceBlocked(); +} + +// 1st run: create. +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + PRE_PRE_CreateThenRemoveConfiguration) { + auto configuration = MakeConfiguration("persistent"); + InstallFilteringConfiguration(std::move(configuration)); +} + +// 2nd run: remove. +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + PRE_CreateThenRemoveConfiguration) { + auto configurations = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetInstalledFilteringConfigurations(); + auto configuration = base::ranges::find(configurations, "persistent", + &FilteringConfiguration::GetName); + ASSERT_TRUE(configuration != configurations.end()); + UninstallFilteringConfiguration((*configuration)->GetName()); + configurations = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetInstalledFilteringConfigurations(); + configuration = base::ranges::find(configurations, "persistent", + &FilteringConfiguration::GetName); + ASSERT_TRUE(configuration == configurations.end()); +} + +// 3rd run: verify not present. +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationBrowserTest, + CreateThenRemoveConfiguration) { + auto configurations = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetInstalledFilteringConfigurations(); + auto configuration = base::ranges::find(configurations, "persistent", + &FilteringConfiguration::GetName); + ASSERT_TRUE(configuration == configurations.end()); +} + +class AdblockFilteringConfigurationDisableSwitchBrowserTest + : public AdblockFilteringConfigurationBrowserTest { + public: + void SetUpCommandLine(base::CommandLine* command_line) override { + if (base::StartsWith( + ::testing::UnitTest::GetInstance()->current_test_info()->name(), + "PRE_CreateConfigAndConfirmEnableStateAfterReset")) { + command_line->AppendSwitch(adblock::switches::kDisableEyeoFiltering); + } + } +}; + +// 1st run: create configuration and make sure it is enabled by default. +IN_PROC_BROWSER_TEST_F( + AdblockFilteringConfigurationDisableSwitchBrowserTest, + PRE_PRE_PRE_CreateConfigAndConfirmEnableStateAfterReset) { + auto configuration = MakeConfiguration("persistent"); + ASSERT_TRUE(configuration->IsEnabled()); + InstallFilteringConfiguration(std::move(configuration)); +} + +// 2nd run: make sure configuration is enabled after restart. +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationDisableSwitchBrowserTest, + PRE_PRE_CreateConfigAndConfirmEnableStateAfterReset) { + auto configurations = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetInstalledFilteringConfigurations(); + auto configuration = base::ranges::find(configurations, "persistent", + &FilteringConfiguration::GetName); + ASSERT_TRUE(configuration != configurations.end()); + ASSERT_TRUE((*configuration)->IsEnabled()); +} + +// 3rd run: after adding "--disable-eyeo-filtering" make sure configuration is +// disabled. +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationDisableSwitchBrowserTest, + PRE_CreateConfigAndConfirmEnableStateAfterReset) { + auto configurations = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetInstalledFilteringConfigurations(); + auto configuration = base::ranges::find(configurations, "persistent", + &FilteringConfiguration::GetName); + ASSERT_TRUE(configuration != configurations.end()); + ASSERT_FALSE((*configuration)->IsEnabled()); +} + +// 4th run: without "--disable-eyeo-filtering" make sure configuration is still +// disabled. +IN_PROC_BROWSER_TEST_F(AdblockFilteringConfigurationDisableSwitchBrowserTest, + CreateConfigAndConfirmEnableStateAfterReset) { + auto configurations = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetInstalledFilteringConfigurations(); + auto configuration = base::ranges::find(configurations, "persistent", + &FilteringConfiguration::GetName); + ASSERT_TRUE(configuration != configurations.end()); + ASSERT_FALSE((*configuration)->IsEnabled()); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_frame_hierarchy_builder_browsertest.cc b/chrome/browser/adblock/test/adblock_frame_hierarchy_builder_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_frame_hierarchy_builder_browsertest.cc @@ -0,0 +1,464 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include + +#include "base/ranges/algorithm.h" +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ssl/https_upgrades_util.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/content/browser/adblock_filter_match.h" +#include "components/adblock/content/browser/frame_hierarchy_builder.h" +#include "components/adblock/content/browser/resource_classification_runner.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/blocked_content/popup_blocker_tab_helper.h" +#include "components/embedder_support/switches.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace adblock { + +namespace { +class TabAddedRemovedObserver : public TabStripModelObserver { + public: + explicit TabAddedRemovedObserver(TabStripModel* tab_strip_model) { + tab_strip_model->AddObserver(this); + } + + void OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) override { + if (change.type() == TabStripModelChange::kInserted) { + inserted_ = true; + return; + } + if (change.type() == TabStripModelChange::kRemoved) { + EXPECT_TRUE(inserted_); + removed_ = true; + loop_.Quit(); + return; + } + NOTREACHED(); + } + + void Wait() { + if (inserted_ && removed_) { + return; + } + loop_.Run(); + } + + private: + bool inserted_ = false; + bool removed_ = false; + base::RunLoop loop_; +}; +} // namespace + +class ResourceClassificationRunnerObserver + : public ResourceClassificationRunner::Observer { + public: + ~ResourceClassificationRunnerObserver() override { + VerifyNoUnexpectedNotifications(); + } + // ResourceClassificationRunner::Observer: + void OnAdMatched(const GURL& url, + FilterMatchResult match_result, + const std::vector& parent_frame_urls, + ContentType content_type, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + if (match_result == FilterMatchResult::kAllowRule) { + allowed_ads_notifications.push_back(url); + } else { + blocked_ads_notifications.push_back(url); + } + } + + void OnPageAllowed(const GURL& url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + allowed_pages_notifications.push_back(url); + } + + void OnPopupMatched(const GURL& url, + FilterMatchResult match_result, + const GURL& opener_url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + auto& list = (match_result == FilterMatchResult::kBlockRule + ? blocked_popups_notifications + : allowed_popups_notifications); + auto it = std::find(list.begin(), list.end(), url.ExtractFileName()); + ASSERT_FALSE(it == list.end()) + << "Path " << url.ExtractFileName() << " not on list"; + list.erase(it); + if (popup_notifications_run_loop_ && allowed_popups_notifications.empty() && + blocked_popups_notifications.empty()) { + popup_notifications_run_loop_->Quit(); + } + } + + void VerifyNotificationSent(base::StringPiece path, std::vector& list) { + auto it = base::ranges::find(list, path, &GURL::ExtractFileName); + ASSERT_FALSE(it == list.end()) << "Path " << path << " not on list"; + // Remove expected notifications so that we can verify there are no + // unexpected notifications left by the end of each test. + list.erase(it); + } + + void VerifyNoUnexpectedNotifications() { + EXPECT_TRUE(blocked_ads_notifications.empty()); + EXPECT_TRUE(allowed_ads_notifications.empty()); + EXPECT_TRUE(blocked_popups_notifications.empty()); + EXPECT_TRUE(allowed_popups_notifications.empty()); + EXPECT_TRUE(allowed_pages_notifications.empty()); + } + + std::vector blocked_ads_notifications; + std::vector allowed_ads_notifications; + std::vector allowed_pages_notifications; + std::vector blocked_popups_notifications; + std::vector allowed_popups_notifications; + std::unique_ptr popup_notifications_run_loop_; +}; + +// Simulated setup: +// http://outer.com/outermost_frame.html +// has an iframe: http://middle.com/middle_frame.html +// has an iframe: http://inner.com/innermost_frame.html +// has a subresource http://inner.com/resource.png +// +// All of these files are in chrome/test/data/adblock. Cross-domain distribution +// is simulated via SetupCrossSiteRedirector. +// innermost_frame.html reports whether resource.png is visible via +// window.top.postMessage to outermost_frame.html, which stores a global +// subresource_visible JS variable. +class AdblockFrameHierarchyBrowserTest : public InProcessBrowserTest { + public: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_test_server()->ServeFilesFromSourceDirectory( + "chrome/test/data/adblock"); + content::SetupCrossSiteRedirector(embedded_test_server()); + AllowHttpForHostnamesForTesting({"outer.com", "inner.com", "middle.com"}, + browser()->profile()->GetPrefs()); + ASSERT_TRUE(embedded_test_server()->Start()); + auto* classification_runner = + ResourceClassificationRunnerFactory::GetForBrowserContext( + browser()->profile()); + classification_runner->AddObserver(&observer); + } + + void TearDownOnMainThread() override { + auto* classification_runner = + ResourceClassificationRunnerFactory::GetForBrowserContext( + browser()->profile()); + classification_runner->RemoveObserver(&observer); + InProcessBrowserTest::TearDownOnMainThread(); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitch(embedder_support::kDisablePopupBlocking); + } + + void SetFilters(std::vector filters) { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->RemoveCustomFilter(kAllowlistEverythingFilter); + for (auto& filter : filters) { + adblock_configuration->AddCustomFilter(filter); + } + } + + void NavigateToOutermostFrame() { + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL( + "/cross-site/outer.com/outermost_frame.html"))); + } + + void NavigateToOutermostFrameWithAboutBlank() { + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), + embedded_test_server()->GetURL( + "/cross-site/outer.com/outermost_frame_with_about_blank.html"))); + } + + void NavigateToPopupParentFrameAndWaitForNotifications() { + observer.popup_notifications_run_loop_ = std::make_unique(); + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL( + "/cross-site/outer.com/popup_parent.html"))); + if (!(observer.allowed_popups_notifications.empty() && + observer.blocked_popups_notifications.empty())) { + observer.popup_notifications_run_loop_->Run(); + } + } + + void VerifyTargetResourceShown(bool expected_shown) { + // Since in one test we dynamically load (write) `about:blank` iframe after + // parent page is loaded, we need to have some wait & poll mechanism to wait + // until such a frame (and its resources) is completely loaded by JS. + std::string script = base::StringPrintf(R"( + (async () => { + let count = 10; + function waitFor(condition) { + const poll = resolve => { + if(condition() || !count--) resolve(); + else setTimeout(_ => poll(resolve), 200); + } + return new Promise(poll); + } + // Waits up to 2 seconds + await waitFor(_ => subresource_visible === %s); + return subresource_visible === true; + })() + )", + expected_shown ? "true" : "false"); + EXPECT_EQ( + expected_shown, + content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), + script)); + } + + int NumberOfOpenTabs() { return browser()->tab_strip_model()->GetTabCount(); } + + ResourceClassificationRunnerObserver observer; +}; + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + SubresourceShownWithNoFilters) { + SetFilters({}); + NavigateToOutermostFrame(); + VerifyTargetResourceShown(true); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, SubresourceBlocked) { + SetFilters({"/resource.png"}); + NavigateToOutermostFrame(); + VerifyTargetResourceShown(false); + observer.VerifyNotificationSent("resource.png", + observer.blocked_ads_notifications); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + SubresourceAllowedViaInnerFrame) { + SetFilters({"/resource.png", "@@||inner.com^$document"}); + NavigateToOutermostFrame(); + VerifyTargetResourceShown(true); + observer.VerifyNotificationSent("resource.png", + observer.allowed_ads_notifications); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + SubresourceAllowedViaMiddleFrame) { + SetFilters({"/resource.png", "@@||middle.com^$document"}); + NavigateToOutermostFrame(); + VerifyTargetResourceShown(true); + observer.VerifyNotificationSent("resource.png", + observer.allowed_ads_notifications); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + SubresourceAllowedViaOutermostFrame) { + SetFilters({"/resource.png", "@@||outer.com^$document"}); + NavigateToOutermostFrame(); + VerifyTargetResourceShown(true); + observer.VerifyNotificationSent("resource.png", + observer.allowed_ads_notifications); + observer.VerifyNotificationSent("outermost_frame.html", + observer.allowed_pages_notifications); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + SubresourceBlockedWhenInvalidAllowRule) { + SetFilters({"/resource.png", "@@||bogus.com^$document"}); + NavigateToOutermostFrame(); + VerifyTargetResourceShown(false); + observer.VerifyNotificationSent("resource.png", + observer.blocked_ads_notifications); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + DISABLED_PopupHandledByChromiumWithoutFilters) { + // Without any popup-specific filters, blocking popups is handed over to + // Chromium, which has it's own heuristics that are not based on filters. + SetFilters({}); + NavigateToPopupParentFrameAndWaitForNotifications(); + // The popup was not opened: + EXPECT_EQ(1, NumberOfOpenTabs()); + // Because Chromium's built-in popup blocker stopped it: + EXPECT_EQ(1u, blocked_content::PopupBlockerTabHelper::FromWebContents( + browser()->tab_strip_model()->GetActiveWebContents()) + ->GetBlockedPopupsCount()); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, PopupBlockedByFilter) { + SetFilters({"http://inner.com*/popup.html$popup"}); + observer.blocked_popups_notifications.emplace_back("popup.html"); + TabAddedRemovedObserver observer(browser()->tab_strip_model()); + NavigateToPopupParentFrameAndWaitForNotifications(); + observer.Wait(); + EXPECT_EQ(1, NumberOfOpenTabs()); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, PopupAllowedByFilter) { + SetFilters({"http://inner.com*/popup.html$popup", + "@@http://inner.com*/popup.html$popup"}); + observer.allowed_popups_notifications.emplace_back("popup.html"); + NavigateToPopupParentFrameAndWaitForNotifications(); + // Popup was allowed to open in a new tab + EXPECT_EQ(2, NumberOfOpenTabs()); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + PopupAllowedByDomainSpecificFilter) { + // The frame that wants to open the popup is hosted on middle.com. + // The $popup allow rule applies to that frame. + SetFilters({"http://inner.com*/popup.html$popup", + "@@http://inner.com*/popup.html$popup,domain=middle.com"}); + observer.allowed_popups_notifications.emplace_back("popup.html"); + NavigateToPopupParentFrameAndWaitForNotifications(); + // Popup was allowed to open in a new tab + EXPECT_EQ(2, NumberOfOpenTabs()); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + PopupNotAllowedByDomainSpecificFilter) { + // The frame that wants to open the popup is hosted on middle.com. + // The $popup allow rule does not apply because it is specific to outer.com. + // outer.com is not the frame that is opening the popup. + SetFilters({"http://inner.com*/popup.html$popup", + "@@http://inner.com*/popup.html$popup,domain=outer.com"}); + observer.blocked_popups_notifications.emplace_back("popup.html"); + TabAddedRemovedObserver observer(browser()->tab_strip_model()); + NavigateToPopupParentFrameAndWaitForNotifications(); + observer.Wait(); + EXPECT_EQ(1, NumberOfOpenTabs()); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + PopupAllowedByParentDocument) { + // The outermost frame has a blanket allowing rule of $document type. + SetFilters({"http://inner.com*/popup.html$popup", + "@@||outer.com^$document,domain=outer.com"}); + observer.allowed_popups_notifications.emplace_back("popup.html"); + NavigateToPopupParentFrameAndWaitForNotifications(); + // Popup was allowed to open in a new tab + EXPECT_EQ(2, NumberOfOpenTabs()); + observer.VerifyNotificationSent("popup_parent.html", + observer.allowed_pages_notifications); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, + PopupAllowedByIntermediateParentDocument) { + // The middle frame has a blanket allowing rule of $document type. + SetFilters({"http://inner.com*/popup.html$popup", + "@@||middle.com^$document,domain=outer.com"}); + observer.allowed_popups_notifications.emplace_back("popup.html"); + NavigateToPopupParentFrameAndWaitForNotifications(); + // Popup was allowed to open in a new tab + EXPECT_EQ(2, NumberOfOpenTabs()); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, BlankFrameHiding) { + SetFilters({"##.about_blank_div"}); + NavigateToOutermostFrameWithAboutBlank(); + std::string script = R"( + function writeIframe() { + let frameDocument = document.getElementById("about_blank").contentWindow.document; + frameDocument.open("text/html"); + frameDocument.write(` + + +
+ +
+ + `); + frameDocument.close(); + } + if (document.readyState == "complete") { + writeIframe(); + } else { + document.getElementById("about_blank").addEventListener("load", writeIframe); + } + )"; + EXPECT_TRUE(content::ExecJs( + browser()->tab_strip_model()->GetActiveWebContents(), script)); + VerifyTargetResourceShown(false); + SetFilters({"@@^eyeo=true$document"}); + NavigateToOutermostFrameWithAboutBlank(); + EXPECT_TRUE(content::ExecJs( + browser()->tab_strip_model()->GetActiveWebContents(), script)); + VerifyTargetResourceShown(true); +} + +IN_PROC_BROWSER_TEST_F(AdblockFrameHierarchyBrowserTest, BlankFrameBlocking) { + SetFilters({"/resource.png"}); + NavigateToOutermostFrameWithAboutBlank(); + std::string script = R"( + function writeIframe() { + let frameDocument = document.getElementById("about_blank").contentWindow.document; + frameDocument.open("text/html"); + frameDocument.write(` + + + + + `); + frameDocument.close(); + } + if (document.readyState == "complete") { + writeIframe(); + } else { + document.getElementById("about_blank").addEventListener("load", writeIframe); + } + )"; + EXPECT_TRUE(content::ExecJs( + browser()->tab_strip_model()->GetActiveWebContents(), script)); + VerifyTargetResourceShown(false); + observer.VerifyNotificationSent("resource.png", + observer.blocked_ads_notifications); + SetFilters({"@@^eyeo=true$document"}); + NavigateToOutermostFrameWithAboutBlank(); + EXPECT_TRUE(content::ExecJs( + browser()->tab_strip_model()->GetActiveWebContents(), script)); + VerifyTargetResourceShown(true); + observer.VerifyNotificationSent("resource.png", + observer.allowed_ads_notifications); +} + +// More tests can be added / parametrized, e.g.: +// - elemhide blocking filters (in conjunction with $elemhide allow rules) +// - $subdocument-based allow rules + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_multiple_tabs_browsertest.cc b/chrome/browser/adblock/test/adblock_multiple_tabs_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_multiple_tabs_browsertest.cc @@ -0,0 +1,160 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/content/browser/resource_classification_runner.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/sessions/content/session_tab_helper.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +namespace adblock { + +class AdblockMultipleTabsBrowserTest + : public InProcessBrowserTest, + public ResourceClassificationRunner::Observer { + public: + void SetUpOnMainThread() override { + host_resolver()->AddRule(kTestDomain, "127.0.0.1"); + embedded_test_server()->ServeFilesFromSourceDirectory( + "chrome/test/data/adblock"); + ASSERT_TRUE(embedded_test_server()->Start()); + ResourceClassificationRunnerFactory::GetForBrowserContext( + browser()->profile()) + ->AddObserver(this); + SetFilters({"blocked.png", "allowed.png", "@@allowed.png"}); + } + + void TearDownInProcessBrowserTestFixture() override { + ASSERT_EQ(kTabsCount, static_cast(tabs_with_blocked_resource_.size())); + ASSERT_EQ(kTabsCount, static_cast(tabs_with_allowed_resource_.size())); + } + + void SetFilters(std::vector filters) { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->RemoveCustomFilter(kAllowlistEverythingFilter); + for (auto& filter : filters) { + adblock_configuration->AddCustomFilter(filter); + } + } + + void RestoreTabs(Browser* browser) { + content::DOMMessageQueue queue; + RecentTabsSubMenuModel menu(nullptr, browser); + menu.ExecuteCommand(menu.GetFirstRecentTabsCommandId(), 0); + for (int i = 0; i < kTabsCount; ++i) { + std::string message; + EXPECT_TRUE(queue.WaitForMessage(&message)); + EXPECT_EQ("\"READY\"", message); + } + } + + // ResourceClassificationRunner::Observer: + void OnAdMatched(const GURL& url, + FilterMatchResult match_result, + const std::vector& parent_frame_urls, + ContentType content_type, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + const content::WebContents* wc = + content::WebContents::FromRenderFrameHost(render_frame_host); + if (match_result == FilterMatchResult::kBlockRule && + url.path() == "/blocked.png") { + tabs_with_blocked_resource_.insert( + sessions::SessionTabHelper::IdForTab(wc).id()); + } else if (match_result == FilterMatchResult::kAllowRule && + url.path() == "/allowed.png") { + tabs_with_allowed_resource_.insert( + sessions::SessionTabHelper::IdForTab(wc).id()); + } + } + + void OnPageAllowed(const GURL& url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override {} + + void OnPopupMatched(const GURL& url, + FilterMatchResult match_result, + const GURL& opener_url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override {} + + protected: + const int kTabsCount = 4; + const char* kTestDomain = "example.com"; + std::set tabs_with_blocked_resource_; + std::set tabs_with_allowed_resource_; +}; + +IN_PROC_BROWSER_TEST_F(AdblockMultipleTabsBrowserTest, PRE_OpenManyTabs) { + // Load page in already opened tab + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), + embedded_test_server()->GetURL(kTestDomain, "/tab-restore.html"))); + // Open more tabs + for (int i = 0; i < kTabsCount - 1; ++i) { + ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition( + browser(), + embedded_test_server()->GetURL(kTestDomain, "/tab-restore.html"), + WindowOpenDisposition::NEW_FOREGROUND_TAB, + ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP)); + } + EXPECT_EQ(kTabsCount, browser()->tab_strip_model()->count()); + EXPECT_EQ(kTabsCount, static_cast(tabs_with_blocked_resource_.size())); + EXPECT_EQ(kTabsCount, static_cast(tabs_with_allowed_resource_.size())); + + // Open a new browser instance + ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL(url::kAboutBlankURL), WindowOpenDisposition::NEW_WINDOW, + ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER); + BrowserList* active_browser_list = BrowserList::GetInstance(); + EXPECT_EQ(2u, active_browser_list->size()); + + // Close the 1st browser and clear tabs test data + CloseBrowserSynchronously(browser()); + EXPECT_EQ(1u, active_browser_list->size()); + tabs_with_blocked_resource_.clear(); + tabs_with_allowed_resource_.clear(); + + Browser* browser = active_browser_list->get(0); + // Restore tabs from1 st browser instance (already closed) in 2nd instance + RestoreTabs(browser); +} + +IN_PROC_BROWSER_TEST_F(AdblockMultipleTabsBrowserTest, OpenManyTabs) { + ASSERT_EQ(0u, tabs_with_blocked_resource_.size()); + ASSERT_EQ(0u, tabs_with_allowed_resource_.size()); + // Restore tabs from previous session (previous test) + RestoreTabs(browser()); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_non_ascii_browsertest.cc b/chrome/browser/adblock/test/adblock_non_ascii_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_non_ascii_browsertest.cc @@ -0,0 +1,78 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace adblock { + +class AdblockNonASCIIBrowserTest : public InProcessBrowserTest { + public: + void SetUpOnMainThread() override { + host_resolver()->AddRule("xn----dtbfdbwspgnceulm.xn--p1ai", "127.0.0.1"); + embedded_test_server()->ServeFilesFromSourceDirectory( + "chrome/test/data/adblock"); + content::SetupCrossSiteRedirector(embedded_test_server()); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + void SetFilters(std::vector filters) { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->RemoveCustomFilter(kAllowlistEverythingFilter); + for (auto& filter : filters) { + adblock_configuration->AddCustomFilter(filter); + } + } + + std::string ExecuteScriptAndExtractString(const std::string& js_code) const { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + return content::EvalJs(web_contents->GetPrimaryMainFrame(), js_code) + .ExtractString(); + } +}; + +IN_PROC_BROWSER_TEST_F(AdblockNonASCIIBrowserTest, BlockNonASCII) { + static constexpr char kCheckVisibility[] = + R"(getComputedStyle(document.getElementsByClassName("форум")[0]).display)"; + + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), + embedded_test_server()->GetURL("форум-трейдеров.рф", "/non-ascii.html"))); + EXPECT_EQ("block", ExecuteScriptAndExtractString(kCheckVisibility)); + + SetFilters({"xn----dtbfdbwspgnceulm.xn--p1ai##.форум"}); + ASSERT_TRUE(ui_test_utils::NavigateToURL( + browser(), + embedded_test_server()->GetURL("форум-трейдеров.рф", "/non-ascii.html"))); + + EXPECT_EQ("none", ExecuteScriptAndExtractString(kCheckVisibility)); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_popup_browsertest.cc b/chrome/browser/adblock/test/adblock_popup_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_popup_browsertest.cc @@ -0,0 +1,463 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "base/run_loop.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h" +#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/content/browser/resource_classification_runner.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "gmock/gmock.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "url/gurl.h" + +namespace adblock { + +namespace { +class TabAddedRemovedObserver : public TabStripModelObserver { + public: + explicit TabAddedRemovedObserver(TabStripModel* tab_strip_model) { + tab_strip_model->AddObserver(this); + } + + void OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) override { + if (change.type() == TabStripModelChange::kInserted) { + inserted_ = true; + return; + } + if (change.type() == TabStripModelChange::kRemoved) { + EXPECT_TRUE(inserted_); + removed_ = true; + loop_.Quit(); + return; + } + NOTREACHED(); + } + + void Wait() { + if (inserted_ && removed_) { + return; + } + loop_.Run(); + } + + private: + bool inserted_ = false; + bool removed_ = false; + base::RunLoop loop_; +}; + +enum class Redirection { ClientSide, ServerSide }; + +} // namespace + +class AdblockPopupBrowserTest + : public InProcessBrowserTest, + public ResourceClassificationRunner::Observer, + public testing::WithParamInterface { + public: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_test_server()->RegisterRequestHandler(base::BindRepeating( + &AdblockPopupBrowserTest::RequestHandler, base::Unretained(this))); + ASSERT_TRUE(embedded_test_server()->Start()); + ResourceClassificationRunnerFactory::GetForBrowserContext( + browser()->profile()) + ->AddObserver(this); + } + + void TearDownOnMainThread() override { + VerifyNoUnexpectedNotifications(); + ResourceClassificationRunnerFactory::GetForBrowserContext( + browser()->profile()) + ->RemoveObserver(this); + InProcessBrowserTest::TearDownOnMainThread(); + } + + void VerifyNoUnexpectedNotifications() { + EXPECT_TRUE(blocked_popups_notifications_expectations_.empty()); + EXPECT_TRUE(allowed_popups_notifications_expectations_.empty()); + } + + bool IsServerSideRedirection() { + return GetParam() == Redirection::ServerSide; + } + + virtual std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) { + if (base::StartsWith("/main_page.html", request.relative_url)) { + static constexpr char kPopupFrameParent[] = + R"( + + + + + + + )"; + std::unique_ptr http_response( + new net::test_server::BasicHttpResponse); + http_response->set_code(net::HTTP_OK); + http_response->set_content(kPopupFrameParent); + http_response->set_content_type("text/html"); + return std::move(http_response); + } + if (base::StartsWith("/popup_frame.html", request.relative_url)) { + static constexpr char kPopupFrame[] = + R"( + + + + + + + Trigger link based popup with redirect + Trigger link based popup without redirect + Trigger script based popup (tab) with redirect + Trigger script based popup (window) with redirect + + )"; + std::unique_ptr http_response( + new net::test_server::BasicHttpResponse); + http_response->set_code(net::HTTP_OK); + http_response->set_content(kPopupFrame); + http_response->set_content_type("text/html"); + return std::move(http_response); + } + if (base::StartsWith("/popup_no_redirect.html", request.relative_url) || + base::StartsWith("/popup_redirected.html", request.relative_url)) { + std::unique_ptr http_response( + new net::test_server::BasicHttpResponse); + http_response->set_code(net::HTTP_OK); + http_response->set_content(""); + http_response->set_content_type("text/html"); + return std::move(http_response); + } + if (base::StartsWith("/popup_will_redirect.html", request.relative_url)) { + static constexpr char kClientSideRedirectingPopup[] = + R"( + + + + + + + )"; + std::unique_ptr http_response( + new net::test_server::BasicHttpResponse); + if (IsServerSideRedirection()) { + http_response->set_code(net::HTTP_MOVED_PERMANENTLY); + http_response->AddCustomHeader( + "Location", GetPageUrl("/popup_redirected.html").spec()); + } else { + http_response->set_code(net::HTTP_OK); + http_response->set_content(kClientSideRedirectingPopup); + http_response->set_content_type("text/html"); + } + return std::move(http_response); + } + return nullptr; + } + + void SetFilters(std::vector filters) { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->RemoveCustomFilter(kAllowlistEverythingFilter); + for (auto& filter : filters) { + adblock_configuration->AddCustomFilter(filter); + } + } + + void TriggerPopup(const std::string& popup_id) { + std::string script = base::StringPrintf( + R"( + let doc = document.querySelector('iframe[id="popup_frame"]').contentWindow.document; + let element = doc.getElementById('%s'); + element.click(); + )", + popup_id.c_str()); + EXPECT_TRUE(content::ExecJs( + browser()->tab_strip_model()->GetActiveWebContents(), script)); + } + + GURL GetPageUrl(const std::string& page) { + return embedded_test_server()->GetURL("popup_frame.org", page); + } + + void NavigateToPage() { + ASSERT_TRUE( + NavigateToURL(browser()->tab_strip_model()->GetActiveWebContents(), + GetPageUrl("/main_page.html"))); + } + + void WaitForTabToLoad() { + content::WebContents* popup = + browser()->tab_strip_model()->GetActiveWebContents(); + WaitForLoadStop(popup); + } + + void SetupNotificationsWaiter(base::RunLoop* run_loop) { + run_loop_ = run_loop; + } + + // ResourceClassificationRunner::Observer: + void OnAdMatched(const GURL& url, + FilterMatchResult match_result, + const std::vector& parent_frame_urls, + ContentType content_type, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override {} + + void OnPageAllowed(const GURL& url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override {} + + void OnPopupMatched(const GURL& url, + FilterMatchResult match_result, + const GURL& opener_url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + auto& list = (match_result == FilterMatchResult::kBlockRule + ? blocked_popups_notifications_expectations_ + : allowed_popups_notifications_expectations_); + auto it = std::find(list.begin(), list.end(), url.ExtractFileName()); + ASSERT_FALSE(it == list.end()) + << "Path " << url.ExtractFileName() << " not on list"; + list.erase(it); + if (run_loop_ && allowed_popups_notifications_expectations_.empty() && + blocked_popups_notifications_expectations_.empty()) { + run_loop_->Quit(); + } + } + + std::vector allowed_popups_notifications_expectations_; + std::vector blocked_popups_notifications_expectations_; + raw_ptr run_loop_ = nullptr; +}; + +IN_PROC_BROWSER_TEST_F(AdblockPopupBrowserTest, PopupLinkBlocked) { + SetFilters({"popup_no_redirect.html^$popup"}); + blocked_popups_notifications_expectations_.emplace_back( + "popup_no_redirect.html"); + NavigateToPage(); + TabAddedRemovedObserver observer(browser()->tab_strip_model()); + TriggerPopup("popup_link_no_redirect"); + observer.Wait(); + EXPECT_EQ(1, browser()->tab_strip_model()->count()); +} + +IN_PROC_BROWSER_TEST_P(AdblockPopupBrowserTest, + PopupScriptTabWithRedirectBlocked) { + SetFilters({"popup_redirected.html^$popup"}); + blocked_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + TabAddedRemovedObserver observer(browser()->tab_strip_model()); + TriggerPopup("popup_script_tab"); + observer.Wait(); + EXPECT_EQ(1, browser()->tab_strip_model()->count()); +} + +IN_PROC_BROWSER_TEST_P(AdblockPopupBrowserTest, + PopupScriptWindowWithRedirectBlocked) { + SetFilters({"popup_redirected.html^$popup"}); + blocked_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + TriggerPopup("popup_script_window"); + // Wait for 2nd browser to get closed (new window popup blocked) + EXPECT_EQ(2u, BrowserList::GetInstance()->size()); + ui_test_utils::WaitForBrowserToClose(BrowserList::GetInstance()->get(1)); + EXPECT_EQ(1u, BrowserList::GetInstance()->size()); +} + +IN_PROC_BROWSER_TEST_P(AdblockPopupBrowserTest, PopupLinkWithRedirectBlocked) { + SetFilters({"popup_redirected.html^$popup"}); + blocked_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + TabAddedRemovedObserver observer(browser()->tab_strip_model()); + TriggerPopup("popup_link_will_redirect"); + observer.Wait(); + EXPECT_EQ(1, browser()->tab_strip_model()->count()); +} + +IN_PROC_BROWSER_TEST_P( + AdblockPopupBrowserTest, + PopupScriptTabWithRedirectAllowedByIntermediateParentDocument) { + SetFilters({"popup_redirected.html^$popup", "@@/popup_frame.html^$document"}); + allowed_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + ui_test_utils::TabAddedWaiter waiter(browser()); + TriggerPopup("popup_script_tab"); + waiter.Wait(); + WaitForTabToLoad(); + EXPECT_EQ(2, browser()->tab_strip_model()->count()); +} + +IN_PROC_BROWSER_TEST_P( + AdblockPopupBrowserTest, + PopupScriptWindowWithRedirectAllowedByIntermediateParentDocument) { + SetFilters({"popup_redirected.html^$popup", "@@/popup_frame.html^$document"}); + allowed_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + base::RunLoop run_loop; + SetupNotificationsWaiter(&run_loop); + TriggerPopup("popup_script_window"); + run_loop.Run(); + EXPECT_EQ(2u, BrowserList::GetInstance()->size()); +} + +IN_PROC_BROWSER_TEST_P( + AdblockPopupBrowserTest, + PopupLinkWithRedirectAllowedByIntermediateParentDocument) { + SetFilters({"popup_redirected.html^$popup", "@@/popup_frame.html^$document"}); + allowed_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + ui_test_utils::TabAddedWaiter waiter(browser()); + TriggerPopup("popup_link_will_redirect"); + waiter.Wait(); + WaitForTabToLoad(); + EXPECT_EQ(2, browser()->tab_strip_model()->count()); + ; +} + +IN_PROC_BROWSER_TEST_P(AdblockPopupBrowserTest, + PopupScriptTabWithRedirectAllowedByParentDocument) { + SetFilters({"popup_redirected.html^$popup", "@@/main_page.html^$document"}); + allowed_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + ui_test_utils::TabAddedWaiter waiter(browser()); + TriggerPopup("popup_script_tab"); + waiter.Wait(); + WaitForTabToLoad(); + EXPECT_EQ(2, browser()->tab_strip_model()->count()); +} + +IN_PROC_BROWSER_TEST_P(AdblockPopupBrowserTest, + PopupScriptWindowWithRedirectAllowedByParentDocument) { + SetFilters({"popup_redirected.html^$popup", "@@/main_page.html^$document"}); + allowed_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + base::RunLoop run_loop; + SetupNotificationsWaiter(&run_loop); + TriggerPopup("popup_script_window"); + run_loop.Run(); + EXPECT_EQ(2u, BrowserList::GetInstance()->size()); +} + +IN_PROC_BROWSER_TEST_P(AdblockPopupBrowserTest, + PopupLinkWithRedirectAllowedByParentDocument) { + SetFilters({"popup_redirected.html^$popup", "@@/main_page.html^$document"}); + allowed_popups_notifications_expectations_.emplace_back( + "popup_redirected.html"); + NavigateToPage(); + ui_test_utils::TabAddedWaiter waiter(browser()); + TriggerPopup("popup_link_will_redirect"); + waiter.Wait(); + WaitForTabToLoad(); + EXPECT_EQ(2, browser()->tab_strip_model()->count()); +} + +// Make sure that we correctly recognize and apply blocking of +// redirected popups only for real popups. +IN_PROC_BROWSER_TEST_P(AdblockPopupBrowserTest, + LinkOpenedByContextMenuInNewTabNotBlocked) { + SetFilters({"popup_redirected.html^$popup"}); + ContextMenuNotificationObserver menu_observer( + IDC_CONTENT_CONTEXT_OPENLINKNEWTAB); + ui_test_utils::AllBrowserTabAddedWaiter add_tab; + + std::string script = base::StringPrintf( + "data:text/html,link", + GetPageUrl("/popup_will_redirect.html").spec().c_str()); + // Go to a page with a link + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(script))); + + // Opens a link in a new tab via a "real" context menu. + blink::WebMouseEvent mouse_event( + blink::WebInputEvent::Type::kMouseDown, + blink::WebInputEvent::kNoModifiers, + blink::WebInputEvent::GetStaticTimeStampForTests()); + mouse_event.button = blink::WebMouseEvent::Button::kRight; + mouse_event.SetPositionInWidget(15, 15); + content::WebContents* tab = + browser()->tab_strip_model()->GetActiveWebContents(); + gfx::Rect offset = tab->GetContainerBounds(); + mouse_event.SetPositionInScreen(15 + offset.x(), 15 + offset.y()); + mouse_event.click_count = 1; + tab->GetPrimaryMainFrame() + ->GetRenderViewHost() + ->GetWidget() + ->ForwardMouseEvent(mouse_event); + mouse_event.SetType(blink::WebInputEvent::Type::kMouseUp); + tab->GetPrimaryMainFrame() + ->GetRenderViewHost() + ->GetWidget() + ->ForwardMouseEvent(mouse_event); + + // The menu_observer will select "Open in new tab", wait for the new tab to + // be added. + tab = add_tab.Wait(); + EXPECT_TRUE(content::WaitForLoadStop(tab)); + + // Verify that it's the correct tab. + EXPECT_EQ(GetPageUrl("/popup_redirected.html"), tab->GetLastCommittedURL()); +} + +INSTANTIATE_TEST_SUITE_P(All, + AdblockPopupBrowserTest, + testing::Values(Redirection::ClientSide, + Redirection::ServerSide)); + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_snippets_browsertest.cc b/chrome/browser/adblock/test/adblock_snippets_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_snippets_browsertest.cc @@ -0,0 +1,80 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace adblock { + +class AdblockSnippetsBrowserTest : public InProcessBrowserTest { + public: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_test_server()->ServeFilesFromSourceDirectory( + "chrome/test/data/adblock"); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + void SetFilters(std::vector filters) { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->RemoveCustomFilter(kAllowlistEverythingFilter); + for (auto& filter : filters) { + adblock_configuration->AddCustomFilter(filter); + } + } + + GURL GetUrl(const std::string& path) { + return embedded_test_server()->GetURL("example.org", path); + } + + void VerifyTargetVisibility(bool is_hidden, const std::string& id) { + std::string is_invisible_js = + "getComputedStyle(document.getElementById('{{node id}}')).display == " + "'none'"; + base::ReplaceSubstringsAfterOffset(&is_invisible_js, 0, "{{node id}}", id); + EXPECT_EQ( + is_hidden, + content::EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), + is_invisible_js)); + } +}; + +IN_PROC_BROWSER_TEST_F(AdblockSnippetsBrowserTest, VerifyXpath3) { + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl("/xpath3.html"))); + VerifyTargetVisibility(false, "xpath3-target"); + SetFilters( + {"example.org#$#hide-if-matches-xpath3 //*[@id=\"xpath3-target\"]"}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl("/xpath3.html"))); + VerifyTargetVisibility(true, "xpath3-target"); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_subscription_service_browsertest.cc b/chrome/browser/adblock/test/adblock_subscription_service_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_subscription_service_browsertest.cc @@ -0,0 +1,201 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/ranges/algorithm.h" +#include "base/run_loop.h" +#include "base/strings/string_split.h" +#include "base/test/bind.h" +#include "base/threading/thread_restrictions.h" +#include "base/time/time.h" +#include "chrome/browser/adblock/subscription_persistent_metadata_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/version_info/version_info.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/http/http_request_headers.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +namespace adblock { + +class AdblockSubscriptionServiceBrowserTest + : public InProcessBrowserTest, + public SubscriptionService::SubscriptionObserver { + public: + AdblockSubscriptionServiceBrowserTest() { + SubscriptionServiceFactory::SetUpdateCheckAndDelayIntervalsForTesting( + base::Seconds(1), base::Seconds(1)); + } + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + https_server_ = std::make_unique( + net::EmbeddedTestServer::TYPE_HTTPS); + https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_OK); + } + + bool RequestHeadersContainAcceptLanguage( + const net::test_server::HttpRequest& request) { + const auto accept_language_it = + request.headers.find(net::HttpRequestHeaders::kAcceptLanguage); + return accept_language_it != request.headers.end() && + !accept_language_it->second.empty(); + } + + bool RequestHeadersContainAcceptEncodingBrotli( + const net::test_server::HttpRequest& request) { + const auto accept_encoding_it = + request.headers.find(net::HttpRequestHeaders::kAcceptEncoding); + if (accept_encoding_it == request.headers.end()) { + return false; + } + const auto split_encodings = + base::SplitString(accept_encoding_it->second, ",", + base::WhitespaceHandling::TRIM_WHITESPACE, + base::SplitResult::SPLIT_WANT_NONEMPTY); + return base::ranges::find(split_encodings, "br") != split_encodings.end(); + } + + std::unique_ptr + HandleSubscriptionUpdateRequestWithUrlCheck( + std::string expected_url_part, + const net::test_server::HttpRequest& request) { + static const char kSubscriptionHeader[] = + "[Adblock Plus 2.0]\n" + "! Checksum: X5A8vtJDBW2a9EgS9glqbg\n" + "! Version: 202202061935\n" + "! Last modified: 06 Feb 2022 19:35 UTC\n" + "! Expires: 1 days (update frequency)\n\n"; + if (base::StartsWith(request.relative_url, kSubscription, + base::CompareCase::SENSITIVE) && + !request_already_handled_) { + request_already_handled_ = true; + EXPECT_TRUE(RequestHeadersContainAcceptLanguage(request)); + EXPECT_TRUE(RequestHeadersContainAcceptEncodingBrotli(request)); + std::string os; + base::ReplaceChars(version_info::GetOSType(), base::kWhitespaceASCII, "", + &os); + EXPECT_TRUE(request.relative_url.find(expected_url_part) != + std::string::npos); + EXPECT_TRUE(request.relative_url.find("addonName=eyeo-chromium-sdk") != + std::string::npos); + EXPECT_TRUE(request.relative_url.find("addonVersion=1.0") != + std::string::npos); + EXPECT_TRUE(request.relative_url.find("platformVersion=1.0") != + std::string::npos); + EXPECT_TRUE(request.relative_url.find("platform=" + os) != + std::string::npos); + auto http_response = + std::make_unique(); + http_response->set_code(net::HTTP_OK); + http_response->set_content(kSubscriptionHeader); + http_response->set_content_type("text/plain"); + return std::move(http_response); + } + + // Unhandled requests result in the Embedded test server sending a 404. + return nullptr; + } + + void ExpectFilterListRequestMadeWithLastVersion(std::string last_version) { + https_server_->RegisterRequestHandler( + base::BindRepeating(&AdblockSubscriptionServiceBrowserTest:: + HandleSubscriptionUpdateRequestWithUrlCheck, + base::Unretained(this), last_version)); + ASSERT_TRUE(https_server_->Start(kPort)); + } + + // adblock::SubscriptionService::SubscriptionObserver + void OnSubscriptionInstalled(const GURL& url) override { + if (base::StartsWith(url.spec(), + https_server_->GetURL(kSubscription).spec())) { + // In order to ensure next run requests an update, mark the subscription + // as almost expired. + SubscriptionPersistentMetadataFactory::GetForBrowserContext( + browser()->profile()) + ->SetExpirationInterval(url, base::Milliseconds(1)); + CloseBrowserAsynchronously(browser()); + } + } + + std::unique_ptr https_server_; + bool request_already_handled_ = false; + static const std::string kSubscription; + // Port is hardcoded so the server url is the same across tests + static const int kPort = 65432; +}; + +const std::string AdblockSubscriptionServiceBrowserTest::kSubscription = + "/subscription.txt"; + +IN_PROC_BROWSER_TEST_F(AdblockSubscriptionServiceBrowserTest, PRE_LastVersion) { + ExpectFilterListRequestMadeWithLastVersion("&lastVersion=0&"); + // Downloading a filter list and setting its expiry time to almost zero, so + // the next run will have to update it ASAP. + + auto* subscription_service = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()); + subscription_service->AddObserver(this); + // Using a custom subscription URL here because before test sets + // up the server then SubscriptionService already started fetching default + // subscriptions. + subscription_service->GetAdblockFilteringConfiguration()->AddFilterList( + https_server_->GetURL(kSubscription)); + // Wait until subscription is downloaded and stored. + RunUntilBrowserProcessQuits(); +} + +IN_PROC_BROWSER_TEST_F(AdblockSubscriptionServiceBrowserTest, LastVersion) { + ExpectFilterListRequestMadeWithLastVersion("&lastVersion=202202061935&"); + auto* subscription_service = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()); + subscription_service->AddObserver(this); + // Wait for subscription update to trigger a network request. + RunUntilBrowserProcessQuits(); +} + +IN_PROC_BROWSER_TEST_F(AdblockSubscriptionServiceBrowserTest, + FilterFileDeletedAfterConversion) { + base::ScopedAllowBlockingForTesting allow_blocking; + ConversionExecutors* conversion_executors = + SubscriptionServiceFactory::GetInstance(); + DCHECK(conversion_executors); + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + const auto filter_list_path = temp_dir.GetPath().AppendASCII("easylist.txt"); + std::vector filter_list_contents = { + "[\"Adblock Plus 2.0\"]\n", "invalid file"}; + for (const auto& file_content : filter_list_contents) { + base::WriteFile(filter_list_path, file_content); + ASSERT_TRUE(base::PathExists(filter_list_path)); + base::RunLoop run_loop; + conversion_executors->ConvertFilterListFile( + DefaultSubscriptionUrl(), filter_list_path, + base::BindLambdaForTesting( + [&run_loop](ConversionResult result) { run_loop.Quit(); })); + run_loop.Run(); + ASSERT_FALSE(base::PathExists(filter_list_path)); + } +} + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_telemetry_service_browsertest.cc b/chrome/browser/adblock/test/adblock_telemetry_service_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_telemetry_service_browsertest.cc @@ -0,0 +1,256 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include "base/json/json_reader.h" +#include "chrome/browser/adblock/adblock_telemetry_service_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "components/adblock/core/activeping_telemetry_topic_provider.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" + +namespace adblock { + +class AdblockTelemetryServiceBrowserTestBase : public InProcessBrowserTest { + public: + // We need to set server and request handler asap + void SetUpInProcessBrowserTestFixture() override { + InProcessBrowserTest::SetUpInProcessBrowserTestFixture(); + host_resolver()->AddRule( + ActivepingTelemetryTopicProvider::DefaultBaseUrl().host(), "127.0.0.1"); + http_server_.RegisterRequestHandler(base::BindRepeating( + &AdblockTelemetryServiceBrowserTestBase::RequestHandler, + base::Unretained(this))); + ASSERT_TRUE(http_server_.Start()); + ActivepingTelemetryTopicProvider::SetHttpPortForTesting( + http_server_.port()); + auto testing_interval = base::Seconds(2); + ActivepingTelemetryTopicProvider::SetIntervalsForTesting(testing_interval); + AdblockTelemetryServiceFactory::GetInstance() + ->SetCheckAndDelayIntervalsForTesting(testing_interval, + testing_interval); + } + + base::Value* GetFirstPing(absl::optional& parsed) { + return GetPayload(parsed)->Find("first_ping"); + } + + base::Value* GetLastPing(absl::optional& parsed) { + return GetPayload(parsed)->Find("last_ping"); + } + + base::Value* GetLastPingTag(absl::optional& parsed) { + return GetPayload(parsed)->Find("last_ping_tag"); + } + + base::Value* GetPreviousLastPing(absl::optional& parsed) { + return GetPayload(parsed)->Find("previous_last_ping"); + } + + void CloseBrowserFromAnyThread() { + content::GetUIThreadTaskRunner({base::TaskPriority::USER_BLOCKING}) + ->PostTask(FROM_HERE, + base::BindOnce(&AdblockTelemetryServiceBrowserTestBase:: + CloseBrowserAsynchronously, + base::Unretained(this), browser())); + } + + std::unique_ptr CreateResponse( + const std::string& token) { + auto http_response = + std::make_unique(); + http_response->set_code(net::HTTP_OK); + http_response->set_content("{\"token\": \"" + token + "\"}"); + http_response->set_content_type("text/plain"); + return std::move(http_response); + } + + virtual std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) = 0; + + private: + base::Value::Dict* GetPayload(absl::optional& parsed) { + EXPECT_TRUE(parsed && parsed->is_dict()); + base::Value::Dict* parsed_dict = parsed->GetIfDict(); + EXPECT_TRUE(parsed_dict); + base::Value::Dict* payload = parsed_dict->FindDict("payload"); + EXPECT_TRUE(payload); + return payload; + } + + net::EmbeddedTestServer http_server_; +}; + +// Test three initial pings each after startup and each fails for the 1st time +class AdblockTelemetryServiceFirstPingAfterRestartWithRetryBrowserTest + : public AdblockTelemetryServiceBrowserTestBase { + public: + std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) override { + EXPECT_TRUE(base::StartsWith(request.relative_url, + "/topic/eyeochromium_activeping/version/1")); + EXPECT_TRUE(request.has_content); + absl::optional parsed = + base::JSONReader::Read(request.content); + base::Value* first_ping = GetFirstPing(parsed); + base::Value* last_ping = GetLastPing(parsed); + base::Value* previous_last_ping = GetPreviousLastPing(parsed); + + if (expected_first_ping_.empty()) { + EXPECT_FALSE(first_ping); + } else { + EXPECT_EQ(expected_first_ping_, *first_ping); + } + + if (expected_last_ping_.empty()) { + EXPECT_FALSE(last_ping); + } else { + EXPECT_EQ(expected_last_ping_, *last_ping); + } + + if (expected_previous_last_ping_.empty()) { + EXPECT_FALSE(previous_last_ping); + } else { + EXPECT_EQ(expected_previous_last_ping_, *previous_last_ping); + } + + if (!attempt_++) { + // Force retry by 404 response but 1st save last_ping_tag if any + base::Value* last_ping_tag = GetLastPingTag(parsed); + if (last_ping_tag) { + previous_last_ping_tag_ = last_ping_tag->GetString(); + } + return nullptr; + } + + // Verifies that retried ping has the same last_ping_tag + if (!previous_last_ping_tag_.empty()) { + base::Value* last_ping_tag = GetLastPingTag(parsed); + EXPECT_EQ(previous_last_ping_tag_, *last_ping_tag); + } + + // If we get expected telemetry ping with retry we simply finish the test + // by closing the browser, otherwise test will fail with a timeout. + CloseBrowserFromAnyThread(); + + // This will be still processed before CloseBrowserFromAnyThread() applies + return CreateResponse(server_response_); + } + + void TearDownInProcessBrowserTestFixture() override { + // Make sure we called RequestHandler exactly twice: 1st ping failed, 2nd + // was successful + EXPECT_EQ(2, attempt_); + } + + protected: + std::string server_response_; + std::string expected_first_ping_; + std::string expected_last_ping_; + std::string expected_previous_last_ping_; + + private: + int attempt_ = 0; + std::string previous_last_ping_tag_ = ""; +}; + +IN_PROC_BROWSER_TEST_F( + AdblockTelemetryServiceFirstPingAfterRestartWithRetryBrowserTest, + PRE_PRE_TestPing) { + // Sets the ping to be returned by the server and checked in the next test + server_response_ = "11111"; + RunUntilBrowserProcessQuits(); +} + +IN_PROC_BROWSER_TEST_F( + AdblockTelemetryServiceFirstPingAfterRestartWithRetryBrowserTest, + PRE_TestPing) { + // Sets the ping to be returned by the server and checked in the next test + server_response_ = "22222"; + expected_first_ping_ = expected_last_ping_ = "11111"; + RunUntilBrowserProcessQuits(); +} + +IN_PROC_BROWSER_TEST_F( + AdblockTelemetryServiceFirstPingAfterRestartWithRetryBrowserTest, + TestPing) { + expected_first_ping_ = "11111"; + expected_last_ping_ = "22222"; + expected_previous_last_ping_ = "11111"; + RunUntilBrowserProcessQuits(); +} + +// Test three inital pings +class AdblockTelemetryServiceSubsequentPingsBrowserTest + : public AdblockTelemetryServiceBrowserTestBase { + public: + std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) override { + EXPECT_TRUE(base::StartsWith(request.relative_url, + "/topic/eyeochromium_activeping/version/1")); + EXPECT_TRUE(request.has_content); + absl::optional parsed = + base::JSONReader::Read(request.content); + base::Value* first_ping = GetFirstPing(parsed); + base::Value* last_ping = GetLastPing(parsed); + base::Value* previous_last_ping = GetPreviousLastPing(parsed); + + if (count_ == 1) { + // No ping payload in the very 1st ping + EXPECT_FALSE(first_ping); + EXPECT_FALSE(last_ping); + } else if (count_ == 2) { + // For 2nd ping `first_ping` == `last_ping` + EXPECT_EQ(first_ping_, *first_ping); + EXPECT_EQ(first_ping_, *last_ping); + } else if (count_ == 3) { + // From 3rd ping onward `first_ping` != `last_ping` and we also get + // `previous_last_ping` + EXPECT_EQ(first_ping_, *first_ping); + EXPECT_EQ(second_ping_, *last_ping); + EXPECT_EQ(first_ping_, *previous_last_ping); + } + + // If we get three expected telemetry pings we simply finish the test by + // closing the browser, otherwise test will fail with a timeout. + if (count_ == 3) { + CloseBrowserFromAnyThread(); + return nullptr; + } + return CreateResponse(count_++ == 1 ? first_ping_ : second_ping_); + } + + void TearDownInProcessBrowserTestFixture() override { + // Make sure we called RequestHandler exactly three times + EXPECT_EQ(3, count_); + } + + private: + const std::string first_ping_ = "11111"; + const std::string second_ping_ = "22222"; + int count_ = 1; +}; + +IN_PROC_BROWSER_TEST_F(AdblockTelemetryServiceSubsequentPingsBrowserTest, + TestPing) { + RunUntilBrowserProcessQuits(); +} + +} // namespace adblock diff --git a/chrome/browser/adblock/test/adblock_web_bundle_browsertest.cc b/chrome/browser/adblock/test/adblock_web_bundle_browsertest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/adblock/test/adblock_web_bundle_browsertest.cc @@ -0,0 +1,427 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include +#include +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/functional/bind.h" +#include "base/path_service.h" +#include "base/strings/string_util.h" +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/content/browser/resource_classification_runner.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/web_package/web_bundle_builder.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/embedded_test_server/request_handler_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace adblock { + +class AdblockWebBundleBrowserTest + : public InProcessBrowserTest, + public ResourceClassificationRunner::Observer { + public: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_test_server()->RegisterDefaultHandler( + base::BindRepeating(&AdblockWebBundleBrowserTest::HandleFileRequest, + base::Unretained(this))); + ASSERT_TRUE(embedded_test_server()->Start()); + PrepareTempDirWithContent(); + ResourceClassificationRunnerFactory::GetForBrowserContext( + browser()->profile()) + ->AddObserver(this); + } + + void TearDownOnMainThread() override { + ResourceClassificationRunnerFactory::GetForBrowserContext( + browser()->profile()) + ->RemoveObserver(this); + InProcessBrowserTest::TearDownOnMainThread(); + } + + void PrepareTempDirWithContent() { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + CreateIndexFile(); + CreateByResourceWebBundle(); + CreateByBundleFileWebBundle(); + CreateByScopeFileWebBundle(); + } + + std::string GetFileContentFromTestDir( + base::FilePath::StringType relative_path) { + base::FilePath root; + CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &root)); + root = root.AppendASCII("chrome/test/data/adblock/wbn"); + std::string content; + CHECK(base::ReadFileToString(root.Append(relative_path), &content)); + return content; + } + + // In order for an html file to use absolute URLs in 'src' attributes to + // resources hosted by the embedded_test_server(), we need to replace + // {{{baseUrl}}} with the server's URL. It changes for every run. + std::string GetHtmlContentWithReplacements( + base::FilePath::StringType relative_path) { + std::string content = GetFileContentFromTestDir(relative_path); + base::ReplaceSubstringsAfterOffset( + &content, 0, "{{{baseUrl}}}", + embedded_test_server()->GetURL("example.org", "/").spec()); + return content; + } + + void CreateByResourceWebBundle() { + web_package::WebBundleBuilder builder; + builder.AddExchange("by_resource/blue_subresource_loading.css", + {{":status", "200"}, {"content-type", "text/css"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_resource/blue_subresource_loading.css"))); + builder.AddExchange("by_resource/blue_subresource_loading.png", + {{":status", "200"}, {"content-type", "image/png"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_resource/blue_subresource_loading.png"))); + builder.AddExchange("by_resource/red_subresource_loading.css", + {{":status", "200"}, {"content-type", "text/css"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_resource/red_subresource_loading.css"))); + builder.AddExchange("by_resource/red_subresource_loading.png", + {{":status", "200"}, {"content-type", "image/png"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_resource/red_subresource_loading.png"))); + builder.AddExchange( + "by_resource/xhr_result_1_subresource_loading.json", + {{":status", "200"}, {"content-type", "application/json"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_resource/xhr_result_1_subresource_loading.json"))); + builder.AddExchange( + "by_resource/fetch_result_1_subresource_loading.json", + {{":status", "200"}, {"content-type", "application/json"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_resource/fetch_result_1_subresource_loading.json"))); + builder.AddPrimaryURL(embedded_test_server()->GetURL("example.org", "/")); + const auto binary_data = builder.CreateBundle(); + ASSERT_TRUE(base::WriteFile( + temp_dir_.GetPath().AppendASCII("by_resource.wbn"), binary_data)); + } + + void CreateByBundleFileWebBundle() { + web_package::WebBundleBuilder builder; + builder.AddExchange("by_bundle_file/green_subresource_loading.css", + {{":status", "200"}, {"content-type", "text/css"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_bundle_file/green_subresource_loading.css"))); + builder.AddExchange("by_bundle_file/green_subresource_loading.png", + {{":status", "200"}, {"content-type", "image/png"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_bundle_file/green_subresource_loading.png"))); + builder.AddExchange("by_bundle_file/purple_subresource_loading.css", + {{":status", "200"}, {"content-type", "text/css"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_bundle_file/purple_subresource_loading.css"))); + builder.AddExchange("by_bundle_file/purple_subresource_loading.png", + {{":status", "200"}, {"content-type", "image/png"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_bundle_file/purple_subresource_loading.png"))); + builder.AddExchange( + "by_bundle_file/xhr_result_2_subresource_loading.json", + {{":status", "200"}, {"content-type", "application/json"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_bundle_file/xhr_result_2_subresource_loading.json"))); + builder.AddExchange( + "by_bundle_file/fetch_result_2_subresource_loading.json", + {{":status", "200"}, {"content-type", "application/json"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_bundle_file/fetch_result_2_subresource_loading.json"))); + builder.AddPrimaryURL(embedded_test_server()->GetURL("example.org", "/")); + const auto binary_data = builder.CreateBundle(); + ASSERT_TRUE(base::WriteFile( + temp_dir_.GetPath().AppendASCII("by_bundle_file.wbn"), binary_data)); + } + + void CreateByScopeFileWebBundle() { + web_package::WebBundleBuilder builder; + builder.AddExchange("by_scope/orange_subresource_loading.css", + {{":status", "200"}, {"content-type", "text/css"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_scope/orange_subresource_loading.css"))); + builder.AddExchange("by_scope/orange_subresource_loading.png", + {{":status", "200"}, {"content-type", "image/png"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_scope/orange_subresource_loading.png"))); + builder.AddExchange("by_scope/pink_subresource_loading.css", + {{":status", "200"}, {"content-type", "text/css"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_scope/pink_subresource_loading.css"))); + builder.AddExchange("by_scope/pink_subresource_loading.png", + {{":status", "200"}, {"content-type", "image/png"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_scope/pink_subresource_loading.png"))); + builder.AddExchange( + "by_scope/xhr_result_3_subresource_loading.json", + {{":status", "200"}, {"content-type", "application/json"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_scope/xhr_result_3_subresource_loading.json"))); + builder.AddExchange( + "by_scope/fetch_result_3_subresource_loading.json", + {{":status", "200"}, {"content-type", "application/json"}}, + GetFileContentFromTestDir(FILE_PATH_LITERAL( + "by_scope/fetch_result_3_subresource_loading.json"))); + builder.AddPrimaryURL(embedded_test_server()->GetURL("example.org", "/")); + const auto binary_data = builder.CreateBundle(); + ASSERT_TRUE(base::WriteFile(temp_dir_.GetPath().AppendASCII("by_scope.wbn"), + binary_data)); + } + + void CreateIndexFile() { + const auto html = GetHtmlContentWithReplacements( + FILE_PATH_LITERAL("index.html.mustache")); + ASSERT_TRUE( + base::WriteFile(temp_dir_.GetPath().AppendASCII("index.html"), html)); + } + + std::unique_ptr HandleFileRequest( + const net::test_server::HttpRequest& request) { + auto response = + net::test_server::HandleFileRequest(temp_dir_.GetPath(), request); + if (response) { + auto* basic = + static_cast(response.get()); + if (temp_dir_.GetPath() + .AppendASCII(request.GetURL().path().substr(1)) + .MatchesExtension(FILE_PATH_LITERAL(".wbn"))) { + basic->set_content_type("application/webbundle"); + } + basic->AddCustomHeader("X-Content-Type-Options", "nosniff"); + basic->AddCustomHeader("Access-Control-Allow-Origin", "*"); + } + return response; + } + + GURL GetUrl() { + return embedded_test_server()->GetURL("example.org", "/index.html"); + } + + void SetFilters(std::vector filters) { + auto* adblock_configuration = + SubscriptionServiceFactory::GetForBrowserContext(browser()->profile()) + ->GetAdblockFilteringConfiguration(); + adblock_configuration->RemoveCustomFilter(kAllowlistEverythingFilter); + for (auto& filter : filters) { + adblock_configuration->AddCustomFilter(filter); + } + } + + bool EvaluateJs(std::string value) { + return content::EvalJs(browser() + ->tab_strip_model() + ->GetActiveWebContents() + ->GetPrimaryMainFrame(), + value) + .ExtractBool(); + } + + bool IsImageLoaded(std::string selector) { + return EvaluateJs( + "(function(){ node = document.querySelector('" + selector + + "'); return node.complete && node.naturalHeight !== 0; })()"); + } + + bool IsCssBlocked(std::string selector) { + // On the test page, a box with this |id| is grey if element-specific CSS + // was blocked. Grey is the default color for divs, and gets overridden + // to element-specific colors by blockable CSS files. + return EvaluateJs( + "(function(){ return " + "window.getComputedStyle(document.querySelector('" + + selector + "')).backgroundColor == 'rgb(128, 128, 128)'; })()"); + } + + // Waits until the textContent of the selected item stops being "Pending" and + // returns whatever it's final value is. + // This asynchronous wait is because scripts that change the textContent may + // execute with a delay. + std::string GetSelectorTextContent(std::string selector) { + return content::EvalJs(browser() + ->tab_strip_model() + ->GetActiveWebContents() + ->GetPrimaryMainFrame(), + R"( + (async function (selector) { + return new Promise(resolve => { + if (!document.querySelector(selector).textContent.includes('Pending')) { + return resolve(document.querySelector(selector).textContent); + } + + const observer = new MutationObserver(mutations => { + if (!document.querySelector(selector).textContent.includes('Pending')) { + resolve(document.querySelector(selector).textContent); + observer.disconnect(); + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true + }); + }); +})(' )" + selector + "');") + .ExtractString(); + } + + bool IsXhrOrFetchRequestBlocked(std::string selector) { + return GetSelectorTextContent(selector).find("Error") != std::string::npos; + } + + bool IsXhrOrFetchRequestAllowed(std::string selector) { + return GetSelectorTextContent(selector).find("succeeded") != + std::string::npos; + } + + // ResourceClassificationRunner::Observer: + void OnAdMatched(const GURL& url, + FilterMatchResult match_result, + const std::vector& parent_frame_urls, + ContentType content_type, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + if (match_result == FilterMatchResult::kBlockRule) { + blocked_ads_notifications_.push_back(url); + } + } + + void OnPageAllowed(const GURL& url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override {} + + void OnPopupMatched(const GURL& url, + FilterMatchResult match_result, + const GURL& opener_url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override {} + + base::ScopedTempDir temp_dir_; + std::vector blocked_ads_notifications_; +}; + +IN_PROC_BROWSER_TEST_F(AdblockWebBundleBrowserTest, BlockImageByResource) { + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + const auto* blocked_image_selector = + R"(img[src="by_resource/blue_subresource_loading.png"])"; + const auto* other_image_selector = + R"(img[src="by_resource/red_subresource_loading.png"])"; + EXPECT_TRUE(IsImageLoaded(blocked_image_selector)); + EXPECT_TRUE(IsImageLoaded(other_image_selector)); + SetFilters({"/blue_subresource_loading.png"}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + EXPECT_FALSE(IsImageLoaded(blocked_image_selector)); + EXPECT_TRUE(IsImageLoaded(other_image_selector)); +} + +IN_PROC_BROWSER_TEST_F(AdblockWebBundleBrowserTest, BlockCssByResource) { + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + EXPECT_FALSE(IsCssBlocked("div.blue")); + SetFilters({"blue_subresource_loading.css"}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + EXPECT_TRUE(IsCssBlocked("div.blue")); +} + +IN_PROC_BROWSER_TEST_F(AdblockWebBundleBrowserTest, BlockXhrByResource) { + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + const auto* blocked_selector = "code#xhr_result_by_resource"; + EXPECT_TRUE(IsXhrOrFetchRequestAllowed(blocked_selector)); + SetFilters({"/xhr_result_1_subresource_loading.json"}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + EXPECT_TRUE(IsXhrOrFetchRequestBlocked(blocked_selector)); +} + +IN_PROC_BROWSER_TEST_F(AdblockWebBundleBrowserTest, BlockFetchByResource) { + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + const auto* blocked_selector = "code#fetch_result_by_resource"; + EXPECT_TRUE(IsXhrOrFetchRequestAllowed(blocked_selector)); + SetFilters({"/fetch_result_1_subresource_loading.json"}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + EXPECT_TRUE(IsXhrOrFetchRequestBlocked(blocked_selector)); +} + +IN_PROC_BROWSER_TEST_F(AdblockWebBundleBrowserTest, BlockByBundle) { + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + const auto* blocked_image_selector = + R"(img[src="by_bundle_file/green_subresource_loading.png"])"; + EXPECT_TRUE(IsImageLoaded(blocked_image_selector)); + EXPECT_FALSE(IsCssBlocked("div.green")); + EXPECT_FALSE(IsCssBlocked("div.purple")); + EXPECT_TRUE(IsXhrOrFetchRequestAllowed("code#xhr_result_by_bundle_file")); + EXPECT_TRUE(IsXhrOrFetchRequestAllowed("code#fetch_result_by_bundle_file")); + + SetFilters({"by_bundle_file.wbn$webbundle"}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + EXPECT_FALSE(IsImageLoaded(blocked_image_selector)); + EXPECT_TRUE(IsCssBlocked("div.green")); + EXPECT_TRUE(IsCssBlocked("div.purple")); + EXPECT_THAT(blocked_ads_notifications_, + testing::Contains(embedded_test_server()->GetURL( + "example.org", "/by_bundle_file.wbn"))); + // FIXME the following two expectations fail because the state of the requests + // is "pending" rather than allowed or blocked: DPD-1887 + // EXPECT_TRUE(IsXhrOrFetchRequestBlocked("code#xhr_result_by_bundle_file")); + // EXPECT_TRUE(IsXhrOrFetchRequestBlocked("code#fetch_result_by_bundle_file")); +} + +IN_PROC_BROWSER_TEST_F(AdblockWebBundleBrowserTest, BlockByScope) { + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + const auto* blocked_image_selector = + R"(img[src="by_scope/orange_subresource_loading.png"])"; + EXPECT_TRUE(IsImageLoaded(blocked_image_selector)); + EXPECT_FALSE(IsCssBlocked("div.orange")); + EXPECT_FALSE(IsCssBlocked("div.pink")); + EXPECT_TRUE(IsXhrOrFetchRequestAllowed("code#xhr_result_by_scope")); + EXPECT_TRUE(IsXhrOrFetchRequestAllowed("code#fetch_result_by_scope")); + + SetFilters({"by_scope/"}); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetUrl())); + EXPECT_FALSE(IsImageLoaded(blocked_image_selector)); + EXPECT_TRUE(IsCssBlocked("div.orange")); + EXPECT_TRUE(IsCssBlocked("div.pink")); + EXPECT_TRUE(IsXhrOrFetchRequestBlocked("code#xhr_result_by_scope")); + EXPECT_TRUE(IsXhrOrFetchRequestBlocked("code#fetch_result_by_scope")); +} + +// TODO: +// - Mixed origins +// - Signed bundles. including case of navigation between signed and unsigned +// content. +// - If you block the whole bundle, all resources requests to components +// declared in + + + + + + + +
+ +

+
+
+
diff --git a/chrome/browser/resources/adblock_internals/adblock_internals.ts b/chrome/browser/resources/adblock_internals/adblock_internals.ts
new file mode 100644
--- /dev/null
+++ b/chrome/browser/resources/adblock_internals/adblock_internals.ts
@@ -0,0 +1,43 @@
+// This file is part of eyeo Chromium SDK,
+// Copyright (C) 2006-present eyeo GmbH
+//
+// eyeo Chromium SDK is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 3 as
+// published by the Free Software Foundation.
+//
+// eyeo Chromium SDK is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with eyeo Chromium SDK.  If not, see .
+
+import {getRequiredElement} from 'chrome://resources/js/util_ts.js';
+import {AdblockInternalsPageHandler} from './adblock_internals.mojom-webui.js';
+
+async function debugInfo(): Promise {
+  const info = await AdblockInternalsPageHandler.getRemote().getDebugInfo();
+  return info.debugInfo;
+}
+
+async function refresh() {
+  getRequiredElement('content').innerText = await debugInfo();
+}
+
+getRequiredElement('copy-button').addEventListener('click', async () => {
+  navigator.clipboard.writeText(await debugInfo());
+});
+
+getRequiredElement('download-button').addEventListener('click', async () => {
+  const url = URL.createObjectURL(new Blob([await debugInfo()], {type: 'text/plain'}));
+  const a = document.createElement('a');
+  a.href = url;
+  a.download = 'adblock-internals.txt';
+  a.click();
+  URL.revokeObjectURL(url);
+});
+
+getRequiredElement('refresh').addEventListener('click', refresh);
+
+document.addEventListener('DOMContentLoaded', refresh);
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -5,6 +5,10 @@
 // This test creates a fake safebrowsing service, where we can inject known-
 // threat urls.  It then uses a real browser to go to these urls, and sends
 // "goback" or "proceed" commands and verifies they work.
+//
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
 
 #include 
 #include 
@@ -62,6 +66,7 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/adblock/core/features.h"
 #include "components/google/core/common/google_util.h"
 #include "components/grit/components_resources.h"
 #include "components/omnibox/browser/omnibox_prefs.h"
@@ -608,7 +613,8 @@ class SafeBrowsingBlockingPageBrowserTest
         {kTagAndAttributeParamName, "div,foo,div,baz"}};
     base::test::FeatureRefAndParams tag_and_attribute(
         safe_browsing::kThreatDomDetailsTagAndAttributeFeature, parameters);
-    scoped_feature_list_.InitWithFeaturesAndParameters({tag_and_attribute}, {});
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {tag_and_attribute}, {adblock::kAdblockPlusFeature});
   }
 
   SafeBrowsingBlockingPageBrowserTest(
diff --git a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
--- a/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_browser_test_harness.cc
@@ -1,6 +1,10 @@
 // Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+//
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
 
 #include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
 
@@ -22,6 +26,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/chrome_test_utils.h"
+#include "components/adblock/core/features.h"
 #include "components/blocked_content/safe_browsing_triggered_popup_blocker.h"
 #include "components/content_settings/browser/page_specific_content_settings.h"
 #include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
@@ -71,7 +76,8 @@ MockSubresourceFilterObserver::~MockSubresourceFilterObserver() = default;
 SubresourceFilterBrowserTest::SubresourceFilterBrowserTest() {
   scoped_feature_list_.InitWithFeatures(
       /*enabled_features=*/{kAdTagging},
-      /*disabled_features=*/{features::kHttpsUpgrades});
+      /*disabled_features=*/{features::kHttpsUpgrades,
+                             adblock::kAdblockPlusFeature});
 }
 
 SubresourceFilterBrowserTest::~SubresourceFilterBrowserTest() = default;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1,6 +1,10 @@
 # Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+#
+# This source code is a part of eyeo Chromium SDK.
+# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file.
+
 
 import("//build/config/buildflags_paint_preview.gni")
 import("//build/config/chromebox_for_meetings/buildflags.gni")
@@ -231,6 +235,10 @@ static_library("ui") {
     "webid/identity_dialog_controller.h",
     "webui/about_ui.cc",
     "webui/about_ui.h",
+    "webui/adblock_internals/adblock_internals_page_handler_impl.cc",
+    "webui/adblock_internals/adblock_internals_page_handler_impl.h",
+    "webui/adblock_internals/adblock_internals_ui.cc",
+    "webui/adblock_internals/adblock_internals_ui.h",
     "webui/autofill_and_password_manager_internals/autofill_internals_ui.cc",
     "webui/autofill_and_password_manager_internals/autofill_internals_ui.h",
     "webui/autofill_and_password_manager_internals/internals_ui_handler.cc",
@@ -432,12 +440,13 @@ static_library("ui") {
     "//chrome/browser/share",
     "//chrome/browser/storage_access_api",
     "//chrome/browser/ui/side_panel:side_panel_enums",
-    "//chrome/browser/ui/webui:configs",
+    "//chrome/browser/ui/webui/adblock_internals:mojo_bindings",
     "//chrome/browser/ui/webui/location_internals:mojo_bindings",
     "//chrome/browser/ui/webui/omnibox:mojo_bindings",
     "//chrome/browser/ui/webui/segmentation_internals:mojo_bindings",
     "//chrome/browser/ui/webui/suggest_internals:mojo_bindings",
     "//chrome/browser/ui/webui/usb_internals:mojo_bindings",
+    "//chrome/browser/ui/webui:configs",
     "//chrome/common",
     "//chrome/common/net",
     "//chrome/common/search:mojo_bindings",
@@ -449,6 +458,7 @@ static_library("ui") {
     "//components/about_ui",
     "//components/access_code_cast/common:metrics",
     "//components/account_id",
+    "//components/adblock/content:browser",
     "//components/autofill/content/browser",
     "//components/autofill/content/browser:risk_proto",
     "//components/autofill/core/browser",
diff --git a/chrome/browser/ui/prefs/pref_watcher.cc b/chrome/browser/ui/prefs/pref_watcher.cc
--- a/chrome/browser/ui/prefs/pref_watcher.cc
+++ b/chrome/browser/ui/prefs/pref_watcher.cc
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
+
 #include "chrome/browser/ui/prefs/pref_watcher.h"
 
 #include "base/functional/bind.h"
@@ -13,6 +17,7 @@
 #include "chrome/browser/renderer_preferences_util.h"
 #include "chrome/browser/ui/prefs/prefs_tab_helper.h"
 #include "chrome/common/pref_names.h"
+#include "components/adblock/core/common/adblock_prefs.h"
 #include "components/language/core/browser/pref_names.h"
 #include "components/live_caption/pref_names.h"
 #include "components/privacy_sandbox/tracking_protection_settings.h"
@@ -68,6 +73,14 @@ const char* const kWebPrefsToObserve[] = {
 #else
     prefs::kAccessibilityFocusHighlightEnabled,
 #endif
+
+    adblock::common::prefs::kAdblockAllowedDomainsLegacy,
+    adblock::common::prefs::kAdblockCustomFiltersLegacy,
+    adblock::common::prefs::kAdblockCustomSubscriptionsLegacy,
+    adblock::common::prefs::kAdblockSubscriptionsLegacy,
+    adblock::common::prefs::kEnableAcceptableAdsLegacy,
+    adblock::common::prefs::kEnableAdblockLegacy,
+
 };
 
 const int kWebPrefsToObserveLength = std::size(kWebPrefsToObserve);
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -1,6 +1,10 @@
 // Copyright 2014 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+//
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
 
 #include "chrome/browser/ui/tab_helpers.h"
 
@@ -13,6 +17,9 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/adblock/element_hider_factory.h"
+#include "chrome/browser/adblock/sitekey_storage_factory.h"
+#include "chrome/browser/adblock/subscription_service_factory.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/breadcrumbs/breadcrumb_manager_tab_helper.h"
 #include "chrome/browser/browser_process.h"
@@ -111,6 +118,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_isolated_world_ids.h"
 #include "chrome/common/chrome_switches.h"
+#include "components/adblock/content/browser/adblock_webcontents_observer.h"
 #include "components/autofill/content/browser/content_autofill_client.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
 #include "components/autofill/core/browser/browser_autofill_manager.h"
@@ -341,6 +349,16 @@ void TabHelpers::AttachTabHelpers(WebContents* web_contents) {
                                                    optimization_guide_decider);
     }
   }
+
+  AdblockWebContentObserver::CreateForWebContents(
+      web_contents,
+      adblock::SubscriptionServiceFactory::GetForBrowserContext(
+          web_contents->GetBrowserContext()),
+      adblock::ElementHiderFactory::GetForBrowserContext(
+          web_contents->GetBrowserContext()),
+      adblock::SitekeyStorageFactory::GetForBrowserContext(
+          web_contents->GetBrowserContext()),
+      std::make_unique());
   autofill::ChromeAutofillClient::CreateForWebContents(web_contents);
   if (breadcrumbs::IsEnabled())
     BreadcrumbManagerTabHelper::CreateForWebContents(web_contents);
diff --git a/chrome/browser/ui/webui/adblock_internals/BUILD.gn b/chrome/browser/ui/webui/adblock_internals/BUILD.gn
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/webui/adblock_internals/BUILD.gn
@@ -0,0 +1,23 @@
+#
+# This file is part of eyeo Chromium SDK,
+# Copyright (C) 2006-present eyeo GmbH
+#
+# eyeo Chromium SDK is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3 as
+# published by the Free Software Foundation.
+#
+# eyeo Chromium SDK is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with eyeo Chromium SDK.  If not, see .
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojo_bindings") {
+  sources = [ "adblock_internals.mojom" ]
+  webui_module_path = "/"
+  use_typescript_sources = true
+}
diff --git a/chrome/browser/ui/webui/adblock_internals/adblock_internals.mojom b/chrome/browser/ui/webui/adblock_internals/adblock_internals.mojom
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/webui/adblock_internals/adblock_internals.mojom
@@ -0,0 +1,20 @@
+// This file is part of eyeo Chromium SDK,
+// Copyright (C) 2006-present eyeo GmbH
+//
+// eyeo Chromium SDK is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 3 as
+// published by the Free Software Foundation.
+//
+// eyeo Chromium SDK is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with eyeo Chromium SDK.  If not, see .
+
+module mojom.adblock_internals;
+
+interface AdblockInternalsPageHandler {
+  GetDebugInfo() => (string debug_info);
+};
diff --git a/chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.cc b/chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.cc
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.cc
@@ -0,0 +1,115 @@
+/*
+ * This file is part of eyeo Chromium SDK,
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * eyeo Chromium SDK is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * eyeo Chromium SDK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with eyeo Chromium SDK.  If not, see .
+ */
+
+#include "chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.h"
+
+#include "base/time/time_to_iso8601.h"
+#include "chrome/browser/adblock/adblock_telemetry_service_factory.h"
+#include "chrome/browser/adblock/session_stats_factory.h"
+#include "chrome/browser/adblock/subscription_service_factory.h"
+#include "components/adblock/core/adblock_telemetry_service.h"
+#include "components/adblock/core/session_stats.h"
+#include "components/adblock/core/subscription/subscription_config.h"
+#include "components/adblock/core/subscription/subscription_service.h"
+
+namespace {
+
+std::string SubscriptionInstallationStateToString(
+    adblock::Subscription::InstallationState state) {
+  using State = adblock::Subscription::InstallationState;
+  switch (state) {
+    case State::Installed:
+      return "Installed";
+    case State::Installing:
+      return "Installing";
+    case State::Preloaded:
+      return "Preloaded";
+    case State::Unknown:
+      return "Unknown";
+  }
+  NOTREACHED();
+  return "";
+}
+
+std::string DebugLine(std::string name, std::string value, int level) {
+  return std::string(2 * level, ' ') + name + ": " + value + '\n';
+}
+
+std::string DebugLine(std::string name, int value, int level) {
+  return DebugLine(name, std::to_string(value), level);
+}
+
+}  // namespace
+
+AdblockInternalsPageHandlerImpl::AdblockInternalsPageHandlerImpl(
+    Profile* profile,
+    mojo::PendingReceiver
+        receiver)
+    : profile_(profile), receiver_(this, std::move(receiver)) {}
+
+AdblockInternalsPageHandlerImpl::~AdblockInternalsPageHandlerImpl() = default;
+
+void AdblockInternalsPageHandlerImpl::GetDebugInfo(
+    GetDebugInfoCallback callback) {
+  CHECK(profile_);
+  auto* service =
+      adblock::SubscriptionServiceFactory::GetForBrowserContext(profile_);
+  auto* stats = adblock::SessionStatsFactory::GetForBrowserContext(profile_);
+  auto allowed = stats->GetSessionAllowedAdsCount();
+  auto blocked = stats->GetSessionBlockedAdsCount();
+  std::string content;
+  for (auto* config : service->GetInstalledFilteringConfigurations()) {
+    content += DebugLine("Configuration", config->GetName(), 0);
+    content += DebugLine("Enabled", config->IsEnabled(), 1);
+    for (const auto& it : config->GetAllowedDomains()) {
+      content += DebugLine("Allowed domain", it, 1);
+    }
+    for (const auto& it : config->GetCustomFilters()) {
+      content += DebugLine("Custom filter", it, 1);
+    }
+    for (auto it : service->GetCurrentSubscriptions(config)) {
+      auto url = it->GetSourceUrl();
+      content += DebugLine("Subscription", url.spec(), 1);
+      content += DebugLine(
+          "State",
+          SubscriptionInstallationStateToString(it->GetInstallationState()), 2);
+      content += DebugLine("Title", it->GetTitle(), 2);
+      content += DebugLine("Version", it->GetCurrentVersion(), 2);
+      content += DebugLine("Last update",
+                           base::TimeToISO8601(it->GetInstallationTime()), 2);
+      content += DebugLine("Total allowed", allowed[url], 2);
+      content += DebugLine("Total blocked", blocked[url], 2);
+    }
+  }
+
+  auto* telemetry_service =
+      adblock::AdblockTelemetryServiceFactory::GetForProfile(profile_);
+  telemetry_service->GetTopicProvidersDebugInfo(base::BindOnce(
+      &AdblockInternalsPageHandlerImpl::OnTelemetryServiceInfoArrived,
+      std::move(callback), std::move(content)));
+}
+
+void AdblockInternalsPageHandlerImpl::OnTelemetryServiceInfoArrived(
+    GetDebugInfoCallback callback,
+    std::string content,
+    std::vector topic_provider_content) {
+  for (auto& topic_provider_debug_info : topic_provider_content) {
+    content +=
+        DebugLine("Eyeometry topic provider", topic_provider_debug_info, 0);
+  }
+  std::move(callback).Run(std::move(content));
+}
diff --git a/chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.h b/chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.h
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of eyeo Chromium SDK,
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * eyeo Chromium SDK is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * eyeo Chromium SDK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with eyeo Chromium SDK.  If not, see .
+ */
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ADBLOCK_INTERNALS_ADBLOCK_INTERNALS_PAGE_HANDLER_IMPL_H_
+#define CHROME_BROWSER_UI_WEBUI_ADBLOCK_INTERNALS_ADBLOCK_INTERNALS_PAGE_HANDLER_IMPL_H_
+
+#include "base/memory/raw_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/adblock_internals/adblock_internals.mojom.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+class AdblockInternalsPageHandlerImpl
+    : public mojom::adblock_internals::AdblockInternalsPageHandler {
+ public:
+  explicit AdblockInternalsPageHandlerImpl(
+      Profile* profile,
+      mojo::PendingReceiver<
+          mojom::adblock_internals::AdblockInternalsPageHandler> receiver);
+  AdblockInternalsPageHandlerImpl(const AdblockInternalsPageHandlerImpl&) =
+      delete;
+  AdblockInternalsPageHandlerImpl& operator=(
+      const AdblockInternalsPageHandlerImpl&) = delete;
+  ~AdblockInternalsPageHandlerImpl() override;
+
+  // mojom::adblock_internals::AdblockInternalsPageHandler:
+  void GetDebugInfo(GetDebugInfoCallback callback) override;
+
+ private:
+  static void OnTelemetryServiceInfoArrived(
+      GetDebugInfoCallback callback,
+      std::string content,
+      std::vector topic_provider_content);
+  raw_ptr profile_;
+  mojo::Receiver
+      receiver_;
+};
+#endif  // CHROME_BROWSER_UI_WEBUI_ADBLOCK_INTERNALS_ADBLOCK_INTERNALS_PAGE_HANDLER_IMPL_H_
diff --git a/chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.cc b/chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.cc
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.cc
@@ -0,0 +1,47 @@
+/*                                                                            \
+ * This file is part of eyeo Chromium SDK,                                    \
+ * Copyright (C) 2006-present eyeo GmbH                                       \
+ *                                                                            \
+ * eyeo Chromium SDK is free software: you can redistribute it and/or modify  \
+ * it under the terms of the GNU General Public License version 3 as          \
+ * published by the Free Software Foundation.                                 \
+ *                                                                            \
+ * eyeo Chromium SDK is distributed in the hope that it will be useful,       \
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of             \
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              \
+ * GNU General Public License for more details.                               \
+ *                                                                            \
+ * You should have received a copy of the GNU General Public License          \
+ * along with eyeo Chromium SDK.  If not, see . \
+ */
+
+#include "chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/webui/adblock_internals/adblock_internals_page_handler_impl.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/adblock_internals_resources.h"
+#include "chrome/grit/adblock_internals_resources_map.h"
+#include "content/public/browser/web_ui_data_source.h"
+
+AdblockInternalsUI::AdblockInternalsUI(content::WebUI* web_ui)
+    : ui::MojoWebUIController(web_ui), profile_(Profile::FromWebUI(web_ui)) {
+  content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
+      profile_, chrome::kChromeUIAdblockInternalsHost);
+  webui::SetupWebUIDataSource(source,
+                              base::make_span(kAdblockInternalsResources,
+                                              kAdblockInternalsResourcesSize),
+                              IDR_ADBLOCK_INTERNALS_ADBLOCK_INTERNALS_HTML);
+}
+
+AdblockInternalsUI::~AdblockInternalsUI() = default;
+
+WEB_UI_CONTROLLER_TYPE_IMPL(AdblockInternalsUI)
+
+void AdblockInternalsUI::BindInterface(
+    mojo::PendingReceiver
+        receiver) {
+  handler_ = std::make_unique(
+      profile_, std::move(receiver));
+}
diff --git a/chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.h b/chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.h
new file mode 100644
--- /dev/null
+++ b/chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of eyeo Chromium SDK,
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * eyeo Chromium SDK is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * eyeo Chromium SDK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with eyeo Chromium SDK.  If not, see .
+ */
+
+#ifndef CHROME_BROWSER_UI_WEBUI_ADBLOCK_INTERNALS_ADBLOCK_INTERNALS_UI_H_
+#define CHROME_BROWSER_UI_WEBUI_ADBLOCK_INTERNALS_ADBLOCK_INTERNALS_UI_H_
+
+#include "chrome/browser/ui/webui/adblock_internals/adblock_internals.mojom.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "ui/webui/mojo_web_ui_controller.h"
+
+class Profile;
+
+class AdblockInternalsUI : public ui::MojoWebUIController {
+ public:
+  explicit AdblockInternalsUI(content::WebUI* web_ui);
+
+  AdblockInternalsUI(const AdblockInternalsUI&) = delete;
+  AdblockInternalsUI& operator=(const AdblockInternalsUI&) = delete;
+
+  ~AdblockInternalsUI() override;
+
+  void BindInterface(
+      mojo::PendingReceiver<
+          mojom::adblock_internals::AdblockInternalsPageHandler> receiver);
+
+ private:
+  WEB_UI_CONTROLLER_TYPE_DECL();
+
+  raw_ptr profile_;
+  std::unique_ptr
+      handler_;
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_ADBLOCK_INTERNALS_ADBLOCK_INTERNALS_UI_H_
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -1,6 +1,10 @@
 // Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+//
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
 
 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
 
@@ -32,6 +36,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/signin_features.h"
 #include "chrome/browser/ui/webui/about_ui.h"
+#include "chrome/browser/ui/webui/adblock_internals/adblock_internals_ui.h"
 #include "chrome/browser/ui/webui/autofill_and_password_manager_internals/autofill_internals_ui.h"
 #include "chrome/browser/ui/webui/autofill_and_password_manager_internals/password_manager_internals_ui.h"
 #include "chrome/browser/ui/webui/browsing_topics/browsing_topics_internals_ui.h"
@@ -434,8 +439,12 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
   // after the host name.
   if (url.host_piece() == chrome::kChromeUIAccessibilityHost)
     return &NewWebUI;
-  if (url.host_piece() == chrome::kChromeUIAutofillInternalsHost)
+  if (url.host_piece() == chrome::kChromeUIAdblockInternalsHost) {
+    return &NewWebUI;
+  }
+  if (url.host_piece() == chrome::kChromeUIAutofillInternalsHost) {
     return &NewWebUI;
+  }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   if (url.host_piece() == chrome::kChromeUIAppDisabledHost)
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -1,6 +1,9 @@
 # Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+#
+# This source code is a part of eyeo Chromium SDK.
+# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file.
 
 import("//build/buildflag_header.gni")
 import("//build/config/chrome_build.gni")
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -1,6 +1,10 @@
 // Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+//
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
 
 #include "chrome/common/webui_url_constants.h"
 
@@ -32,6 +36,7 @@ const char kChromeUIAboutURL[] = "chrome://about/";
 const char kChromeUIActivateSafetyCheckSettingsURL[] =
     "chrome://settings/safetyCheck?activateSafetyCheck";
 const char kChromeUIAccessibilityHost[] = "accessibility";
+const char kChromeUIAdblockInternalsHost[] = "adblock-internals";
 const char kChromeUIAllSitesPath[] = "/content/all";
 const char kChromeUIAppIconHost[] = "app-icon";
 const char kChromeUIAppIconURL[] = "chrome://app-icon/";
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -1,6 +1,10 @@
 // Copyright 2017 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+//
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
 
 // Contains constants for WebUI UI/Host/SubPage constants. Anything else go in
 // chrome/common/url_constants.h.
@@ -33,6 +37,7 @@ extern const char kChromeUIAboutHost[];
 extern const char kChromeUIAboutURL[];
 extern const char kChromeUIActivateSafetyCheckSettingsURL[];
 extern const char kChromeUIAccessibilityHost[];
+extern const char kChromeUIAdblockInternalsHost[];
 extern const char kChromeUIAllSitesPath[];
 extern const char kChromeUIAppIconHost[];
 extern const char kChromeUIAppIconURL[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1,6 +1,9 @@
 # Copyright 2014 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+#
+# This source code is a part of eyeo Chromium SDK.
+# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file.
 
 import("//build/cipd/cipd.gni")
 import("//build/config/buildflags_paint_preview.gni")
@@ -25,6 +28,7 @@ import("//chrome/test/base/js2gtest.gni")
 import("//chrome/test/include_js_tests.gni")
 import("//chrome/version.gni")
 import("//chromeos/ash/components/assistant/assistant.gni")
+import("//components/adblock/features.gni")
 import("//components/captive_portal/core/features.gni")
 import("//components/enterprise/buildflags/buildflags.gni")
 import("//components/feed/features.gni")
@@ -1206,6 +1210,7 @@ if (is_android) {
 
     deps += [
       ":persisted_tab_data_test_proto",
+      "//components/adblock/content:browser",
       "//components/autofill/content/browser:test_support",
       "//components/back_forward_cache:back_forward_cache",
       "//components/browsing_data/core:core",
@@ -1587,6 +1592,7 @@ if (!is_android) {
       "//chrome/test/data/webui/mojo:mojo_bindings",
       "//chrome/test/media_router/access_code_cast:access_code_cast_integration_base",
       "//chrome/test/payments:test_support",
+      "//components/adblock/content:browser",
       "//components/autofill/content/browser:risk_proto",
       "//components/autofill/content/browser:test_support",
       "//components/autofill/content/common/mojom",
@@ -1924,6 +1930,7 @@ if (!is_android) {
       "//ash/components/arc/test/data/icons",
       "//chrome/browser/page_load_metrics/integration_tests/data/",
       "//chrome/renderer/resources/extensions/",
+      "//chrome/test/data/adblock/",
       "//chrome/test/data/cart/",
       "//components/test/data/ad_tagging/",
       "//components/test/data/ads_observer/",
@@ -2007,6 +2014,17 @@ if (!is_android) {
       "../browser/accessibility/image_annotation_browsertest.cc",
       "../browser/accessibility/interstitial_accessibility_browsertest.cc",
       "../browser/accessibility/page_colors_browsertest.cc",
+      "../browser/adblock/test/adblock_content_browser_client_browsertest.cc",
+      "../browser/adblock/test/adblock_filter_list_browsertest.cc",
+      "../browser/adblock/test/adblock_filtering_configurations_browsertest.cc",
+      "../browser/adblock/test/adblock_frame_hierarchy_builder_browsertest.cc",
+      "../browser/adblock/test/adblock_multiple_tabs_browsertest.cc",
+      "../browser/adblock/test/adblock_non_ascii_browsertest.cc",
+      "../browser/adblock/test/adblock_popup_browsertest.cc",
+      "../browser/adblock/test/adblock_snippets_browsertest.cc",
+      "../browser/adblock/test/adblock_subscription_service_browsertest.cc",
+      "../browser/adblock/test/adblock_telemetry_service_browsertest.cc",
+      "../browser/adblock/test/adblock_web_bundle_browsertest.cc",
       "../browser/apps/guest_view/app_view_browsertest.cc",
       "../browser/apps/guest_view/web_view_browsertest.cc",
       "../browser/apps/platform_apps/app_browsertest.cc",
@@ -2736,6 +2754,10 @@ if (!is_android) {
       sources += [ "../browser/chrome_for_testing/chrome_for_testing_info_bar_browsertest.cc" ]
     }
 
+    if (eyeo_intercept_debug_url) {
+      sources += [ "../browser/adblock/test/adblock_debug_url_browsertest.cc" ]
+    }
+
     if (enable_reporting) {
       sources += [ "../browser/net/reporting_browsertest.cc" ]
     }
@@ -3570,6 +3592,7 @@ if (!is_android) {
         ]
       }
 
+
       if (is_chromeos_ash && enable_extensions) {
         deps +=
             [ "//chromeos/ash/components/network/portal_detector:test_support" ]
@@ -5860,6 +5883,7 @@ test("unit_tests") {
     "../browser/about_flags_unittest.cc",
     "../browser/accessibility/media_app/ax_media_app_handler_unittest.cc",
     "../browser/active_use_util_unittest.cc",
+    "../browser/adblock/adblock_content_browser_client_unittest.cc",
     "../browser/after_startup_task_utils_unittest.cc",
     "../browser/apps/icon_standardizer_unittest.cc",
     "../browser/apps/user_type_filter_unittest.cc",
@@ -6528,6 +6552,8 @@ test("unit_tests") {
     "//chrome/services/file_util:unit_tests",
     "//chrome/services/qrcode_generator/public/cpp",
     "//components/account_id",
+    "//components/adblock/content/browser:test_support",
+    "//components/adblock/core:test_support",
     "//components/assist_ranker/proto",
     "//components/autofill/content/browser:test_support",
     "//components/background_sync",
@@ -10003,6 +10029,7 @@ if (!is_android) {
 
     public_deps = [
       "//chrome/browser:test_support_ui",
+      "//components/adblock/core:test_support",
       "//content/public/browser",
       "//content/test:test_support",
       "//google_apis:test_support",
diff --git a/chrome/test/base/chrome_test_launcher.cc b/chrome/test/base/chrome_test_launcher.cc
--- a/chrome/test/base/chrome_test_launcher.cc
+++ b/chrome/test/base/chrome_test_launcher.cc
@@ -1,6 +1,10 @@
 // Copyright 2012 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+//
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
 
 #include "chrome/test/base/chrome_test_launcher.h"
 
@@ -26,6 +30,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/adblock/adblock_content_browser_client.h"
 #include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/metrics/chrome_feature_list_creator.h"
 #include "chrome/common/chrome_constants.h"
@@ -154,7 +159,7 @@ ChromeTestLauncherDelegate::GetUserDataDirectoryCommandLineSwitch() {
 // watch for long-running tasks and produce a useful timeout message in order to
 // find the cause of flaky timeout tests.
 class BrowserTestChromeContentBrowserClient
-    : public ChromeContentBrowserClient {
+    : public AdblockContentBrowserClient {
  public:
   bool CreateThreadPool(base::StringPiece name) override {
     base::test::TaskEnvironment::CreateThreadPool();
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -2,6 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// This source code is a part of eyeo Chromium SDK.
+// Use of this source code is governed by the GPLv3 that can be found in the
+// components/adblock/LICENSE file.
+
 #include "chrome/test/base/in_process_browser_test.h"
 
 #include 
@@ -163,6 +167,10 @@
 #include "content/public/test/network_connection_change_simulator.h"
 #endif
 
+#include "chrome/browser/adblock/subscription_service_factory.h"
+#include "components/adblock/core/common/adblock_constants.h"
+#include "components/adblock/core/subscription/subscription_service.h"
+
 namespace {
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -653,8 +661,19 @@ void InProcessBrowserTest::CreatedBrowserMainParts(
 
 void InProcessBrowserTest::SelectFirstBrowser() {
   const BrowserList* browser_list = BrowserList::GetInstance();
-  if (!browser_list->empty())
+  if (!browser_list->empty()) {
     browser_ = browser_list->get(0);
+    // Adding an allowing filter that overrides and disables all blocking
+    // filters in order to avoid unwanted interactions with simulated network
+    // loads. This custom filter is removed for tests that specifically verify
+    // ad-filtering.
+    auto* adblock_configuration =
+        adblock::SubscriptionServiceFactory::GetForBrowserContext(browser_->profile())
+            ->GetAdblockFilteringConfiguration();
+    if (adblock_configuration) {
+      adblock_configuration->AddCustomFilter(adblock::kAllowlistEverythingFilter);
+    }
+  }
 }
 
 void InProcessBrowserTest::RecordPropertyFromMap(
diff --git a/chrome/test/data/adblock/filterlist_that_allows_resource.txt b/chrome/test/data/adblock/filterlist_that_allows_resource.txt
new file mode 100644
--- /dev/null
+++ b/chrome/test/data/adblock/filterlist_that_allows_resource.txt
@@ -0,0 +1,7 @@
+  [Adblock Plus 2.0]
+! Version: 202212191158
+! Title: Allow resource.png
+! Expires: 1 days
+
+@@*resource.png
+#@##subresource
diff --git a/chrome/test/data/adblock/filterlist_that_blocks_resource.txt b/chrome/test/data/adblock/filterlist_that_blocks_resource.txt
new file mode 100644
--- /dev/null
+++ b/chrome/test/data/adblock/filterlist_that_blocks_resource.txt
@@ -0,0 +1,6 @@
+ [Adblock Plus 2.0]
+! Version: 202212191158
+! Title: Block resource.png
+! Expires: 1 days
+
+*resource.png
diff --git a/chrome/test/data/adblock/filterlist_that_hides_resource.txt b/chrome/test/data/adblock/filterlist_that_hides_resource.txt
new file mode 100644
--- /dev/null
+++ b/chrome/test/data/adblock/filterlist_that_hides_resource.txt
@@ -0,0 +1,6 @@
+ [Adblock Plus 2.0]
+! Version: 202212191158
+! Title: Hide resource.png
+! Expires: 1 days
+
+###subresource
diff --git a/chrome/test/data/adblock/innermost_frame.html b/chrome/test/data/adblock/innermost_frame.html
new file mode 100644
--- /dev/null
+++ b/chrome/test/data/adblock/innermost_frame.html
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+  
+
+
+
+  
+
+
+
diff --git a/chrome/test/data/adblock/middle_frame.html b/chrome/test/data/adblock/middle_frame.html
new file mode 100644
--- /dev/null
+++ b/chrome/test/data/adblock/middle_frame.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+  
+
+
+
diff --git a/chrome/test/data/adblock/non-ascii.html b/chrome/test/data/adblock/non-ascii.html
new file mode 100644
--- /dev/null
+++ b/chrome/test/data/adblock/non-ascii.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+  
+
+
+    
Should be shidden
+ + diff --git a/chrome/test/data/adblock/outermost_frame.html b/chrome/test/data/adblock/outermost_frame.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/outermost_frame.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + diff --git a/chrome/test/data/adblock/outermost_frame_with_about_blank.html b/chrome/test/data/adblock/outermost_frame_with_about_blank.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/outermost_frame_with_about_blank.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + diff --git a/chrome/test/data/adblock/popup.html b/chrome/test/data/adblock/popup.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/popup.html @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/chrome/test/data/adblock/popup_opener.html b/chrome/test/data/adblock/popup_opener.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/popup_opener.html @@ -0,0 +1,26 @@ + + + + + + + Trigger link based popup + + + + diff --git a/chrome/test/data/adblock/popup_parent.html b/chrome/test/data/adblock/popup_parent.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/popup_parent.html @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/chrome/test/data/adblock/resource.png b/chrome/test/data/adblock/resource.png new file mode 100644 index 0000000000000000000000000000000000000000..39f3630072b2677debf4aac66b20096852453a7f GIT binary patch literal 47293 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

7cOc&DF@xl^-3}B(kApb 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�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 KkuH-o3HTopq;ALn literal 0 HcmV?d00001 diff --git a/chrome/test/data/adblock/tab-restore.html b/chrome/test/data/adblock/tab-restore.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/tab-restore.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + diff --git a/chrome/test/data/adblock/wbn/LICENSE b/chrome/test/data/adblock/wbn/LICENSE new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/LICENSE @@ -0,0 +1,14 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . diff --git a/chrome/test/data/adblock/wbn/blocked_bundle/LICENSE b/chrome/test/data/adblock/wbn/blocked_bundle/LICENSE new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/blocked_bundle/LICENSE @@ -0,0 +1,14 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . diff --git a/chrome/test/data/adblock/wbn/blocked_bundle/fetch_result_1_blocked_bundle.json b/chrome/test/data/adblock/wbn/blocked_bundle/fetch_result_1_blocked_bundle.json new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/blocked_bundle/fetch_result_1_blocked_bundle.json @@ -0,0 +1 @@ +{ "method": "fetch", "result": 1 } diff --git a/chrome/test/data/adblock/wbn/blocked_bundle/fetch_result_2_blocked_bundle.json b/chrome/test/data/adblock/wbn/blocked_bundle/fetch_result_2_blocked_bundle.json new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/blocked_bundle/fetch_result_2_blocked_bundle.json @@ -0,0 +1 @@ +{ "method": "fetch", "result": 2 } diff --git a/chrome/test/data/adblock/wbn/by_bundle_file/LICENSE b/chrome/test/data/adblock/wbn/by_bundle_file/LICENSE new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/by_bundle_file/LICENSE @@ -0,0 +1,14 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . diff --git a/chrome/test/data/adblock/wbn/by_bundle_file/fetch_result_2_subresource_loading.json b/chrome/test/data/adblock/wbn/by_bundle_file/fetch_result_2_subresource_loading.json new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/by_bundle_file/fetch_result_2_subresource_loading.json @@ -0,0 +1 @@ +{ "method": "fetch", "result": 2 } diff --git a/chrome/test/data/adblock/wbn/by_bundle_file/green_subresource_loading.css b/chrome/test/data/adblock/wbn/by_bundle_file/green_subresource_loading.css new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/by_bundle_file/green_subresource_loading.css @@ -0,0 +1 @@ +.green { background-color: green } diff --git a/chrome/test/data/adblock/wbn/by_bundle_file/green_subresource_loading.png b/chrome/test/data/adblock/wbn/by_bundle_file/green_subresource_loading.png new file mode 100644 index 0000000000000000000000000000000000000000..2040a0a2b2cd006f3ade7be290ff37ad8fd0452e GIT binary patch literal 772 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911L)MWvCLm. diff --git a/chrome/test/data/adblock/wbn/by_resource/blue_subresource_loading.css b/chrome/test/data/adblock/wbn/by_resource/blue_subresource_loading.css new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/by_resource/blue_subresource_loading.css @@ -0,0 +1 @@ +.blue { background-color: blue } diff --git a/chrome/test/data/adblock/wbn/by_resource/blue_subresource_loading.png b/chrome/test/data/adblock/wbn/by_resource/blue_subresource_loading.png new file mode 100644 index 0000000000000000000000000000000000000000..7a0773bec90118a4a83069ee0900449dcc511a49 GIT binary patch literal 773 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911L)MWvCLmZZ}T. diff --git a/chrome/test/data/adblock/wbn/by_scope/fetch_result_3_subresource_loading.json b/chrome/test/data/adblock/wbn/by_scope/fetch_result_3_subresource_loading.json new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/by_scope/fetch_result_3_subresource_loading.json @@ -0,0 +1 @@ +{ "method": "fetch", "result": 3 } diff --git a/chrome/test/data/adblock/wbn/by_scope/orange_subresource_loading.css b/chrome/test/data/adblock/wbn/by_scope/orange_subresource_loading.css new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/by_scope/orange_subresource_loading.css @@ -0,0 +1 @@ +.orange { background-color: orange } diff --git a/chrome/test/data/adblock/wbn/by_scope/orange_subresource_loading.png b/chrome/test/data/adblock/wbn/by_scope/orange_subresource_loading.png new file mode 100644 index 0000000000000000000000000000000000000000..348f1275fa97592546d48a3f5a339625c231ba2e GIT binary patch literal 774 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911L)MWvCLmkF2lAmzQji)bo59o7&t;ucLK6V>Gl+EDj>gTe~DWM4fjk(;R literal 0 HcmV?d00001 diff --git a/chrome/test/data/adblock/wbn/by_scope/xhr_result_3_subresource_loading.json b/chrome/test/data/adblock/wbn/by_scope/xhr_result_3_subresource_loading.json new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/by_scope/xhr_result_3_subresource_loading.json @@ -0,0 +1 @@ +{ "method": "xhr", "result": 3 } diff --git a/chrome/test/data/adblock/wbn/index.html.mustache b/chrome/test/data/adblock/wbn/index.html.mustache new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/wbn/index.html.mustache @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + +

Chromium Web bundles test

+ +

Identified by resource

+ +
    +
  • Resources in by_resource.wbn +
      +
    • Shows blue box with by_resource/blue_subresource_loading.css, grey box after the css blocked: +
    • +
    • Shows by_resource/blue_subresource_loading.png, nothing after the png blocked: +
    • +
    • Shows red box with by_resource/red_subresource_loading.css, grey box after the css blocked: +
    • +
    • Shows by_resource/red_subresource_loading.png, nothing after the png blocked: +
    • +
    • Shows xhr result for by_resource/xhr_result_1_subresource_loading.json: +
      Pending
    • +
    • Shows fetch result for by_resource/fetch_result_1_subresource_loading.json: +
      Pending
    • +
    +
  • +
+ +

Identified by bundle file

+ +
    +
  • Resources in by_bundle_file.wbn (Please click here to check fetch behavior with blocked bundle) +
      +
    • Shows green box with by_bundle_file/green_subresource_loading.css, grey box after the css blocked: +
    • +
    • Shows by_bundle_file/green_subresource_loading.png, nothing after the png blocked: +
    • +
    • Shows purple box with by_bundle_file/purple_subresource_loading.css, grey box after the css blocked: +
    • +
    • Shows by_bundle_file/purple_subresource_loading.png, nothing after the png blocked: +
    • +
    • Shows xhr result for by_bundle_file/xhr_result_2_subresource_loading.json: +
      Pending
    • +
    • Shows fetch result for by_bundle_file/fetch_result_2_subresource_loading.json: +
      Pending
    • +
    +
  • +
+ +

Identified by scope

+ +
    +
  • Resources in by_scope.wbn +
      +
    • Shows orange box with by_scope/orange_subresource_loading.css, grey box after the css blocked: +
    • +
    • Shows by_scope/orange_subresource_loading.png, nothing after the png blocked: +
    • +
    • Shows pink box with by_scope/pink_subresource_loading.css, grey box after the css blocked: +
    • +
    • Shows by_scope/pink_subresource_loading.png, nothing after the png blocked: +
    • +
    • Shows xhr result for by_scope/xhr_result_3_subresource_loading.json: +
      Pending
    • +
    • Shows fetch result for by_scope/fetch_result_3_subresource_loading.json: +
      Pending
    • +
    +
  • +
+ +

+To navigate to {{{navigation_to_unsigned_bundles_baseUrl}}} within a bundle file, download the bundle file by clicking here. +

+ + diff --git a/chrome/test/data/adblock/xpath3.html b/chrome/test/data/adblock/xpath3.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/adblock/xpath3.html @@ -0,0 +1,23 @@ + + + + + +
Target
+ + -- 2.25.1