LeOSium_webview/LeOS/patches/00Eyeo-Adblock-Remove-Priva...

12065 lines
510 KiB
Diff
Raw Normal View History

2023-11-18 11:46:19 +01:00
From: uazo <uazo@users.noreply.github.com>
Date: Thu, 29 Sep 2022 11:27:35 +0000
Subject: Eyeo Adblock for Bromite
Change the normal behaviour of Eyeo Chromium SDK to
suit Bromite logic
---
.../android/java/res/xml/main_preferences.xml | 19 +-
chrome/browser/BUILD.gn | 2 -
.../adblock/adblock_content_browser_client.cc | 26 +-
.../adblock_private/adblock_private_api.cc | 74 +-
.../api/adblock_private/adblock_private_api.h | 49 +
.../eyeo_filtering_private_api.cc | 25 +-
...hrome_browser_main_extra_parts_profiles.cc | 2 -
chrome/browser/resources/settings/BUILD.gn | 2 +
.../settings/adblock_page/adblock_page.html | 207 +
.../settings/adblock_page/adblock_page.ts | 286 ++
.../settings/basic_page/basic_page.html | 7 +
.../settings/basic_page/basic_page.ts | 1 +
.../resources/settings/page_visibility.ts | 1 +
chrome/browser/resources/settings/route.ts | 4 +
chrome/browser/resources/settings/router.ts | 1 +
chrome/browser/resources/settings/settings.ts | 1 +
.../settings/settings_menu/settings_menu.html | 5 +
chrome/browser/ui/tab_helpers.cc | 6 +-
.../adblock_internals_page_handler_impl.cc | 20 +-
.../extensions/api/_permission_features.json | 8 -
.../common/extensions/api/adblock_private.idl | 8 +
chrome/test/BUILD.gn | 5 -
components/adblock/android/BUILD.gn | 3 +-
components/adblock/android/adblock_jni.cc | 53 +-
.../adblock/android/adblock_strings.grd | 54 +-
.../java/res/layout/adblock_custom_item.xml | 14 +-
.../layout/adblock_filter_lists_list_item.xml | 15 +
...ences.xml => eyeo_adblock_preferences.xml} | 39 +-
.../components/adblock/AdblockController.java | 51 +-
.../AdblockAllowedDomainsFragment.java | 7 +-
.../AdblockCustomFilterListsFragment.java | 32 +-
.../AdblockCustomFiltersFragment.java | 7 +-
.../settings/AdblockCustomItemFragment.java | 20 +-
.../settings/AdblockFilterListsAdapter.java | 6 +
.../settings/AdblockSettingsFragment.java | 45 +-
components/adblock/content/browser/BUILD.gn | 18 +-
.../adblock_controller_factory_base.cc | 5 +-
.../browser/adblock_webcontents_observer.cc | 25 +-
.../browser/adblock_webcontents_observer.h | 5 +-
.../content_security_policy_injector_impl.cc | 1 +
.../content/browser/element_hider_impl.cc | 4 +-
.../subscription_service_factory_base.cc | 11 +-
.../subscription_service_factory_base.h | 1 +
components/adblock/core/BUILD.gn | 39 -
.../activeping_telemetry_topic_provider.cc | 285 --
.../activeping_telemetry_topic_provider.h | 87 -
.../adblock/core/adblock_controller_impl.cc | 10 +-
components/adblock/core/adblock_switches.cc | 1 -
components/adblock/core/adblock_switches.h | 1 -
.../adblock/core/adblock_telemetry_service.cc | 258 --
.../adblock/core/adblock_telemetry_service.h | 120 -
components/adblock/core/common/BUILD.gn | 8 -
.../adblock/core/common/adblock_constants.cc | 2 -
.../adblock/core/common/adblock_constants.h | 1 -
.../adblock/core/common/adblock_prefs.cc | 47 +-
.../adblock/core/common/adblock_prefs.h | 1 +
.../adblock/core/common/adblock_utils.cc | 23 -
.../configuration/filtering_configuration.h | 3 +
.../persistent_filtering_configuration.cc | 10 +
.../persistent_filtering_configuration.h | 3 +
.../core/converter/flatbuffer_converter.cc | 2 +-
.../adblock/core/converter/parser/metadata.cc | 10 +-
.../core/converter/parser/test/test_rules.txt | 21 +
.../core/converter/parser/url_filter.cc | 10 +-
.../converter/parser/url_filter_options.cc | 21 +-
.../serializer/flatbuffer_serializer.cc | 75 +-
.../adblock/core/sitekey_storage_impl.cc | 7 +
.../core/subscription/conversion_executors.h | 1 +
.../filtering_configuration_maintainer.h | 4 +
...filtering_configuration_maintainer_impl.cc | 31 +-
.../filtering_configuration_maintainer_impl.h | 4 +-
.../ongoing_subscription_request_impl.cc | 33 +-
.../preloaded_subscription_provider_impl.cc | 4 +-
.../adblock/core/subscription/subscription.cc | 19 +
.../adblock/core/subscription/subscription.h | 3 +
.../subscription_collection_impl.cc | 1 +
.../core/subscription/subscription_config.cc | 20 +-
.../core/subscription/subscription_config.h | 6 +-
.../subscription_downloader_impl.cc | 21 +-
.../subscription_persistent_metadata.h | 1 +
.../subscription_persistent_metadata_impl.cc | 7 +
.../subscription_persistent_metadata_impl.h | 1 +
.../subscription_persistent_storage_impl.cc | 11 +-
.../core/subscription/subscription_service.h | 9 +
.../subscription/subscription_service_impl.cc | 78 +-
.../subscription/subscription_service_impl.h | 9 +
.../subscription/subscription_updater_impl.cc | 8 +-
.../subscription_validator_impl.cc | 4 +-
.../browser/bromite_content_settings/ads.inc | 3 +
components/resources/BUILD.gn | 1 -
components/resources/adblock_resources.grdp | 3 -
components/resources/adblocking/.gitignore | 2 +-
components/resources/adblocking/BUILD.gn | 30 +-
.../adblocking/elemhide_for_selector.jst | 2 +-
.../resources/adblocking/elemhideemu.jst | 2 +
.../snippets/dist/isolated-first-xpath3.jst | 62 +
.../dist/isolated-first-xpath3.source.jst | 3696 +++++++++++++++++
.../snippets/dist/isolated-first.jst | 65 +
.../snippets/dist/isolated-first.source.jst | 3624 ++++++++++++++++
.../blink/renderer/core/css/style_engine.cc | 8 +
.../blink/renderer/core/css/style_engine.h | 1 +
.../renderer/core/exported/web_document.cc | 15 +-
.../definitions/adblock_private.d.ts | 14 +
103 files changed, 8857 insertions(+), 1166 deletions(-)
create mode 100644 chrome/browser/resources/settings/adblock_page/adblock_page.html
create mode 100644 chrome/browser/resources/settings/adblock_page/adblock_page.ts
rename components/adblock/android/java/res/xml/{adblock_preferences.xml => eyeo_adblock_preferences.xml} (56%)
delete mode 100644 components/adblock/core/activeping_telemetry_topic_provider.cc
delete mode 100644 components/adblock/core/activeping_telemetry_topic_provider.h
delete mode 100644 components/adblock/core/adblock_telemetry_service.cc
delete mode 100644 components/adblock/core/adblock_telemetry_service.h
create mode 100644 components/adblock/core/converter/parser/test/test_rules.txt
create mode 100644 components/content_settings/core/browser/bromite_content_settings/ads.inc
create mode 100644 components/resources/adblocking/snippets/dist/isolated-first-xpath3.jst
create mode 100644 components/resources/adblocking/snippets/dist/isolated-first-xpath3.source.jst
create mode 100755 components/resources/adblocking/snippets/dist/isolated-first.jst
create mode 100755 components/resources/adblocking/snippets/dist/isolated-first.source.jst
diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
--- a/chrome/android/java/res/xml/main_preferences.xml
+++ b/chrome/android/java/res/xml/main_preferences.xml
@@ -1,13 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright 2015 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.
--->
-
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orderingFromXml="false">
@@ -75,6 +66,11 @@ Use of this source code is governed by the GPLv3 that can be found in the compon
android:key="safety_check"
android:order="12"
android:title="@string/prefs_safety_check"/>
+ <Preference
+ android:fragment="org.chromium.components.adblock.settings.AdblockSettingsFragment"
+ android:key="eyeo_adblock"
+ android:order="13"
+ android:title="@string/adblock_settings_title" />
<Preference
android:key="notifications"
android:order="13"
@@ -104,11 +100,6 @@ Use of this source code is governed by the GPLv3 that can be found in the compon
android:key="accessibility"
android:order="18"
android:title="@string/prefs_accessibility"/>
- <Preference
- android:fragment="org.chromium.components.adblock.settings.AdblockSettingsFragment"
- android:key="adblock"
- android:order="18"
- android:title="@string/adblock_settings_title" />
<Preference
android:fragment="org.chromium.components.browser_ui.site_settings.SiteSettings"
android:key="content_settings"
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -165,8 +165,6 @@ static_library("browser") {
"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",
diff --git a/chrome/browser/adblock/adblock_content_browser_client.cc b/chrome/browser/adblock/adblock_content_browser_client.cc
--- a/chrome/browser/adblock/adblock_content_browser_client.cc
+++ b/chrome/browser/adblock/adblock_content_browser_client.cc
@@ -24,6 +24,8 @@
#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/content_settings/host_content_settings_map_factory.h"
+#include "components/content_settings/core/browser/host_content_settings_map.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"
@@ -59,6 +61,13 @@ bool IsFilteringNeeded(content::RenderFrameHost* frame) {
auto* profile =
Profile::FromBrowserContext(frame->GetProcess()->GetBrowserContext());
if (profile) {
+ content::RenderFrameHost* embedder = frame->GetOutermostMainFrameOrEmbedder();
+ const auto& embedder_url = embedder->GetLastCommittedURL();
+ HostContentSettingsMap* settings_map = HostContentSettingsMapFactory::GetForProfile(profile);
+ if (settings_map && settings_map->GetContentSetting(embedder_url, GURL(), ContentSettingsType::ADS)
+ == CONTENT_SETTING_ALLOW) {
+ return false;
+ }
// Filtering may be needed if there's at least one enabled
// FilteringConfiguration.
return base::ranges::any_of(
@@ -102,23 +111,6 @@ class AdblockContextData : public base::SupportsUserData::Data {
adblock::SitekeyStorageFactory::GetForBrowserContext(browser_context),
adblock::ContentSecurityPolicyInjectorFactory::GetForBrowserContext(
browser_context)};
-#ifdef EYEO_INTERCEPT_DEBUG_URL
- if (use_test_loader) {
- auto proxy = std::make_unique<adblock::AdblockURLLoaderFactoryForTest>(
- 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<adblock::AdblockURLLoaderFactory>(
std::move(config),
content::GlobalRenderFrameHostId(render_process_id,
diff --git a/chrome/browser/extensions/api/adblock_private/adblock_private_api.cc b/chrome/browser/extensions/api/adblock_private/adblock_private_api.cc
--- a/chrome/browser/extensions/api/adblock_private/adblock_private_api.cc
+++ b/chrome/browser/extensions/api/adblock_private/adblock_private_api.cc
@@ -18,7 +18,8 @@
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/no_destructor.h"
-#include "base/time/time_to_iso8601.h"
+#include "base/i18n/time_formatting.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/adblock/resource_classification_runner_factory.h"
#include "chrome/browser/adblock/session_stats_factory.h"
@@ -77,26 +78,12 @@ std::vector<api::adblock_private::SessionStatsEntry> CopySessionsStats(
return result;
}
-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::vector<api::adblock_private::Subscription> CopySubscriptions(
+ raw_ptr<adblock::SubscriptionService> subscription_service,
const std::vector<scoped_refptr<adblock::Subscription>>
current_subscriptions) {
+ raw_ptr<adblock::SubscriptionPersistentMetadata> metadata =
+ subscription_service->GetMetadata();
std::vector<api::adblock_private::Subscription> result;
for (auto& sub : current_subscriptions) {
api::adblock_private::Subscription js_sub;
@@ -104,9 +91,13 @@ std::vector<api::adblock_private::Subscription> CopySubscriptions(
js_sub.title = sub->GetTitle();
js_sub.current_version = sub->GetCurrentVersion();
js_sub.installation_state =
- SubscriptionInstallationStateToString(sub->GetInstallationState());
+ adblock::Subscription::SubscriptionInstallationStateToString(sub->GetInstallationState());
js_sub.last_installation_time =
- base::TimeToISO8601(sub->GetInstallationTime());
+ base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(sub->GetInstallationTime()));
+ if (metadata) {
+ js_sub.download_success_count = metadata->GetDownloadSuccessCount(sub->GetSourceUrl());
+ js_sub.download_error_count = metadata->GetDownloadErrorCount(sub->GetSourceUrl());
+ }
result.emplace_back(std::move(js_sub));
}
return result;
@@ -347,6 +338,47 @@ void AdblockPrivateAPI::OnListenerAdded(
namespace api {
+AdblockPrivateStartUpdateFunction::AdblockPrivateStartUpdateFunction() {}
+
+AdblockPrivateStartUpdateFunction::~AdblockPrivateStartUpdateFunction() {}
+
+ExtensionFunction::ResponseAction AdblockPrivateStartUpdateFunction::Run() {
+ auto* subscription_service =
+ adblock::SubscriptionServiceFactory::GetForBrowserContext(
+ browser_context());
+ subscription_service->StartUpdate();
+ return RespondNow(NoArguments());
+}
+
+AdblockPrivateSetPrivilegedFiltersEnabledFunction::AdblockPrivateSetPrivilegedFiltersEnabledFunction() {}
+
+AdblockPrivateSetPrivilegedFiltersEnabledFunction::~AdblockPrivateSetPrivilegedFiltersEnabledFunction() {}
+
+ExtensionFunction::ResponseAction AdblockPrivateSetPrivilegedFiltersEnabledFunction::Run() {
+ absl::optional<api::adblock_private::SetEnabled::Params> params =
+ api::adblock_private::SetEnabled::Params::Create(args());
+ EXTENSION_FUNCTION_VALIDATE(params);
+
+ auto* subscription_service =
+ adblock::SubscriptionServiceFactory::GetForBrowserContext(
+ browser_context());
+ subscription_service->SetPrivilegedFiltersEnabled(params->enabled);
+ return RespondNow(NoArguments());
+}
+
+AdblockPrivateIsPrivilegedFiltersEnabledFunction::AdblockPrivateIsPrivilegedFiltersEnabledFunction() {}
+
+AdblockPrivateIsPrivilegedFiltersEnabledFunction::~AdblockPrivateIsPrivilegedFiltersEnabledFunction() {}
+
+ExtensionFunction::ResponseAction AdblockPrivateIsPrivilegedFiltersEnabledFunction::Run() {
+ auto* subscription_service =
+ adblock::SubscriptionServiceFactory::GetForBrowserContext(
+ browser_context());
+ return RespondNow(
+ ArgumentList(api::adblock_private::IsEnabled::Results::Create(
+ subscription_service->IsPrivilegedFiltersEnabled())));
+}
+
AdblockPrivateSetEnabledFunction::AdblockPrivateSetEnabledFunction() {}
AdblockPrivateSetEnabledFunction::~AdblockPrivateSetEnabledFunction() {}
@@ -514,7 +546,7 @@ AdblockPrivateGetInstalledSubscriptionsFunction::Run() {
<< "adblock_private expects \"adblock\" configuration";
return RespondNow(ArgumentList(
api::adblock_private::GetInstalledSubscriptions::Results::Create(
- CopySubscriptions(subscription_service->GetCurrentSubscriptions(
+ CopySubscriptions(subscription_service, subscription_service->GetCurrentSubscriptions(
subscription_service->GetAdblockFilteringConfiguration())))));
}
diff --git a/chrome/browser/extensions/api/adblock_private/adblock_private_api.h b/chrome/browser/extensions/api/adblock_private/adblock_private_api.h
--- a/chrome/browser/extensions/api/adblock_private/adblock_private_api.h
+++ b/chrome/browser/extensions/api/adblock_private/adblock_private_api.h
@@ -58,6 +58,55 @@ void BrowserContextKeyedAPIFactory<
namespace api {
+class AdblockPrivateStartUpdateFunction : public ExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("adblockPrivate.startUpdate", UNKNOWN)
+ AdblockPrivateStartUpdateFunction();
+
+ private:
+ ~AdblockPrivateStartUpdateFunction() override;
+
+ ResponseAction Run() override;
+
+ AdblockPrivateStartUpdateFunction(const AdblockPrivateStartUpdateFunction&) =
+ delete;
+ AdblockPrivateStartUpdateFunction& operator=(
+ const AdblockPrivateStartUpdateFunction&) = delete;
+};
+
+
+class AdblockPrivateSetPrivilegedFiltersEnabledFunction : public ExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("adblockPrivate.setPrivilegedFiltersEnabled", UNKNOWN)
+ AdblockPrivateSetPrivilegedFiltersEnabledFunction();
+
+ private:
+ ~AdblockPrivateSetPrivilegedFiltersEnabledFunction() override;
+
+ ResponseAction Run() override;
+
+ AdblockPrivateSetPrivilegedFiltersEnabledFunction(const AdblockPrivateSetPrivilegedFiltersEnabledFunction&) =
+ delete;
+ AdblockPrivateSetPrivilegedFiltersEnabledFunction& operator=(
+ const AdblockPrivateSetPrivilegedFiltersEnabledFunction&) = delete;
+};
+
+class AdblockPrivateIsPrivilegedFiltersEnabledFunction : public ExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("adblockPrivate.isPrivilegedFiltersEnabled", UNKNOWN)
+ AdblockPrivateIsPrivilegedFiltersEnabledFunction();
+
+ private:
+ ~AdblockPrivateIsPrivilegedFiltersEnabledFunction() override;
+
+ ResponseAction Run() override;
+
+ AdblockPrivateIsPrivilegedFiltersEnabledFunction(const AdblockPrivateIsPrivilegedFiltersEnabledFunction&) =
+ delete;
+ AdblockPrivateIsPrivilegedFiltersEnabledFunction& operator=(
+ const AdblockPrivateIsPrivilegedFiltersEnabledFunction&) = delete;
+};
+
class AdblockPrivateSetEnabledFunction : public ExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("adblockPrivate.setEnabled", UNKNOWN)
diff --git a/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.cc b/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.cc
--- a/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.cc
+++ b/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.cc
@@ -18,7 +18,8 @@
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/no_destructor.h"
-#include "base/time/time_to_iso8601.h"
+#include "base/i18n/time_formatting.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/adblock/resource_classification_runner_factory.h"
#include "chrome/browser/adblock/session_stats_factory.h"
@@ -29,6 +30,7 @@
#include "components/adblock/content/browser/resource_classification_runner.h"
#include "components/adblock/core/common/adblock_utils.h"
#include "components/adblock/core/common/content_type.h"
+#include "components/adblock/core/subscription/subscription.h"
#include "components/adblock/core/configuration/filtering_configuration.h"
#include "components/adblock/core/configuration/persistent_filtering_configuration.h"
#include "components/adblock/core/session_stats.h"
@@ -94,23 +96,6 @@ std::vector<api::eyeo_filtering_private::SessionStatsEntry> CopySessionsStats(
return result;
}
-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::vector<api::eyeo_filtering_private::Subscription> CopySubscriptions(
const std::vector<scoped_refptr<adblock::Subscription>>
current_subscriptions) {
@@ -121,9 +106,9 @@ std::vector<api::eyeo_filtering_private::Subscription> CopySubscriptions(
js_sub.title = sub->GetTitle();
js_sub.current_version = sub->GetCurrentVersion();
js_sub.installation_state =
- SubscriptionInstallationStateToString(sub->GetInstallationState());
+ adblock::Subscription::SubscriptionInstallationStateToString(sub->GetInstallationState());
js_sub.last_installation_time =
- base::TimeToISO8601(sub->GetInstallationTime());
+ base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(sub->GetInstallationTime()));
result.emplace_back(std::move(js_sub));
}
return result;
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -17,7 +17,6 @@
#include "chrome/browser/accessibility/accessibility_labels_service_factory.h"
#include "chrome/browser/accessibility/page_colors_factory.h"
#include "chrome/browser/adblock/adblock_controller_factory.h"
-#include "chrome/browser/adblock/adblock_telemetry_service_factory.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"
@@ -756,7 +755,6 @@ void ChromeBrowserMainExtraPartsProfiles::
ExitTypeServiceFactory::GetInstance();
#endif
adblock::AdblockControllerFactory::GetInstance();
- adblock::AdblockTelemetryServiceFactory::GetInstance();
adblock::ContentSecurityPolicyInjectorFactory::GetInstance();
adblock::ElementHiderFactory::GetInstance();
adblock::ResourceClassificationRunnerFactory::GetInstance();
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -74,6 +74,7 @@ build_webui("build") {
web_component_files = [
"a11y_page/a11y_page.ts",
"about_page/about_page.ts",
+ "adblock_page/adblock_page.ts",
"appearance_page/appearance_fonts_page.ts",
"appearance_page/appearance_page.ts",
"appearance_page/home_url_input.ts",
@@ -372,6 +373,7 @@ build_webui("build") {
ts_composite = true
ts_definitions = [
"//tools/typescript/definitions/autofill_private.d.ts",
+ "//tools/typescript/definitions/adblock_private.d.ts",
"//tools/typescript/definitions/chrome_send.d.ts",
"//tools/typescript/definitions/language_settings_private.d.ts",
"//tools/typescript/definitions/management.d.ts",
diff --git a/chrome/browser/resources/settings/adblock_page/adblock_page.html b/chrome/browser/resources/settings/adblock_page/adblock_page.html
new file mode 100644
--- /dev/null
+++ b/chrome/browser/resources/settings/adblock_page/adblock_page.html
@@ -0,0 +1,207 @@
+<!--
+ This file is part of Bromite.
+
+ Bromite is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bromite 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 Bromite. If not, see <https://www.gnu.org/licenses/>.
+-->
+
+<style include="settings-shared cr-shared-style settings-shared action-link iron-flex">
+ #languagesCollapse .list-item.selected {
+ min-height: var(--settings-row-two-line-min-height);
+ }
+</style>
+<settings-toggle-button class="hr" pref="{{prefs.adblock.enabled}}"
+ on-change="onAdblockEnabled_"
+ label="Enable Adblock Plus" data-qa="adblock-toogle"
+ sub-label-icon="[[additionalMessageIcon]]"
+ sub-label="[[additionalMessage]]">
+</settings-toggle-button>
+
+<div hidden$="[[!prefs.adblock.enabled.value]]">
+
+ <div class="cr-row first">
+ <div class="flex cr-padded-text">
+ <div>Enable anti-circumvention and snippets</div>
+ <div class="cr-secondary-text">
+ Snippets are pieces of JavaScript code, injected by the Adblock Plus, that execute within the context of a website and combat advanced ads that circumvent ordinary blocking.
+ The functionality is ONLY allowed for the list
+ <a href="https://easylist-downloads.adblockplus.org/abp-filters-anti-cv.txt">abp-filters-anti-cv.txt</a>
+ which is activated by this setting.
+ <br><a href="https://github.com/abp-filters/abp-filters-anti-cv">Open ABP anti-circumvention filter list repo</a>
+ <br><a href="https://developers.eyeo.com/snippets/snippets-overview">Open ABP Snippets Overview</a>
+ </div>
+ </div>
+ <div class="separator"></div>
+ <cr-toggle id="enablePrivilegedFiltersToggle"
+ checked="[[isEnablePrivilegedFiltersToggle_]]"
+ on-change="onEnablePrivilegedFiltersToggle_">
+ </cr-toggle>
+ </div>
+
+ <div class="cr-row first">
+ <div class="flex cr-padded-text">
+ </div>
+ <cr-button class="button" on-click="startUpdateCycle">
+ Check for updates now
+ </cr-button>
+ </div>
+
+ <div style="border-top: 1px solid silver">
+ <cr-expand-button class="cr-row first" expanded="{{recommendedSubscriptions}}" data-qa="recommended-subscriptions-menu-item">
+ <div>
+ <div>Built in Subscriptions ([[countEnabled]] selected)</div>
+ <div class="cr-secondary-text">
+ Add the languages in which you regularly browse websites in
+ </div>
+ </div>
+ </cr-expand-button>
+ <iron-collapse opened="{{recommendedSubscriptions}}">
+ <div class="list-frame vertical-list">
+ <template is="dom-repeat" items="[[subscriptions]]">
+ <div class="list-item">
+ <cr-checkbox id="[[item.url]]" no-set-pref on-change="selectRecommendedSubscription"
+ checked="{{item.enabled}}">
+ </cr-checkbox>
+ <div class="start cr-padded-text">
+ <div data-qa="recommended-subscription-list-item">
+ [[item.title]]
+ </div>
+ <div hidden="[[item.enabled]]">
+ <div><b>Download from:</b> [[item.url]]</div>
+ </div>
+ <div hidden="[[!item.enabled]]" style="margin-left: 20px; margin-top: 5px;">
+ <div><b>URL:</b> [[item.url]]</div>
+ <div><b>Version:</b> [[item.current_version]]</div>
+ <div><b>Status:</b> [[item.installation_state]] ([[item.last_installation_time]])</div>
+ <div><b>Download errors:</b> [[item.download_error_count]]/[[item.download_success_count]]</div>
+ </div>
+ </div>
+ </div>
+ </template>
+ </div>
+ </iron-collapse>
+ </div>
+ <div>
+ <cr-expand-button class="cr-row first" expanded="{{allowedDomainsOpened}}" data-qa="allowed-domains-menu-item">
+ <div>
+ Allowed Domains ([[allowedDomainsCount]] added)
+ </div>
+ <div class="cr-secondary-text">
+ Support your favorite websites by adding them to this list. You might see ads on them.
+ </div>
+ </cr-expand-button>
+ <iron-collapse opened="{{allowedDomainsOpened}}">
+ <div class="list-frame vertical-list">
+ <template is="dom-repeat" items="{{allowedDomains}}">
+ <div class="list-item">
+ <div class="start cr-padded-text">
+ <div data-qa="allowed-domains-list-item">
+ [[item]]
+ </div>
+ </div>
+ <cr-button class="button" id="[[item]]" on-click="removeAllowedDomain" data-qa="remove-allowed-domains-btn">
+ Remove
+ </cr-button>
+ </div>
+ </template>
+ <div class="list-item">
+ <div class="start cr-padded-text">
+ <cr-input slot="inputs" value="{{allowedDomainInput}}" data-qa="allowed-domains-input">
+ <cr-button slot="suffix" class="action-button" id="[[item]]" on-click="addAllowedDomain" data-qa="add-allowed-domains-btn">
+ Add
+ </cr-button>
+ </cr-input>
+ </div>
+ </div>
+ </div>
+ </iron-collapse>
+ </div>
+ <div>
+ <cr-expand-button class="cr-row first" expanded="{{customSubscriptionsOpened}}" data-qa="custom-subscriptions-menu-item">
+ <div>
+ Custom Subscriptions ([[customSubscriptions.length]] added)
+ </div>
+ <div class="cr-secondary-text">
+ Add custom filter urls
+ </div>
+ </cr-expand-button>
+ <iron-collapse opened="{{customSubscriptionsOpened}}">
+ <div class="list-frame vertical-list">
+ <template is="dom-repeat" items="{{customSubscriptions}}">
+ <div class="list-item">
+ <div class="start cr-padded-text">
+ <div data-qa="custom-subscriptions-list-item">
+ [[item.url]]
+ <div style="margin-left: 20px; margin-top: 5px;">
+ <div><b>Version:</b> [[item.current_version]]</div>
+ <div><b>Status:</b> [[item.installation_state]] ([[item.last_installation_time]])</div>
+ <div><b>Download errors:</b> [[item.download_error_count]]/[[item.download_success_count]]</div>
+ </div>
+ </div>
+ </div>
+ <cr-button class="button" id="[[item.url]]"
+ on-click="removeCustomSubscription" data-qa="remove-custom-subscriptions-btn">
+ Remove
+ </cr-button>
+ </div>
+ </template>
+ <div class="list-item">
+ <div class="start cr-padded-text">
+ <cr-input slot="inputs" value="{{customSubscriptionInput}}" data-qa="custom-subscriptions-input">
+ <cr-button slot="suffix" class="action-button" id="[[item]]" on-click="addCustomSubscription" data-qa="add-custom-subscription-btn">
+ Add
+ </cr-button>
+ </cr-input>
+ </div>
+ </div>
+ </div>
+ </iron-collapse>
+ </div>
+ <div>
+ <cr-expand-button class="cr-row first" expanded="{{customFiltersOpened}}" data-qa="custom-filters-menu-item">
+ <div>
+ Custom Filters ([[customFilters.length]] added)
+ </div>
+ <div class="cr-secondary-text">
+ Add custom filter commands
+ </div>
+ </cr-expand-button>
+ <iron-collapse opened="{{customFiltersOpened}}">
+ <div class="list-frame vertical-list">
+ <template is="dom-repeat" items="{{customFilters}}">
+ <div class="list-item">
+ <div class="start cr-padded-text">
+ <div data-qa="custom-filters-list-item">
+ [[item]]
+ </div>
+ </div>
+ <cr-button class="button" id="[[item]]" on-click="removeCustomFilter" data-qa="remove-custom-filter-btn">
+ Remove
+ </cr-button>
+ </div>
+ </template>
+ <div class="list-item">
+ <div class="start cr-padded-text">
+ <cr-input slot="inputs" value="{{customFilterInput}}" data-qa="custom-filter-input">
+ <cr-button slot="suffix" class="action-button" id="[[item]]" on-click="addCustomFilter" data-qa="add-custom-filter-btn">
+ Add
+ </cr-button>
+ </cr-input>
+ </div>
+ </div>
+ </div>
+ </iron-collapse>
+ </div>
+
+</div>
+<cr-toast-manager></cr-toast-manager>
diff --git a/chrome/browser/resources/settings/adblock_page/adblock_page.ts b/chrome/browser/resources/settings/adblock_page/adblock_page.ts
new file mode 100644
--- /dev/null
+++ b/chrome/browser/resources/settings/adblock_page/adblock_page.ts
@@ -0,0 +1,286 @@
+/*
+ This file is part of Bromite.
+
+ Bromite is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bromite 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 Bromite. If not, see <https://www.gnu.org/licenses/>.
+*/
+import 'chrome://resources/cr_elements/cr_button/cr_button.js';
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '../settings_shared.css.js';
+
+import { I18nMixin } from 'chrome://resources/cr_elements/i18n_mixin.js';
+import { BaseMixin } from '../base_mixin.js';
+import { PolymerElement } from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import { SettingsToggleButtonElement } from '/shared/settings/controls/settings_toggle_button.js';
+import { PrefsMixin } from 'chrome://resources/cr_components/settings_prefs/prefs_mixin.js';
+import { CrCheckboxElement } from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
+import { getTemplate } from './adblock_page.html.js';
+import { getToastManager } from 'chrome://resources/cr_elements/cr_toast/cr_toast_manager.js';
+
+const SettingsAdblockPageElementBase =
+ I18nMixin(PrefsMixin(BaseMixin((PolymerElement))));
+
+interface Subscription {
+ url: string;
+ title: string;
+ enabled: boolean;
+ current_version: string;
+ download_error_count: number;
+ download_success_count: number;
+ installation_state: string;
+ last_installation_time: string;
+}
+
+export class SettingsAdblockPageElement extends
+ SettingsAdblockPageElementBase {
+ static get is() {
+ return 'settings-adblock-page';
+ }
+
+ static get template() {
+ return getTemplate();
+ }
+
+ // input fields updated by html
+ public customSubscriptionInput: string;
+ public customFilterInput: string;
+ public allowedDomainInput: string;
+ public additionalMessage: string;
+ public additionalMessageIcon: string;
+ public countEnabled: number;
+ public allowedDomainsCount: number;
+ public isEnablePrivilegedFiltersToggle_: boolean;
+
+ // models that will fill templates lists in html
+ public customSubscriptions: Subscription[] = [];
+ public customFilters: Array<string> = [];
+ public allowedDomains: Array<string> = [];
+ public subscriptions: Subscription[] = [];
+
+ private syncSubscriptions() {
+ this.subscriptions = []
+ this.customSubscriptions = [];
+
+ chrome.adblockPrivate.getBuiltInSubscriptions(list => {
+ chrome.adblockPrivate.getInstalledSubscriptions(activelist => {
+ let new_subscriptions: Subscription[] = [];
+ let custom_subscriptions: Subscription[] = [];
+
+ list.forEach(obj => {
+ new_subscriptions.push({
+ title: obj.title,
+ enabled: false,
+ url: obj.url,
+ current_version: '',
+ download_error_count: 0,
+ download_success_count: 0,
+ installation_state: '',
+ last_installation_time: ''
+ })
+ })
+
+ activelist.forEach(obj => {
+ var found = new_subscriptions.find(element => element.url == obj.url);
+ if (found === undefined) {
+ found = {
+ title: obj.title,
+ enabled: false,
+ url: obj.url,
+ current_version: '',
+ download_error_count: 0,
+ download_success_count: 0,
+ installation_state: '',
+ last_installation_time: ''
+ }
+ custom_subscriptions.push(found)
+ }
+ found.enabled = true;
+ found.current_version = obj.current_version;
+ found.download_error_count = obj.download_error_count;
+ found.download_success_count = obj.download_success_count;
+ found.installation_state = obj.installation_state;
+ found.last_installation_time = new Date(obj.last_installation_time).toLocaleString();
+ })
+
+ this.subscriptions = new_subscriptions;
+ this.customSubscriptions = custom_subscriptions;
+ this.updateUI();
+ })
+ });
+ }
+
+ private updateUI() {
+ this.additionalMessage = "";
+ this.additionalMessageIcon = "";
+ this.countEnabled = 0;
+
+ chrome.adblockPrivate.isEnabled(enabled => {
+ if (!enabled) return;
+
+ let c = 0;
+ this.subscriptions.forEach(obj => {
+ if (obj.enabled) c++;
+ });
+ this.countEnabled = c;
+ if (this.countEnabled == 0 &&
+ this.customSubscriptions.length == 0 &&
+ this.customFilters.length == 0) {
+ this.additionalMessage = "No subscriptions selected. Adblock is not active.";
+ this.additionalMessageIcon = "cr:warning";
+ }
+ });
+
+ chrome.adblockPrivate.isPrivilegedFiltersEnabled(enabled => {
+ this.isEnablePrivilegedFiltersToggle_ = enabled;
+ });
+ }
+
+ private syncCustomFilters() {
+ chrome.adblockPrivate.getCustomFilters(domain => {
+ this.customFilters = [];
+ domain.forEach(value => {
+ this.customFilters.push(value);
+ })
+ this.updateUI();
+ });
+ }
+
+ private syncAllowedDomains() {
+ this.allowedDomainsCount = 0;
+ chrome.adblockPrivate.getAllowedDomains(domain => {
+ this.allowedDomains = [];
+ domain.forEach(value => {
+ this.allowedDomains.push(value);
+ })
+ this.allowedDomainsCount = this.allowedDomains.length;
+ this.updateUI();
+ });
+ }
+
+ public override ready() {
+ super.ready();
+
+ this.syncSubscriptions();
+ this.syncCustomFilters();
+ this.syncAllowedDomains();
+ }
+
+ private onAdblockEnabled_(event: Event) {
+ if ((event.target as SettingsToggleButtonElement).checked) {
+ chrome.adblockPrivate.setEnabled(true);
+ } else {
+ chrome.adblockPrivate.setEnabled(false);
+ }
+ this.updateUI();
+ }
+
+ private onEnablePrivilegedFiltersToggle_(_evt: any, enabled: boolean) {
+ chrome.adblockPrivate.setPrivilegedFiltersEnabled(enabled);
+ this.syncSubscriptions();
+ }
+
+ private cleanUrl(url: string) : string {
+ let cleanedUrl : string = "";
+ try {
+ cleanedUrl = new URL(url).host;
+ } catch (err) {
+ try {
+ // one last try by adding schema
+ cleanedUrl = new URL("https://" + url).host;
+ }
+ catch (err) {
+ console.log("malformed url " + url);
+ return "";
+ }
+ }
+ return cleanedUrl;
+ }
+
+ private selectRecommendedSubscription(e: Event) {
+ const url = ((e.target as CrCheckboxElement).id);
+ const enabled = ((e.target as CrCheckboxElement).checked);
+ if (enabled) {
+ chrome.adblockPrivate.installSubscription(url);
+ } else {
+ chrome.adblockPrivate.uninstallSubscription(url);
+ }
+ this.updateUI();
+ }
+
+ private removeCustomFilter(e: Event) {
+ const filter = ((e.target as HTMLElement).id);
+ chrome.adblockPrivate.removeCustomFilter(filter);
+ const i = this.customFilters.indexOf(filter);
+ this.splice('customFilters', i, 1);
+ this.updateUI();
+ }
+
+ private addCustomFilter() {
+ if (this.customFilterInput == undefined || this.customFilterInput == "") return;
+ chrome.adblockPrivate.addCustomFilter(this.customFilterInput);
+ this.customFilterInput = "";
+ this.syncCustomFilters();
+ }
+
+ private removeAllowedDomain(e: Event) {
+ const allowedDomain = ((e.target as HTMLElement).id);
+ chrome.adblockPrivate.removeAllowedDomain(allowedDomain);
+ const i = this.allowedDomains.indexOf(allowedDomain);
+ this.splice('allowedDomains', i, 1);
+ this.allowedDomainsCount = this.allowedDomains.length;
+ this.updateUI();
+ }
+
+ private addAllowedDomain() {
+ if (this.allowedDomainInput == undefined || this.allowedDomainInput == "") return;
+ const cleanedUrl = this.cleanUrl(this.allowedDomainInput);
+ if (cleanedUrl == "") return;
+ chrome.adblockPrivate.addAllowedDomain(cleanedUrl);
+ this.allowedDomainInput = "";
+ this.syncAllowedDomains();
+ }
+
+ private removeCustomSubscription(e: Event) {
+ const url = ((e.target as HTMLElement).id);
+ const subscription = this.customSubscriptions.find( x => x.url = url);
+ chrome.adblockPrivate.uninstallSubscription(subscription!.url);
+ this.splice('customSubscriptions', this.customSubscriptions.indexOf(subscription!), 1);
+ this.updateUI();
+ }
+
+ private addCustomSubscription() {
+ if (this.customSubscriptionInput == undefined || this.customSubscriptionInput == "") return;
+ chrome.adblockPrivate.installSubscription(this.customSubscriptionInput);
+ this.customSubscriptionInput = "";
+ this.syncSubscriptions();
+ }
+
+ private startUpdateCycle() {
+ const toastManager = getToastManager();
+ chrome.adblockPrivate.startUpdate();
+ toastManager.duration = 5000;
+ toastManager.show("Starting update...");
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'settings-adblock-page': SettingsAdblockPageElement;
+ }
+}
+
+customElements.define(
+ SettingsAdblockPageElement.is, SettingsAdblockPageElement);
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.html b/chrome/browser/resources/settings/basic_page/basic_page.html
--- a/chrome/browser/resources/settings/basic_page/basic_page.html
+++ b/chrome/browser/resources/settings/basic_page/basic_page.html
@@ -24,6 +24,13 @@
</settings-people-page>
</settings-section>
</template>
+ <template is="dom-if" if="[[showPage_(pageVisibility.adblock)]]"
+ restamp>
+ <settings-section page-title="Adblock"
+ section="adblock">
+ <settings-adblock-page prefs="{{prefs}}"></settings-adblock-page>
+ </settings-section>
+ </template>
<template is="dom-if" if="[[showPage_(pageVisibility.autofill)]]"
restamp>
<settings-section page-title="$i18n{autofillPageTitle}"
diff --git a/chrome/browser/resources/settings/basic_page/basic_page.ts b/chrome/browser/resources/settings/basic_page/basic_page.ts
--- a/chrome/browser/resources/settings/basic_page/basic_page.ts
+++ b/chrome/browser/resources/settings/basic_page/basic_page.ts
@@ -10,6 +10,7 @@ import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_style.css.js';
import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
import 'chrome://resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import '../adblock_page/adblock_page.js';
import '../appearance_page/appearance_page.js';
import '../privacy_page/preloading_page.js';
import '../privacy_page/privacy_guide/privacy_guide_promo.js';
diff --git a/chrome/browser/resources/settings/page_visibility.ts b/chrome/browser/resources/settings/page_visibility.ts
--- a/chrome/browser/resources/settings/page_visibility.ts
+++ b/chrome/browser/resources/settings/page_visibility.ts
@@ -9,6 +9,7 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
*/
export interface PageVisibility {
a11y?: boolean;
+ adblock?: boolean;
advancedSettings?: boolean;
appearance?: boolean|AppearancePageVisibility;
autofill?: boolean;
diff --git a/chrome/browser/resources/settings/route.ts b/chrome/browser/resources/settings/route.ts
--- a/chrome/browser/resources/settings/route.ts
+++ b/chrome/browser/resources/settings/route.ts
@@ -187,6 +187,10 @@ function createBrowserSettingsRoutes(): SettingsRoutes {
r.FONTS = r.APPEARANCE.createChild('/fonts');
}
+ if (visibility.adblock !== false) {
+ r.ADBLOCK = r.BASIC.createSection('/adblock', 'adblock');
+ }
+
if (visibility.autofill !== false) {
r.AUTOFILL = r.BASIC.createSection(
'/autofill', 'autofill', loadTimeData.getString('autofillPageTitle'));
diff --git a/chrome/browser/resources/settings/router.ts b/chrome/browser/resources/settings/router.ts
--- a/chrome/browser/resources/settings/router.ts
+++ b/chrome/browser/resources/settings/router.ts
@@ -13,6 +13,7 @@ import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/pol
*/
export interface SettingsRoutes {
ABOUT: Route;
+ ADBLOCK: Route;
ACCESSIBILITY: Route;
ADDRESSES: Route;
ADVANCED: Route;
diff --git a/chrome/browser/resources/settings/settings.ts b/chrome/browser/resources/settings/settings.ts
--- a/chrome/browser/resources/settings/settings.ts
+++ b/chrome/browser/resources/settings/settings.ts
@@ -47,6 +47,7 @@ export {AppearanceBrowserProxy, AppearanceBrowserProxyImpl} from './appearance_p
export {SettingsAppearancePageElement, SystemTheme} from './appearance_page/appearance_page.js';
export {HomeUrlInputElement} from './appearance_page/home_url_input.js';
export {SettingsAutofillPageElement} from './autofill_page/autofill_page.js';
+export {SettingsAdblockPageElement} from './adblock_page/adblock_page.js';
export {PasswordCheckReferrer, PasswordManagerImpl, PasswordManagerPage, PasswordManagerProxy} from './autofill_page/password_manager_proxy.js';
export {BaseMixin} from './base_mixin.js';
export {SettingsBasicPageElement} from './basic_page/basic_page.js';
diff --git a/chrome/browser/resources/settings/settings_menu/settings_menu.html b/chrome/browser/resources/settings/settings_menu/settings_menu.html
--- a/chrome/browser/resources/settings/settings_menu/settings_menu.html
+++ b/chrome/browser/resources/settings/settings_menu/settings_menu.html
@@ -140,6 +140,11 @@
$i18n{a11yPageTitle}
<paper-ripple></paper-ripple>
</a>
+ <a role="menuitem" id="adblock" href="/adblock" class="cr-nav-menu-item" hidden="[[!pageVisibility.adblock]]">
+ <iron-icon icon="settings:block"></iron-icon>
+ Adblock
+ <paper-ripple></paper-ripple>
+ </a>
<if expr="not chromeos_ash">
<a role="menuitem" id="system" href="/system" class="cr-nav-menu-item"
hidden="[[!pageVisibility.system]]">
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
@@ -20,6 +20,7 @@
#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/adblock/adblock_controller_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"
@@ -350,6 +351,8 @@ void TabHelpers::AttachTabHelpers(WebContents* web_contents) {
}
}
+ adblock::AdblockControllerFactory::GetForBrowserContext(
+ web_contents->GetBrowserContext());
AdblockWebContentObserver::CreateForWebContents(
web_contents,
adblock::SubscriptionServiceFactory::GetForBrowserContext(
@@ -358,7 +361,8 @@ void TabHelpers::AttachTabHelpers(WebContents* web_contents) {
web_contents->GetBrowserContext()),
adblock::SitekeyStorageFactory::GetForBrowserContext(
web_contents->GetBrowserContext()),
- std::make_unique<adblock::FrameHierarchyBuilder>());
+ std::make_unique<adblock::FrameHierarchyBuilder>(),
+ HostContentSettingsMapFactory::GetForProfile(profile));
autofill::ChromeAutofillClient::CreateForWebContents(web_contents);
if (breadcrumbs::IsEnabled())
BreadcrumbManagerTabHelper::CreateForWebContents(web_contents);
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
--- 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
@@ -17,11 +17,10 @@
#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 "base/i18n/time_formatting.h"
+#include "base/strings/utf_string_conversions.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"
@@ -53,6 +52,13 @@ std::string DebugLine(std::string name, int value, int level) {
return DebugLine(name, std::to_string(value), level);
}
+std::string FormatInstallationTime(base::Time time) {
+ if (time.is_null()) {
+ return "Never";
+ }
+ return base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(time));
+}
+
} // namespace
AdblockInternalsPageHandlerImpl::AdblockInternalsPageHandlerImpl(
@@ -90,17 +96,13 @@ void AdblockInternalsPageHandlerImpl::GetDebugInfo(
content += DebugLine("Title", it->GetTitle(), 2);
content += DebugLine("Version", it->GetCurrentVersion(), 2);
content += DebugLine("Last update",
- base::TimeToISO8601(it->GetInstallationTime()), 2);
+ FormatInstallationTime(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)));
+ std::move(callback).Run(std::move(content));
}
void AdblockInternalsPageHandlerImpl::OnTelemetryServiceInfoArrived(
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -60,10 +60,6 @@
"5107DE9024C329EEA9C9A72D94C16723790C6422" // Apps Developer Tool Dev.
]
},
- "adblockPrivate": {
- "channel": "stable",
- "extension_types": ["extension", "platform_app"]
- },
"autofillPrivate": {
"channel": "trunk",
"extension_types": ["extension", "platform_app"],
@@ -351,10 +347,6 @@
"extension", "legacy_packaged_app", "hosted_app", "platform_app"
]
},
- "eyeoFilteringPrivate": {
- "channel": "stable",
- "extension_types": ["extension", "platform_app"]
- },
"favicon": {
"channel": "stable",
"extension_types": ["extension"]
diff --git a/chrome/common/extensions/api/adblock_private.idl b/chrome/common/extensions/api/adblock_private.idl
--- a/chrome/common/extensions/api/adblock_private.idl
+++ b/chrome/common/extensions/api/adblock_private.idl
@@ -47,6 +47,8 @@ dictionary Subscription {
// Time of last successful installation or update, in ISO 8601 format.
// May be passed directly to the Date constructor.
DOMString last_installation_time;
+ long download_success_count;
+ long download_error_count;
};
dictionary SessionStatsEntry {
@@ -97,6 +99,12 @@ callback ListCallback = void(DOMString[] result);
callback SessionStatsCallback = void(SessionStatsEntry[] result);
interface Functions {
+ // Start an update cycle
+ static void startUpdate();
+ // Allows to turn Adblock on or off.
+ static void setPrivilegedFiltersEnabled(boolean enabled);
+ // Returns whether Adblock is on.
+ static void isPrivilegedFiltersEnabled(StateCallback callback);
// Allows to turn Adblock on or off.
static void setEnabled(boolean enabled);
// Returns whether Adblock is on.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -28,7 +28,6 @@ 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")
@@ -2754,10 +2753,6 @@ 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" ]
}
diff --git a/components/adblock/android/BUILD.gn b/components/adblock/android/BUILD.gn
--- a/components/adblock/android/BUILD.gn
+++ b/components/adblock/android/BUILD.gn
@@ -14,6 +14,7 @@
import("//build/config/android/rules.gni")
import("//build/config/locales.gni")
import("//tools/grit/grit_rule.gni")
+import("//third_party/jni_zero/jni_zero.gni")
source_set("java_bindings") {
sources = [
@@ -120,7 +121,7 @@ android_resources("java_ui_resources") {
"java/res/layout/adblock_custom_item_settings.xml",
"java/res/layout/adblock_filter_lists_list_item.xml",
"java/res/xml/adblock_more_options.xml",
- "java/res/xml/adblock_preferences.xml",
+ "java/res/xml/eyeo_adblock_preferences.xml",
]
deps = [ ":adblock_strings_grd" ]
diff --git a/components/adblock/android/adblock_jni.cc b/components/adblock/android/adblock_jni.cc
--- a/components/adblock/android/adblock_jni.cc
+++ b/components/adblock/android/adblock_jni.cc
@@ -25,6 +25,8 @@
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/jni_weak_ref.h"
+#include "base/i18n/time_formatting.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/logging.h"
#include "components/adblock/android/java_bindings_getters.h"
#include "components/adblock/android/jni_headers/AdblockController_jni.h"
@@ -54,6 +56,10 @@ ScopedJavaLocalRef<jobject> ToJava(JNIEnv* env,
const std::string& url,
const std::string& title,
const std::string& version,
+ adblock::Subscription::InstallationState state,
+ const std::string& installation_time,
+ long download_success_count,
+ long download_error_count,
const std::vector<std::string>& languages) {
ScopedJavaLocalRef<jobject> url_param(
env, env->NewObject(url_class.obj(), url_constructor,
@@ -62,12 +68,19 @@ ScopedJavaLocalRef<jobject> ToJava(JNIEnv* env,
return Java_Subscription_Constructor(env, url_param,
ConvertUTF8ToJavaString(env, title),
ConvertUTF8ToJavaString(env, version),
+ ConvertUTF8ToJavaString(env, Subscription::SubscriptionInstallationStateToString(state)),
+ ConvertUTF8ToJavaString(env, installation_time),
+ download_success_count,
+ download_error_count,
ToJavaArrayOfStrings(env, languages));
}
std::vector<ScopedJavaLocalRef<jobject>> CSubscriptionsToJObjects(
JNIEnv* env,
const std::vector<scoped_refptr<Subscription>>& subscriptions) {
+ auto* subscription_service = adblock::GetSubscriptionService();
+ raw_ptr<adblock::SubscriptionPersistentMetadata> metadata =
+ subscription_service->GetMetadata();
ScopedJavaLocalRef<jclass> url_class = GetClass(env, "java/net/URL");
jmethodID url_constructor = MethodID::Get<MethodID::TYPE_INSTANCE>(
env, url_class.obj(), "<init>", "(Ljava/lang/String;)V");
@@ -76,7 +89,11 @@ std::vector<ScopedJavaLocalRef<jobject>> CSubscriptionsToJObjects(
for (auto& sub : subscriptions) {
jobjects.push_back(ToJava(
env, url_class, url_constructor, sub->GetSourceUrl().spec(),
- sub->GetTitle(), sub->GetCurrentVersion(), std::vector<std::string>{}));
+ sub->GetTitle(), sub->GetCurrentVersion(),
+ sub->GetInstallationState(), base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(sub->GetInstallationTime())),
+ metadata ? metadata->GetDownloadSuccessCount(sub->GetSourceUrl()) : 0,
+ metadata ? metadata->GetDownloadErrorCount(sub->GetSourceUrl()) : 0,
+ std::vector<std::string>{}));
}
return jobjects;
}
@@ -96,6 +113,9 @@ std::vector<ScopedJavaLocalRef<jobject>> CSubscriptionsToJObjects(
if (sub.url.is_valid()) {
jobjects.push_back(ToJava(env, url_class, url_constructor,
sub.url.spec(), sub.title, "",
+ adblock::Subscription::InstallationState::Unknown,
+ /*installation_time*/ "",
+ /*download_success_count*/ 0, /*download_error_count*/ 0,
sub.languages));
}
}
@@ -139,6 +159,37 @@ void AdblockJNI::OnSubscriptionInstalled(const GURL& url) {
} // namespace adblock
+static void
+JNI_AdblockController_StartUpdate(JNIEnv* env) {
+ adblock::GetSubscriptionService()->StartUpdate();
+}
+
+static base::android::ScopedJavaLocalRef<jobjectArray>
+JNI_AdblockController_GetCustomSubscriptions(JNIEnv* env) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ auto* subscription_service = adblock::GetSubscriptionService();
+ if (!subscription_service) {
+ return ToJavaArrayOfObjects(env,
+ std::vector<ScopedJavaLocalRef<jobject>>{});
+ }
+
+ return ToJavaArrayOfObjects(
+ env, adblock::CSubscriptionsToJObjects(
+ env, subscription_service->GetCustomSubscriptions(
+ subscription_service->GetAdblockFilteringConfiguration())));
+}
+
+static jboolean JNI_AdblockController_IsPrivilegedFiltersEnabled(
+ JNIEnv* env) {
+ return adblock::GetSubscriptionService()->IsPrivilegedFiltersEnabled() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void JNI_AdblockController_SetPrivilegedFiltersEnabled(
+ JNIEnv* env,
+ jboolean j_enabled) {
+ adblock::GetSubscriptionService()->SetPrivilegedFiltersEnabled(j_enabled == JNI_TRUE);
+}
+
static void JNI_AdblockController_Bind(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller) {
diff --git a/components/adblock/android/adblock_strings.grd b/components/adblock/android/adblock_strings.grd
--- a/components/adblock/android/adblock_strings.grd
+++ b/components/adblock/android/adblock_strings.grd
@@ -186,10 +186,52 @@
<release seq="1">
<messages fallback_to_english="true">
<message name="IDS_ADBLOCK_SETTINGS_TITLE" desc="Title of Adblock settings menu item [CHAR-LIMIT=12]">
- Ad blocking
+ Adblock Plus settings
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_ENABLE" desc="">
+ Enable Adblock Plus
+ </message>
+ <message name="IDS_FRAGMENT_ADBLOCK_SETTINGS_START_UPDATE" desc="">
+ Check for updates now
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_PRIVILEGED_FILTERS_ENABLED" desc="">
+ Enable anti-circumvention and snippets
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_PRIVILEGED_FILTERS_ENABLED_SUMMARY" desc="">
+ Snippets are pieces of JavaScript code, injected by the Adblock Plus, that execute within the context of a website and combat advanced ads that circumvent ordinary blocking.
+The functionality is ONLY allowed for the list
+https://easylist-downloads.adblockplus.org/abp-filters-anti-cv.txt
+which is activated by this setting.
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_OPEN_REPO_URL_TEXT" desc="">
+ Open ABP anti-circumvention filter list repo
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_OPEN_REPO_URL_SUMMARY" desc="">
+ Open https://github.com/abp-filters/abp-filters-anti-cv in the browser
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_OPEN_REPO_URL" desc="" translateable="false">
+ https://github.com/abp-filters/abp-filters-anti-cv
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_OPEN_SNIPPETS_URL_TEXT" desc="">
+ Open ABP Snippets Overview
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_OPEN_SNIPPETS_URL_SUMMARY" desc="">
+ Open https://developers.eyeo.com/snippets/snippets-overview in the browser
+ </message>
+ <message name="IDS_ADBLOCK_SETTINGS_OPEN_SNIPPETS_URL" desc="" translateable="false">
+ https://developers.eyeo.com/snippets/snippets-overview
+ </message>
+ <message name="IDS_FRAGMENT_ADBLOCK_SETTINGS_FILTER_LISTS_TITLE_COUNT" desc="Title of MultiSelectListPreference to choose filter lists">
+ Filter lists (<ph name="COUNT">%s</ph> selected)
+ </message>
+ <message name="IDS_FRAGMENT_ADBLOCK_MORE_OPTIONS_CUSTOM_FILTER_LISTS_TITLE_COUNT" desc="Title of the Preference and fragment for adding custom filter lists">
+ Custom ad filtering settings (<ph name="COUNT">%s</ph> selected)
+ </message>
+ <message name="IDS_FRAGMENT_ADBLOCK_MORE_OPTIONS_CUSTOM_FILTERS_TITLE_COUNT" desc="Title of the Preference and fragment for adding custom filters">
+ Custom Filters (<ph name="COUNT">%s</ph> selected)
</message>
<message name="IDS_FRAGMENT_ADBLOCK_SETTINGS_ENABLED_SUMMARY" desc="Summary of SwitchPreference to enable/disable Adblock">
- Allow ad blocking on websites in this app
+ Block ads on websites
</message>
<message name="IDS_FRAGMENT_ADBLOCK_SETTINGS_FILTER_LISTS_TITLE" desc="Title of MultiSelectListPreference to choose filter lists">
Filter lists
@@ -216,10 +258,10 @@
More blocking options
</message>
<message name="IDS_FRAGMENT_ADBLOCK_MORE_OPTIONS_CUSTOM_FILTER_LISTS_TITLE" desc="Title of the Preference and fragment for adding custom filter lists">
- Custom ad filtering settings
+ Custom ad filtering urls
</message>
<message name="IDS_FRAGMENT_ADBLOCK_MORE_OPTIONS_CUSTOM_FILTER_LISTS_SUMMARY" desc="Title of the Preference for adding custom filter lists">
- Add custom filter lists
+ Add custom filter urls
</message>
<message name="IDS_FRAGMENT_ADBLOCK_MORE_OPTIONS_ADD_CUSTOM_FILTER_LIST" desc="Hint shown in a EditView for adding custom filter list URL">
https://example.org/myFilterList.txt
@@ -228,10 +270,10 @@
Custom Filters
</message>
<message name="IDS_FRAGMENT_ADBLOCK_MORE_OPTIONS_CUSTOM_FILTERS_SUMMARY" desc="Title of the Preference for adding custom filters">
- Add custom filters
+ Add custom filter commands
</message>
<message name="IDS_FRAGMENT_ADBLOCK_MORE_OPTIONS_CUSTOM_FILTERS_HINT" desc="Hint shown in a EditView for adding custom filter">
- Enter filter
+ Enter filter command
</message>
</messages>
</release>
diff --git a/components/adblock/android/java/res/layout/adblock_custom_item.xml b/components/adblock/android/java/res/layout/adblock_custom_item.xml
--- a/components/adblock/android/java/res/layout/adblock_custom_item.xml
+++ b/components/adblock/android/java/res/layout/adblock_custom_item.xml
@@ -25,14 +25,24 @@
tools:ignore="UseCompoundDrawables">
<!-- Domain/URL -->
- <TextView
- android:id="@+id/fragment_adblock_custom_item_title"
+ <LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:paddingStart="0dp"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/fragment_adblock_custom_item_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="http://www.google.com"/>
+ <TextView
+ android:id="@+id/fragment_adblock_custom_item_status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.TextSmall.Secondary" />
+ </LinearLayout>
<!-- Remove button
UseCompoundDrawables added to parent element to suppress warning
diff --git a/components/adblock/android/java/res/layout/adblock_filter_lists_list_item.xml b/components/adblock/android/java/res/layout/adblock_filter_lists_list_item.xml
--- a/components/adblock/android/java/res/layout/adblock_filter_lists_list_item.xml
+++ b/components/adblock/android/java/res/layout/adblock_filter_lists_list_item.xml
@@ -28,9 +28,24 @@
android:layout_height="wrap_content"
android:layout_marginEnd="21dp"
android:clickable="false" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.TextLarge.Primary" />
+ <TextView
+ android:id="@+id/url"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.TextSmall.Secondary" />
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.TextSmall.Secondary" />
+ </LinearLayout>
</LinearLayout>
diff --git a/components/adblock/android/java/res/xml/adblock_preferences.xml b/components/adblock/android/java/res/xml/eyeo_adblock_preferences.xml
similarity index 56%
rename from components/adblock/android/java/res/xml/adblock_preferences.xml
rename to components/adblock/android/java/res/xml/eyeo_adblock_preferences.xml
--- a/components/adblock/android/java/res/xml/adblock_preferences.xml
+++ b/components/adblock/android/java/res/xml/eyeo_adblock_preferences.xml
@@ -20,7 +20,7 @@
<!-- Adblock: enabled/disabled -->
<org.chromium.components.browser_ui.settings.ChromeSwitchPreference
android:key="fragment_adblock_settings_enabled_key"
- android:title="@string/adblock_settings_title"
+ android:title="@string/adblock_settings_enable"
app:iconSpaceReserved="false"
android:summary="@string/fragment_adblock_settings_enabled_summary" />
@@ -32,6 +32,43 @@
app:iconSpaceReserved="false"
android:summary="@string/fragment_adblock_settings_filter_lists_summary" />
+ <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
+ android:key="fragment_adblock_privileged_filters_enabled_key"
+ android:title="@string/adblock_settings_privileged_filters_enabled"
+ app:iconSpaceReserved="false"
+ android:summary="@string/adblock_settings_privileged_filters_enabled_summary" />
+
+ <org.chromium.chrome.browser.about_settings.HyperlinkPreference
+ android:key="fragment_adblock_settings_open_repo_url"
+ android:title="@string/adblock_settings_open_repo_url_text"
+ app:url="@string/adblock_settings_open_repo_url"
+ android:summary="@string/adblock_settings_open_repo_url_summary" />
+
+ <org.chromium.chrome.browser.about_settings.HyperlinkPreference
+ android:key="fragment_adblock_settings_open_snippets_url"
+ android:title="@string/adblock_settings_open_snippets_url_text"
+ app:url="@string/adblock_settings_open_snippets_url"
+ android:summary="@string/adblock_settings_open_snippets_url_summary" />
+
+
+ <androidx.preference.Preference
+ android:fragment="org.chromium.components.adblock.settings.AdblockCustomFilterListsFragment"
+ android:key="fragment_adblock_more_options_custom_filter_lists_key"
+ android:title="@string/fragment_adblock_more_options_custom_filter_lists_title"
+ app:iconSpaceReserved="false"
+ android:summary="@string/fragment_adblock_more_options_custom_filter_lists_summary" />
+
+ <androidx.preference.Preference
+ android:fragment="org.chromium.components.adblock.settings.AdblockCustomFiltersFragment"
+ android:key="fragment_adblock_more_options_custom_filter_key"
+ android:title="@string/fragment_adblock_more_options_custom_filters_title"
+ app:iconSpaceReserved="false"
+ android:summary="@string/fragment_adblock_more_options_custom_filters_summary" />
+
+ <Preference
+ android:key="fragment_adblock_settings_start_update"
+ android:title="@string/fragment_adblock_settings_start_update"/>
+
<!-- Acceptable Ads: enabled/disabled -->
<org.chromium.components.browser_ui.settings.ChromeSwitchPreference
android:key="fragment_adblock_settings_aa_enabled_key"
diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/AdblockController.java b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockController.java
--- a/components/adblock/android/java/src/org/chromium/components/adblock/AdblockController.java
+++ b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockController.java
@@ -73,6 +73,10 @@ public final class AdblockController extends FilteringConfiguration {
private String mTitle;
private String mVersion = "";
private String[] mLanguages = {};
+ private String mState;
+ private String mInstallationTime;
+ private long mDownloadSuccessCount;
+ private long mDownloadErrorCount;
public Subscription(final URL url, final String title, final String version) {
this.mUrl = url;
@@ -82,11 +86,30 @@ public final class AdblockController extends FilteringConfiguration {
@CalledByNative("Subscription")
public Subscription(
- final URL url, final String title, final String version, final String[] languages) {
+ final URL url, final String title, final String version,
+ final String state, final String installation_time,
+ long download_success_count, long download_error_count,
+ final String[] languages) {
this.mUrl = url;
this.mTitle = title;
this.mVersion = version;
this.mLanguages = languages;
+ this.mState = state;
+ this.mInstallationTime = installation_time;
+ this.mDownloadSuccessCount = download_success_count;
+ this.mDownloadErrorCount = download_error_count;
+ }
+
+ public String state() { return mState; }
+ public String installation_time() { return mInstallationTime; }
+ public long download_success_count() { return mDownloadSuccessCount; }
+ public long download_error_count() { return mDownloadErrorCount; }
+ public String getDescription() {
+ return state()
+ + " Version: " + version()
+ + " Last update: " + installation_time()
+ + " (total " + download_error_count() + " downloads errors, "
+ + download_success_count() + " success)";
}
public String title() {
@@ -117,6 +140,7 @@ public final class AdblockController extends FilteringConfiguration {
@UiThread
public void setAcceptableAdsEnabled(boolean enabled) {
+ enabled = false;
if (enabled)
addFilterList(mAcceptableAds);
else
@@ -144,6 +168,27 @@ public final class AdblockController extends FilteringConfiguration {
removeFilterList(url);
}
+ @UiThread
+ public void startUpdate() {
+ AdblockControllerJni.get().startUpdate();
+ }
+
+ @UiThread
+ public List<Subscription> getCustomSubscriptions() {
+ return (List<Subscription>) (List<?>) Arrays.asList(
+ AdblockControllerJni.get().getCustomSubscriptions());
+ }
+
+ @UiThread
+ public void setPrivilegedFiltersEnabled(boolean enabled) {
+ AdblockControllerJni.get().setPrivilegedFiltersEnabled(enabled);
+ }
+
+ @UiThread
+ public boolean isPrivilegedFiltersEnabled() {
+ return AdblockControllerJni.get().isPrivilegedFiltersEnabled();
+ }
+
@UiThread
public List<Subscription> getInstalledSubscriptions() {
return (List<Subscription>) (List<?>) Arrays.asList(
@@ -196,6 +241,10 @@ public final class AdblockController extends FilteringConfiguration {
@NativeMethods
interface Natives {
+ void startUpdate();
+ boolean isPrivilegedFiltersEnabled();
+ void setPrivilegedFiltersEnabled(boolean enabled);
+ Object[] getCustomSubscriptions();
void bind(AdblockController caller);
Object[] getInstalledSubscriptions();
Object[] getRecommendedSubscriptions();
diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockAllowedDomainsFragment.java b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockAllowedDomainsFragment.java
--- a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockAllowedDomainsFragment.java
+++ b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockAllowedDomainsFragment.java
@@ -26,9 +26,14 @@ import org.chromium.components.adblock.R;
import java.util.List;
-public class AdblockAllowedDomainsFragment extends AdblockCustomItemFragment {
+public class AdblockAllowedDomainsFragment extends AdblockCustomItemFragment<String> {
public AdblockAllowedDomainsFragment() {}
+ @Override
+ protected String getItemText(String item) {
+ return item;
+ }
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomFilterListsFragment.java b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomFilterListsFragment.java
--- a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomFilterListsFragment.java
+++ b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomFilterListsFragment.java
@@ -25,6 +25,7 @@ import android.webkit.URLUtil;
import android.widget.Toast;
import org.chromium.components.adblock.AdblockController;
+import org.chromium.components.adblock.AdblockController.Subscription;
import org.chromium.components.adblock.R;
import java.net.MalformedURLException;
@@ -32,7 +33,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
-public class AdblockCustomFilterListsFragment extends AdblockCustomItemFragment {
+public class AdblockCustomFilterListsFragment extends AdblockCustomItemFragment<Subscription> {
private static final String TAG = AdblockCustomFilterListsFragment.class.getSimpleName();
public AdblockCustomFilterListsFragment() {}
@@ -43,25 +44,18 @@ public class AdblockCustomFilterListsFragment extends AdblockCustomItemFragment
}
@Override
- protected List<String> getItems() {
- final List<AdblockController.Subscription> installed =
- AdblockController.getInstance().getInstalledSubscriptions();
- final List<AdblockController.Subscription> recommended =
- AdblockController.getInstance().getRecommendedSubscriptions();
- final List<String> customStrings = new ArrayList<String>();
- for (final AdblockController.Subscription subscription : installed) {
- if (recommended.contains(subscription)) {
- continue;
- }
- // FIXME(kzlomek): Remove this after DPD-1613
- if (subscription.url().toString().equals(
- "https://easylist-downloads.adblockplus.org/exceptionrules.txt")) {
- continue;
- }
- customStrings.add(subscription.url().toString());
- }
+ protected List<Subscription> getItems() {
+ return AdblockController.getInstance().getCustomSubscriptions();
+ }
+
+ @Override
+ protected String getItemText(Subscription item) {
+ return item.url().toString();
+ }
- return customStrings;
+ @Override
+ protected String getItemStatus(Subscription item) {
+ return item.getDescription();
}
@Override
diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomFiltersFragment.java b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomFiltersFragment.java
--- a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomFiltersFragment.java
+++ b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomFiltersFragment.java
@@ -25,9 +25,14 @@ import org.chromium.components.adblock.R;
import java.util.List;
-public class AdblockCustomFiltersFragment extends AdblockCustomItemFragment {
+public class AdblockCustomFiltersFragment extends AdblockCustomItemFragment<String> {
public AdblockCustomFiltersFragment() {}
+ @Override
+ protected String getItemText(String item) {
+ return item;
+ }
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomItemFragment.java b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomItemFragment.java
--- a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomItemFragment.java
+++ b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockCustomItemFragment.java
@@ -34,7 +34,7 @@ import org.chromium.components.adblock.R;
import java.util.ArrayList;
import java.util.List;
-public abstract class AdblockCustomItemFragment extends PreferenceFragmentCompat {
+public abstract class AdblockCustomItemFragment<T> extends PreferenceFragmentCompat {
private EditText mItem;
private ImageView mAddButton;
private ListView mListView;
@@ -74,7 +74,7 @@ public abstract class AdblockCustomItemFragment extends PreferenceFragmentCompat
protected abstract void addItemImpl(String item);
protected abstract void removeItemImpl(String item);
- protected abstract List<String> getItems();
+ protected abstract List<T> getItems();
protected abstract String getCustomItemTextViewText();
protected abstract String getCustomItemTextViewContentDescription();
protected abstract String getCustomItemAddButtonContentDescription();
@@ -84,10 +84,12 @@ public abstract class AdblockCustomItemFragment extends PreferenceFragmentCompat
// Holder for listview items
private class Holder {
TextView mItem;
+ TextView mStatus;
ImageView mRemoveButton;
Holder(View rootView) {
mItem = rootView.findViewById(R.id.fragment_adblock_custom_item_title);
+ mStatus = rootView.findViewById(R.id.fragment_adblock_custom_item_status);
mRemoveButton = rootView.findViewById(R.id.fragment_adblock_custom_item_remove);
mRemoveButton.setContentDescription(
AdblockCustomItemFragment.this.getCustomItemRemoveButtonContentDescription());
@@ -100,6 +102,7 @@ public abstract class AdblockCustomItemFragment extends PreferenceFragmentCompat
String item = (String) v.getTag();
removeItemImpl(item);
mAdapter.notifyDataSetChanged();
+ mItem.setText(item);
}
};
@@ -127,16 +130,23 @@ public abstract class AdblockCustomItemFragment extends PreferenceFragmentCompat
convertView.setTag(new Holder(convertView));
}
- String item = (String) getItem(position);
+ T item = (T) getItem(position);
Holder holder = (Holder) convertView.getTag();
- holder.mItem.setText(item.toString());
+ holder.mItem.setText(getItemText(item));
+ holder.mStatus.setText(getItemStatus(item));
holder.mRemoveButton.setOnClickListener(removeItemClickListener);
- holder.mRemoveButton.setTag(item.toString());
+ holder.mRemoveButton.setTag(getItemText(item));
return convertView;
}
}
+ protected abstract String getItemText(T item);
+
+ protected String getItemStatus(T item) {
+ return null;
+ }
+
private void initControls() {
mAddButton.setOnClickListener(new View.OnClickListener() {
@Override
diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockFilterListsAdapter.java b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockFilterListsAdapter.java
--- a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockFilterListsAdapter.java
+++ b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockFilterListsAdapter.java
@@ -82,9 +82,12 @@ public class AdblockFilterListsAdapter extends BaseAdapter implements OnClickLis
final List<AdblockController.Subscription> subscriptions =
mController.getInstalledSubscriptions();
boolean subscribed = false;
+ TextView status = view.findViewById(R.id.status);
+ status.setText("");
for (final AdblockController.Subscription subscription : subscriptions) {
if (subscription.url().equals(item.url())) {
subscribed = true;
+ status.setText(subscription.getDescription());
break;
}
}
@@ -94,6 +97,9 @@ public class AdblockFilterListsAdapter extends BaseAdapter implements OnClickLis
TextView description = view.findViewById(R.id.name);
description.setText(item.title());
description.setContentDescription(item.title() + "filer list item title text");
+
+ TextView url = view.findViewById(R.id.url);
+ url.setText(item.url().toString());
return view;
}
diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockSettingsFragment.java b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockSettingsFragment.java
--- a/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockSettingsFragment.java
+++ b/components/adblock/android/java/src/org/chromium/components/adblock/settings/AdblockSettingsFragment.java
@@ -13,6 +13,7 @@
package org.chromium.components.adblock.settings;
import android.os.Bundle;
+import android.widget.Toast;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
@@ -32,7 +33,19 @@ public class AdblockSettingsFragment
private Preference mFilterLists;
private Preference mAllowedDomains;
private Preference mMoreOptions;
-
+ private Preference mStartUpdate;
+ private ChromeSwitchPreference mPrivilegedFilters;
+
+ private static final String START_UPDATE_KEY =
+ "fragment_adblock_settings_start_update";
+ private static final String PRIVILEGED_FILTERS_KEY =
+ "fragment_adblock_privileged_filters_enabled_key";
+ private static final String FILTER_LISTS_KEY =
+ "fragment_adblock_settings_filter_lists_key";
+ private static final String CUSTOM_FILTER_LISTS_KEY =
+ "fragment_adblock_more_options_custom_filter_lists_key";
+ private static final String CUSTOM_FILTER_KEY =
+ "fragment_adblock_more_options_custom_filter_key";
private static final String SETTINGS_ENABLED_KEY = "fragment_adblock_settings_enabled_key";
private static final String SETTINGS_FILTER_LISTS_KEY =
"fragment_adblock_settings_filter_lists_key";
@@ -49,9 +62,20 @@ public class AdblockSettingsFragment
private long mOnOffTogleTimestamp;
private void bindPreferences() {
+ mStartUpdate = findPreference(START_UPDATE_KEY);
+ mStartUpdate.setOnPreferenceClickListener(preference -> {
+ AdblockController.getInstance().startUpdate();
+ Toast toast = Toast.makeText(getContext(),
+ "Checking for updates in progress", Toast.LENGTH_LONG);
+ toast.show();
+ // handle the click so the default action isn't triggered.
+ return true;
+ });
+ mPrivilegedFilters = (ChromeSwitchPreference) findPreference(PRIVILEGED_FILTERS_KEY);
mAdblockEnabled = (ChromeSwitchPreference) findPreference(SETTINGS_ENABLED_KEY);
mFilterLists = findPreference(SETTINGS_FILTER_LISTS_KEY);
mAcceptableAdsEnabled = (ChromeSwitchPreference) findPreference(SETTINGS_AA_ENABLED_KEY);
+ mAcceptableAdsEnabled.setVisible(false);
mAllowedDomains = findPreference(SETTINGS_ALLOWED_DOMAINS_KEY);
mMoreOptions = findPreference(SETTINGS_MORE_OPTIONS_KEY);
}
@@ -62,14 +86,25 @@ public class AdblockSettingsFragment
}
private void applyAdblockEnabled(boolean enabledValue) {
- mFilterLists.setEnabled(enabledValue);
- mAcceptableAdsEnabled.setEnabled(enabledValue);
+ mStartUpdate.setEnabled(enabledValue);
+ mPrivilegedFilters.setEnabled(enabledValue);
mAllowedDomains.setEnabled(enabledValue);
mMoreOptions.setEnabled(enabledValue);
mMoreOptions.setVisible(areMoreOptionsEnabled());
}
private void synchronizePreferences() {
+ findPreference(FILTER_LISTS_KEY).setTitle(
+ getContext().getString(R.string.fragment_adblock_settings_filter_lists_title_count,
+ AdblockController.getInstance().getInstalledSubscriptions().size()));
+ findPreference(CUSTOM_FILTER_LISTS_KEY).setTitle(
+ getContext().getString(R.string.fragment_adblock_more_options_custom_filter_lists_title_count,
+ AdblockController.getInstance().getCustomSubscriptions().size()));
+ findPreference(CUSTOM_FILTER_KEY).setTitle(
+ getContext().getString(R.string.fragment_adblock_more_options_custom_filters_title_count,
+ AdblockController.getInstance().getCustomFilters().size()));
+ mPrivilegedFilters.setChecked(AdblockController.getInstance().isPrivilegedFiltersEnabled());
+ mPrivilegedFilters.setOnPreferenceChangeListener(this);
boolean enabled = AdblockController.getInstance().isEnabled();
mAdblockEnabled.setChecked(enabled);
mAdblockEnabled.setOnPreferenceChangeListener(this);
@@ -107,7 +142,7 @@ public class AdblockSettingsFragment
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- addPreferencesFromResource(R.xml.adblock_preferences);
+ addPreferencesFromResource(R.xml.eyeo_adblock_preferences);
bindPreferences();
synchronizePreferences();
}
@@ -126,6 +161,8 @@ public class AdblockSettingsFragment
maybeEnableMoreOptions();
applyAdblockEnabled((Boolean) newValue);
+ } else if (preference.getKey().equals(PRIVILEGED_FILTERS_KEY)) {
+ AdblockController.getInstance().setPrivilegedFiltersEnabled((Boolean) newValue);
} else {
assert preference.getKey().equals(SETTINGS_AA_ENABLED_KEY);
AdblockController.getInstance().setAcceptableAdsEnabled((Boolean) newValue);
diff --git a/components/adblock/content/browser/BUILD.gn b/components/adblock/content/browser/BUILD.gn
--- a/components/adblock/content/browser/BUILD.gn
+++ b/components/adblock/content/browser/BUILD.gn
@@ -14,15 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with eyeo Chromium SDK. If not, see <http://www.gnu.org/licenses/>.
-import("//components/adblock/features.gni")
-
config("adblock_content_common_config") {
defines = []
-
- if (eyeo_intercept_debug_url) {
- print("WARNING! Enabled intercepting eyeo debug url \"adblock.test.data\"")
- defines += [ "EYEO_INTERCEPT_DEBUG_URL=1" ]
- }
}
source_set("browser_impl") {
@@ -34,8 +27,6 @@ source_set("browser_impl") {
"adblock_controller_factory_base.cc",
"adblock_controller_factory_base.h",
"adblock_filter_match.h",
- "adblock_telemetry_service_factory_base.cc",
- "adblock_telemetry_service_factory_base.h",
"adblock_url_loader_factory.cc",
"adblock_url_loader_factory.h",
"adblock_webcontents_observer.cc",
@@ -62,15 +53,10 @@ source_set("browser_impl") {
]
- if (eyeo_intercept_debug_url) {
- sources += [
- "adblock_url_loader_factory_for_test.cc",
- "adblock_url_loader_factory_for_test.h",
- ]
- }
-
deps = [
"//base",
+ "//components/content_settings/browser",
+ "//components/content_settings/core/browser",
"//components/adblock/core/converter:converter",
"//components/keyed_service/content:content",
"//components/resources:components_resources_grit",
diff --git a/components/adblock/content/browser/adblock_controller_factory_base.cc b/components/adblock/content/browser/adblock_controller_factory_base.cc
--- a/components/adblock/content/browser/adblock_controller_factory_base.cc
+++ b/components/adblock/content/browser/adblock_controller_factory_base.cc
@@ -47,10 +47,7 @@ KeyedService* AdblockControllerFactoryBase::BuildServiceInstanceFor(
std::make_unique<PersistentFilteringConfiguration>(
prefs, kAdblockFilteringConfigurationName);
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- adblock::switches::kDisableAcceptableAds)) {
- adblock_filtering_configuration->RemoveFilterList(AcceptableAdsUrl());
- }
+ adblock_filtering_configuration->RemoveFilterList(AcceptableAdsUrl());
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableAdblock) ||
base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/components/adblock/content/browser/adblock_webcontents_observer.cc b/components/adblock/content/browser/adblock_webcontents_observer.cc
--- a/components/adblock/content/browser/adblock_webcontents_observer.cc
+++ b/components/adblock/content/browser/adblock_webcontents_observer.cc
@@ -21,6 +21,8 @@
#include "components/adblock/content/browser/frame_opener_info.h"
#include "components/adblock/core/common/sitekey.h"
#include "components/adblock/core/subscription/subscription_service.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
+#include "components/content_settings/core/common/content_settings.h"
#include "content/public/browser/navigation_handle.h"
#include "net/base/url_util.h"
#include "third_party/blink/public/common/frame/frame_owner_element_type.h"
@@ -56,7 +58,7 @@ const char* WindowOpenDispositionToString(WindowOpenDisposition value) {
return "";
}
}
-} // namespace
+
void TraceHandleLoadComplete(
intptr_t rfh_trace_id,
@@ -66,18 +68,32 @@ void TraceHandleLoadComplete(
TRACE_ID_LOCAL(rfh_trace_id));
}
+bool IsFilteringNeeded(content::RenderFrameHost* frame, HostContentSettingsMap* settings_map) {
+ content::RenderFrameHost* embedder = frame->GetOutermostMainFrameOrEmbedder();
+ const auto& embedder_url = embedder->GetLastCommittedURL();
+ if (settings_map->GetContentSetting(embedder_url, GURL(), ContentSettingsType::ADS)
+ == CONTENT_SETTING_ALLOW) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
AdblockWebContentObserver::AdblockWebContentObserver(
content::WebContents* web_contents,
adblock::SubscriptionService* subscription_service,
adblock::ElementHider* element_hider,
adblock::SitekeyStorage* sitekey_storage,
- std::unique_ptr<adblock::FrameHierarchyBuilder> frame_hierarchy_builder)
+ std::unique_ptr<adblock::FrameHierarchyBuilder> frame_hierarchy_builder,
+ HostContentSettingsMap* settings_map)
: content::WebContentsObserver(web_contents),
content::WebContentsUserData<AdblockWebContentObserver>(*web_contents),
subscription_service_(subscription_service),
element_hider_(element_hider),
sitekey_storage_(sitekey_storage),
- frame_hierarchy_builder_(std::move(frame_hierarchy_builder)) {}
+ frame_hierarchy_builder_(std::move(frame_hierarchy_builder)),
+ settings_map_(settings_map) {}
AdblockWebContentObserver::~AdblockWebContentObserver() = default;
@@ -122,6 +138,9 @@ void AdblockWebContentObserver::DidFinishNavigation(
if (!navigation_handle->GetRenderFrameHost()) {
return;
}
+ if (!IsFilteringNeeded(navigation_handle->GetRenderFrameHost(), settings_map_)) {
+ return;
+ }
if (!navigation_handle->IsErrorPage()) {
DVLOG(3) << "[eyeo] Ready to inject JS to " << url.spec();
HandleOnLoad(navigation_handle->GetRenderFrameHost());
diff --git a/components/adblock/content/browser/adblock_webcontents_observer.h b/components/adblock/content/browser/adblock_webcontents_observer.h
--- a/components/adblock/content/browser/adblock_webcontents_observer.h
+++ b/components/adblock/content/browser/adblock_webcontents_observer.h
@@ -24,6 +24,7 @@
#include "components/adblock/core/adblock_controller.h"
#include "components/adblock/core/sitekey_storage.h"
#include "components/adblock/core/subscription/subscription_service.h"
+#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
@@ -49,7 +50,8 @@ class AdblockWebContentObserver
adblock::SubscriptionService* subscription_service,
adblock::ElementHider* element_hider,
adblock::SitekeyStorage* sitekey_storage,
- std::unique_ptr<adblock::FrameHierarchyBuilder> frame_hierarchy_builder);
+ std::unique_ptr<adblock::FrameHierarchyBuilder> frame_hierarchy_builder,
+ HostContentSettingsMap* settings_map);
~AdblockWebContentObserver() override;
AdblockWebContentObserver(const AdblockWebContentObserver&) = delete;
AdblockWebContentObserver& operator=(const AdblockWebContentObserver&) =
@@ -81,5 +83,6 @@ class AdblockWebContentObserver
raw_ptr<adblock::SitekeyStorage> sitekey_storage_;
std::unique_ptr<adblock::FrameHierarchyBuilder> frame_hierarchy_builder_;
+ raw_ptr<HostContentSettingsMap> settings_map_ = nullptr;
};
#endif // COMPONENTS_ADBLOCK_CONTENT_BROWSER_ADBLOCK_WEBCONTENTS_OBSERVER_H_
diff --git a/components/adblock/content/browser/content_security_policy_injector_impl.cc b/components/adblock/content/browser/content_security_policy_injector_impl.cc
--- a/components/adblock/content/browser/content_security_policy_injector_impl.cc
+++ b/components/adblock/content/browser/content_security_policy_injector_impl.cc
@@ -36,6 +36,7 @@ std::set<base::StringPiece> GetCspInjections(
const std::vector<GURL> frame_hierarchy_chain) {
TRACE_EVENT1("eyeo", "GetCspInjection", "url", request_url.spec());
std::set<base::StringPiece> injections;
+ if ((true)) return injections;
for (const auto& collection : subscription_collections) {
const auto injection =
collection->GetCspInjections(request_url, frame_hierarchy_chain);
diff --git a/components/adblock/content/browser/element_hider_impl.cc b/components/adblock/content/browser/element_hider_impl.cc
--- a/components/adblock/content/browser/element_hider_impl.cc
+++ b/components/adblock/content/browser/element_hider_impl.cc
@@ -208,12 +208,12 @@ void InsertUserCSSAndApplyElemHidingEmuJS(
if (!frame_host) {
// Render frame host was destroyed before element hiding could be applied.
// This is not a bug, just legitimate a race condition.
- std::move(on_finished).Run(std::move(input));
+ //std::move(on_finished).Run(ElementHider::ElemhideInjectionData{});
return;
}
auto* info = ElementHiderInfo::GetOrCreateForCurrentDocument(frame_host);
if (info->IsElementHidingDone()) {
- std::move(on_finished).Run(ElementHider::ElemhideInjectionData{});
+ //std::move(on_finished).Run(ElementHider::ElemhideInjectionData{});
return;
} else {
info->SetElementHidingDone();
diff --git a/components/adblock/content/browser/subscription_service_factory_base.cc b/components/adblock/content/browser/subscription_service_factory_base.cc
--- a/components/adblock/content/browser/subscription_service_factory_base.cc
+++ b/components/adblock/content/browser/subscription_service_factory_base.cc
@@ -21,6 +21,7 @@
#include <memory>
#include <vector>
+#include "base/rand_util.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
@@ -65,7 +66,7 @@ base::TimeDelta GetUpdateCheckInterval() {
static base::TimeDelta kCheckInterval =
g_update_check_interval_for_testing
? g_update_check_interval_for_testing.value()
- : base::Hours(1);
+ : base::Hours(24) + base::Minutes(base::RandInt(-60,60));
return kCheckInterval;
}
@@ -86,7 +87,8 @@ std::unique_ptr<OngoingSubscriptionRequest> MakeOngoingSubscriptionRequest(
}
ConversionResult ConvertFilterFile(const GURL& subscription_url,
- const base::FilePath& path) {
+ const base::FilePath& path,
+ bool allow_privileged_filter) {
TRACE_EVENT1("eyeo", "ConvertFileToFlatbuffer", "url",
subscription_url.spec());
ConversionResult result;
@@ -96,7 +98,7 @@ ConversionResult ConvertFilterFile(const GURL& subscription_url,
} else {
result = FlatbufferConverter::Convert(
input_stream, subscription_url,
- config::AllowPrivilegedFilters(subscription_url));
+ allow_privileged_filter && config::AllowPrivilegedFilters(subscription_url));
}
base::DeleteFile(path);
return result;
@@ -201,10 +203,11 @@ SubscriptionServiceFactoryBase::ConvertCustomFilters(
void SubscriptionServiceFactoryBase::ConvertFilterListFile(
const GURL& subscription_url,
const base::FilePath& path,
+ bool allow_privileged_filter,
base::OnceCallback<void(ConversionResult)> result_callback) const {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
- base::BindOnce(&ConvertFilterFile, subscription_url, path),
+ base::BindOnce(&ConvertFilterFile, subscription_url, path, allow_privileged_filter),
std::move(result_callback));
}
diff --git a/components/adblock/content/browser/subscription_service_factory_base.h b/components/adblock/content/browser/subscription_service_factory_base.h
--- a/components/adblock/content/browser/subscription_service_factory_base.h
+++ b/components/adblock/content/browser/subscription_service_factory_base.h
@@ -39,6 +39,7 @@ class SubscriptionServiceFactoryBase : public BrowserContextKeyedServiceFactory,
void ConvertFilterListFile(
const GURL& subscription_url,
const base::FilePath& path,
+ bool allow_privileged_filter,
base::OnceCallback<void(ConversionResult)>) const override;
protected:
diff --git a/components/adblock/core/BUILD.gn b/components/adblock/core/BUILD.gn
--- a/components/adblock/core/BUILD.gn
+++ b/components/adblock/core/BUILD.gn
@@ -14,7 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with eyeo Chromium SDK. If not, see <http://www.gnu.org/licenses/>.
-import("//components/adblock/features.gni")
import("//third_party/flatbuffers/flatbuffer.gni")
flatbuffer("schema") {
@@ -61,48 +60,14 @@ generate_sha256_header("schema_hash") {
files_to_hash = [ "${target_gen_dir}/schema/filter_list_schema_generated.h" ]
}
-config("eyeo_telemetry_config") {
- defines = []
-
- if (eyeo_telemetry_server_url != "") {
- # Explicitly setting Telemetry server URL, used for testing with a test
- # server.
- defines += [ "EYEO_TELEMETRY_SERVER_URL=\"$eyeo_telemetry_server_url\"" ]
- } else {
- # Implicitly setting production Telemetry server URL based on
- # eyeo_telemetry_client_id (or a default client id as a fallback).
- if (eyeo_telemetry_client_id != "") {
- defines += [ "EYEO_TELEMETRY_CLIENT_ID=\"$eyeo_telemetry_client_id\"" ]
- } else {
- print("WARNING! gn arg eyeo_telemetry_client_id is not set. " +
- "Users will not be counted correctly by eyeo.")
- eyeo_telemetry_client_id = "eyeochromium"
- }
- eyeo_telemetry_server_url =
- "https://${eyeo_telemetry_client_id}.telemetry.eyeo.com/"
- defines += [ "EYEO_TELEMETRY_SERVER_URL=\"$eyeo_telemetry_server_url\"" ]
- }
-
- if (eyeo_telemetry_activeping_auth_token != "") {
- defines += [ "EYEO_TELEMETRY_ACTIVEPING_AUTH_TOKEN=\"$eyeo_telemetry_activeping_auth_token\"" ]
- } else {
- print("WARNING! gn arg eyeo_telemetry_activeping_auth_token is not set. " +
- "Users will not be counted correctly by eyeo.")
- }
-}
-
source_set("core") {
output_name = "adblock_core"
sources = [
- "activeping_telemetry_topic_provider.cc",
- "activeping_telemetry_topic_provider.h",
"adblock_controller.h",
"adblock_controller_impl.cc",
"adblock_controller_impl.h",
"adblock_switches.cc",
"adblock_switches.h",
- "adblock_telemetry_service.cc",
- "adblock_telemetry_service.h",
"features.cc",
"features.h",
"sitekey_storage.h",
@@ -126,8 +91,6 @@ source_set("core") {
"//components/prefs",
"//components/version_info",
]
-
- configs += [ ":eyeo_telemetry_config" ]
}
source_set("test_support") {
@@ -170,6 +133,4 @@ source_set("unit_tests") {
"//services/network:test_support",
"//testing/gtest",
]
-
- configs += [ ":eyeo_telemetry_config" ]
}
diff --git a/components/adblock/core/activeping_telemetry_topic_provider.cc b/components/adblock/core/activeping_telemetry_topic_provider.cc
deleted file mode 100644
--- a/components/adblock/core/activeping_telemetry_topic_provider.cc
+++ /dev/null
@@ -1,285 +0,0 @@
-/* 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "components/adblock/core/activeping_telemetry_topic_provider.h"
-
-#include "base/json/json_reader.h"
-#include "base/json/json_writer.h"
-#include "base/system/sys_info.h"
-#include "base/time/time.h"
-#include "base/time/time_to_iso8601.h"
-#include "base/uuid.h"
-#include "components/adblock/core/common/adblock_prefs.h"
-#include "components/adblock/core/subscription/subscription_config.h"
-
-namespace adblock {
-namespace {
-int g_http_port_for_testing = 0;
-std::optional<base::TimeDelta> g_time_delta_for_testing;
-
-GURL GetUrl() {
- GURL url(EYEO_TELEMETRY_SERVER_URL);
- if (!g_http_port_for_testing) {
- return url;
- }
- DCHECK_EQ(url::kHttpsScheme, url.scheme());
- GURL::Replacements replacements;
- replacements.SetSchemeStr(url::kHttpScheme);
- const std::string port_str = base::NumberToString(g_http_port_for_testing);
- replacements.SetPortStr(port_str);
- return url.ReplaceComponents(replacements);
-}
-
-base::TimeDelta GetNormalPingInterval() {
- static base::TimeDelta kNormalPingInterval =
- g_time_delta_for_testing ? g_time_delta_for_testing.value()
- : base::Hours(12);
- return kNormalPingInterval;
-}
-
-base::TimeDelta GetRetryPingInterval() {
- static base::TimeDelta kRetryPingInterval =
- g_time_delta_for_testing ? g_time_delta_for_testing.value()
- : base::Hours(1);
- return kRetryPingInterval;
-}
-
-void AppendStringIfPresent(PrefService* pref_service,
- const std::string& pref_name,
- base::StringPiece payload_key,
- base::Value::Dict& payload) {
- auto str = pref_service->GetString(pref_name);
- if (!str.empty()) {
- payload.Set(payload_key, std::move(str));
- }
-}
-} // namespace
-
-ActivepingTelemetryTopicProvider::ActivepingTelemetryTopicProvider(
- utils::AppInfo app_info,
- PrefService* pref_service,
- SubscriptionService* subscription_service,
- const GURL& base_url,
- const std::string& auth_token)
- : app_info_(std::move(app_info)),
- pref_service_(pref_service),
- subscription_service_(subscription_service),
- base_url_(base_url),
- auth_token_(auth_token) {}
-
-ActivepingTelemetryTopicProvider::~ActivepingTelemetryTopicProvider() = default;
-
-// static
-GURL ActivepingTelemetryTopicProvider::DefaultBaseUrl() {
-#if !defined(EYEO_TELEMETRY_CLIENT_ID)
- LOG(WARNING)
- << "[eyeo] Using default Telemetry server since a Telemetry client ID "
- "was "
- "not provided. Users will not be counted correctly by eyeo. Please "
- "set an ID via \"eyeo_telemetry_client_id\" gn argument.";
-#endif
- return GetUrl();
-}
-
-// static
-std::string ActivepingTelemetryTopicProvider::DefaultAuthToken() {
-#if defined(EYEO_TELEMETRY_ACTIVEPING_AUTH_TOKEN)
- DVLOG(1) << "[eyeo] Using " << EYEO_TELEMETRY_ACTIVEPING_AUTH_TOKEN
- << " as Telemetry authentication token";
- return EYEO_TELEMETRY_ACTIVEPING_AUTH_TOKEN;
-#else
- LOG(WARNING)
- << "[eyeo] No Telemetry authentication token defined. Users will "
- "not be counted correctly by eyeo. Please set a token via "
- "\"eyeo_telemetry_activeping_auth_token\" gn argument.";
- return "";
-#endif
-}
-
-GURL ActivepingTelemetryTopicProvider::GetEndpointURL() const {
- return base_url_.Resolve("/topic/eyeochromium_activeping/version/1");
-}
-
-std::string ActivepingTelemetryTopicProvider::GetAuthToken() const {
- return auth_token_;
-}
-
-void ActivepingTelemetryTopicProvider::GetPayload(
- PayloadCallback callback) const {
- std::string serialized;
- // The only way JSONWriter::Write() can return fail is then the Value
- // contains lists or dicts that are too deep (200 levels). We just built the
- // payload and root objects here, they should be really shallow.
- CHECK(base::JSONWriter::Write(GetPayloadInternal(), &serialized));
- std::move(callback).Run(std::move(serialized));
-}
-
-base::Time ActivepingTelemetryTopicProvider::GetTimeOfNextRequest() const {
- const auto next_ping_time =
- pref_service_->GetTime(common::prefs::kTelemetryNextPingTime);
- // Next ping time may be unset if this is a first run. Next request should
- // happen ASAP.
- if (next_ping_time.is_null()) {
- return base::Time::Now();
- }
-
- return next_ping_time;
-}
-
-void ActivepingTelemetryTopicProvider::ParseResponse(
- std::unique_ptr<std::string> response_content) {
- if (!response_content) {
- VLOG(1) << "[eyeo] Telemetry ping failed, no response from server";
- ScheduleNextPing(GetRetryPingInterval());
- return;
- }
-
- VLOG(1) << "[eyeo] Response from Telemetry server: " << *response_content;
- auto parsed = base::JSONReader::ReadDict(*response_content);
- if (!parsed) {
- VLOG(1)
- << "[eyeo] Telemetry ping failed, response could not be parsed as JSON";
- ScheduleNextPing(GetRetryPingInterval());
- return;
- }
-
- auto* error_message = parsed->FindString("error");
- if (error_message) {
- VLOG(1) << "[eyeo] Telemetry ping failed, error message: "
- << *error_message;
- ScheduleNextPing(GetRetryPingInterval());
- return;
- }
-
- // For legacy reasons, "ping_response_time" is sent to us as "token". This
- // should be the server time of when the ping was handled, possibly truncated
- // for anonymity. We don't parse it or interpret it, just send it back with
- // next ping.
- auto* ping_response_time = parsed->FindString("token");
- if (!ping_response_time) {
- VLOG(1) << "[eyeo] Telemetry ping failed, response did not contain a last "
- "ping / token value";
- ScheduleNextPing(GetRetryPingInterval());
- return;
- }
-
- VLOG(1) << "[eyeo] Telemetry ping succeeded";
- ScheduleNextPing(GetNormalPingInterval());
- UpdatePrefs(*ping_response_time);
-}
-
-void ActivepingTelemetryTopicProvider::FetchDebugInfo(
- DebugInfoCallback callback) const {
- base::Value::Dict debug_info;
- debug_info.Set("endpoint_url", GetEndpointURL().spec());
- debug_info.Set("payload", GetPayloadInternal());
- debug_info.Set("first_ping",
- pref_service_->GetString(
- adblock::common::prefs::kTelemetryFirstPingTime));
- debug_info.Set("time_of_next_request",
- base::TimeToISO8601(GetTimeOfNextRequest()));
- debug_info.Set(
- "last_ping",
- pref_service_->GetString(adblock::common::prefs::kTelemetryLastPingTime));
- debug_info.Set("previous_last_ping",
- pref_service_->GetString(
- adblock::common::prefs::kTelemetryPreviousLastPingTime));
- debug_info.Set("next_ping",
- base::TimeToISO8601(pref_service_->GetTime(
- adblock::common::prefs::kTelemetryNextPingTime)));
-
- std::string serialized;
- // The only way JSONWriter::Write() can return fail is then the Value
- // contains lists or dicts that are too deep (200 levels). We just built the
- // payload and root objects here, they should be really shallow.
- CHECK(base::JSONWriter::WriteWithOptions(
- debug_info, base::JsonOptions::OPTIONS_PRETTY_PRINT, &serialized));
- std::move(callback).Run(std::move(serialized));
-}
-
-void ActivepingTelemetryTopicProvider::ScheduleNextPing(base::TimeDelta delay) {
- pref_service_->SetTime(common::prefs::kTelemetryNextPingTime,
- base::Time::Now() + delay);
-}
-
-void ActivepingTelemetryTopicProvider::UpdatePrefs(
- const std::string& ping_response_time) {
- // First ping is only set once per client.
- if (pref_service_->GetString(common::prefs::kTelemetryFirstPingTime)
- .empty()) {
- pref_service_->SetString(common::prefs::kTelemetryFirstPingTime,
- ping_response_time);
- }
- // Previous-to-last becomes last, last becomes current.
- pref_service_->SetString(
- common::prefs::kTelemetryPreviousLastPingTime,
- pref_service_->GetString(common::prefs::kTelemetryLastPingTime));
- pref_service_->SetString(common::prefs::kTelemetryLastPingTime,
- ping_response_time);
- // Generate a new random tag that wil be sent along with ping times in the
- // next request.
- const auto tag = base::Uuid::GenerateRandomV4();
- pref_service_->SetString(common::prefs::kTelemetryLastPingTag,
- tag.AsLowercaseString());
-}
-
-base::Value ActivepingTelemetryTopicProvider::GetPayloadInternal() const {
- base::Value::Dict payload;
- bool aa_enabled = false;
- auto* adblock_configuration =
- subscription_service_->GetAdblockFilteringConfiguration();
- if (adblock_configuration) {
- aa_enabled = base::ranges::any_of(
- adblock_configuration->GetFilterLists(),
- [&](const auto& url) { return url == AcceptableAdsUrl(); });
- }
- payload.Set("addon_name", "eyeo-chromium-sdk");
- payload.Set("addon_version", "2.0.0");
- payload.Set("application", app_info_.name);
- payload.Set("application_version", app_info_.version);
- payload.Set("aa_active", aa_enabled);
- payload.Set("platform", base::SysInfo::OperatingSystemName());
- payload.Set("platform_version", base::SysInfo::OperatingSystemVersion());
- // Server requires the following parameters to either have a correct,
- // non-empty value, or not be present at all. We shall not send empty strings.
- AppendStringIfPresent(pref_service_, common::prefs::kTelemetryLastPingTag,
- "last_ping_tag", payload);
- AppendStringIfPresent(pref_service_, common::prefs::kTelemetryFirstPingTime,
- "first_ping", payload);
- AppendStringIfPresent(pref_service_, common::prefs::kTelemetryLastPingTime,
- "last_ping", payload);
- AppendStringIfPresent(pref_service_,
- common::prefs::kTelemetryPreviousLastPingTime,
- "previous_last_ping", payload);
-
- base::Value::Dict root;
- root.Set("payload", std::move(payload));
- return base::Value(std::move(root));
-}
-
-// static
-void ActivepingTelemetryTopicProvider::SetHttpPortForTesting(
- int http_port_for_testing) {
- g_http_port_for_testing = http_port_for_testing;
-}
-
-// static
-void ActivepingTelemetryTopicProvider::SetIntervalsForTesting(
- base::TimeDelta time_delta) {
- g_time_delta_for_testing = time_delta;
-}
-
-} // namespace adblock
diff --git a/components/adblock/core/activeping_telemetry_topic_provider.h b/components/adblock/core/activeping_telemetry_topic_provider.h
deleted file mode 100644
--- a/components/adblock/core/activeping_telemetry_topic_provider.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef COMPONENTS_ADBLOCK_CORE_ACTIVEPING_TELEMETRY_TOPIC_PROVIDER_H_
-#define COMPONENTS_ADBLOCK_CORE_ACTIVEPING_TELEMETRY_TOPIC_PROVIDER_H_
-
-#include "base/memory/raw_ptr.h"
-#include "base/time/time.h"
-#include "components/adblock/core/adblock_telemetry_service.h"
-#include "components/adblock/core/common/adblock_utils.h"
-#include "components/adblock/core/subscription/subscription_service.h"
-#include "components/prefs/pref_service.h"
-
-namespace adblock {
-
-// Telemetry topic provider that uploads user-counting data for periodic pings.
-// Provides the following data in Payload:
-// - Last ping time, previous-to-last ping time, first ping time
-// - Unique, non-persistent tag for disambiguating pings made by clients in
-// the same day
-// - Whether Acceptable Ads is enabled
-// - Application name & version, platform name & version
-// Note: Provides no user-identifiable information, no persistent tracking
-// data (ie. no traceable UUID) and no information about user actions.
-class ActivepingTelemetryTopicProvider final
- : public AdblockTelemetryService::TopicProvider {
- public:
- ActivepingTelemetryTopicProvider(utils::AppInfo app_info,
- PrefService* pref_service,
- SubscriptionService* subscription_service,
- const GURL& base_url,
- const std::string& auth_token);
- ~ActivepingTelemetryTopicProvider() final;
-
- static GURL DefaultBaseUrl();
- static std::string DefaultAuthToken();
-
- GURL GetEndpointURL() const final;
- std::string GetAuthToken() const final;
- void GetPayload(PayloadCallback callback) const final;
-
- // Normally 12 hours since last ping, 1 hour in case of retries.
- base::Time GetTimeOfNextRequest() const final;
-
- // Attempts to parse "token" (an opaque server description of last ping time)
- // from |response_content|.
- void ParseResponse(std::unique_ptr<std::string> response_content) final;
-
- void FetchDebugInfo(DebugInfoCallback callback) const final;
-
- // Sets the port used by the embedded http server required for browser tests.
- // Must be called before the first call to DefaultBaseUrl().
- static void SetHttpPortForTesting(int http_port_for_testing);
-
- // Sets the internal timing for sending pings required for browser tests.
- // Must be called before AdblockTelemetryService::Start().
- static void SetIntervalsForTesting(base::TimeDelta time_delta);
-
- private:
- void ScheduleNextPing(base::TimeDelta delay);
- void UpdatePrefs(const std::string& ping_response_time);
- base::Value GetPayloadInternal() const;
-
- const utils::AppInfo app_info_;
- raw_ptr<PrefService> pref_service_;
- raw_ptr<SubscriptionService> subscription_service_;
- const GURL base_url_;
- const std::string auth_token_;
-};
-
-} // namespace adblock
-
-#endif // COMPONENTS_ADBLOCK_CORE_ACTIVEPING_TELEMETRY_TOPIC_PROVIDER_H_
diff --git a/components/adblock/core/adblock_controller_impl.cc b/components/adblock/core/adblock_controller_impl.cc
--- a/components/adblock/core/adblock_controller_impl.cc
+++ b/components/adblock/core/adblock_controller_impl.cc
@@ -103,6 +103,7 @@ bool AdblockControllerImpl::IsAdblockEnabled() const {
}
void AdblockControllerImpl::SetAcceptableAdsEnabled(bool enabled) {
+ enabled = false;
if (enabled) {
InstallSubscription(AcceptableAdsUrl());
} else {
@@ -117,6 +118,7 @@ bool AdblockControllerImpl::IsAcceptableAdsEnabled() const {
}
void AdblockControllerImpl::InstallSubscription(const GURL& url) {
+ if (url == AcceptableAdsUrl()) return;
adblock_filtering_configuration_->AddFilterList(url);
}
@@ -165,10 +167,8 @@ void AdblockControllerImpl::RunFirstRunLogic(PrefService* pref_service) {
common::prefs::kInstallFirstStartSubscriptions)) {
// On first run, install additional subscriptions.
for (const auto& cur : known_subscriptions_) {
- if (cur.first_run == SubscriptionFirstRunBehavior::Subscribe) {
- if (cur.url == AcceptableAdsUrl() &&
- base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableAcceptableAds)) {
+ if (cur.first_run == SubscriptionFirstRunBehavior::SubscribeAtFirstRun) {
+ if (cur.url == AcceptableAdsUrl()) {
// Do not install Acceptable Ads on first run because a command line
// switch forbids it. Mostly used for testing.
continue;
@@ -236,7 +236,7 @@ void AdblockControllerImpl::InstallLanguageBasedRecommendedSubscriptions() {
SubscriptionFirstRunBehavior::SubscribeIfLocaleMatch &&
std::find(subscription.languages.begin(), subscription.languages.end(),
language_) != subscription.languages.end()) {
- VLOG(1) << "[eyeo] Using recommended subscription for language \""
+ LOG(INFO) << "[eyeo] Using recommended subscription for language \""
<< language_ << "\": " << subscription.title;
language_specific_subscription_installed = true;
InstallSubscription(subscription.url);
diff --git a/components/adblock/core/adblock_switches.cc b/components/adblock/core/adblock_switches.cc
--- a/components/adblock/core/adblock_switches.cc
+++ b/components/adblock/core/adblock_switches.cc
@@ -19,7 +19,6 @@
namespace adblock::switches {
-const char kDisableAcceptableAds[] = "disable-aa";
const char kDisableAdblock[] = "disable-adblock";
const char kDisableEyeoFiltering[] = "disable-eyeo-filtering";
diff --git a/components/adblock/core/adblock_switches.h b/components/adblock/core/adblock_switches.h
--- a/components/adblock/core/adblock_switches.h
+++ b/components/adblock/core/adblock_switches.h
@@ -20,7 +20,6 @@
namespace adblock::switches {
-extern const char kDisableAcceptableAds[];
extern const char kDisableAdblock[];
extern const char kDisableEyeoFiltering[];
diff --git a/components/adblock/core/adblock_telemetry_service.cc b/components/adblock/core/adblock_telemetry_service.cc
deleted file mode 100644
--- a/components/adblock/core/adblock_telemetry_service.cc
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#include "components/adblock/core/adblock_telemetry_service.h"
-
-#include <string>
-
-#include "base/barrier_callback.h"
-#include "base/functional/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "components/adblock/core/common/adblock_prefs.h"
-#include "components/prefs/pref_service.h"
-#include "net/base/load_flags.h"
-#include "services/network/public/cpp/resource_request.h"
-#include "services/network/public/cpp/simple_url_loader.h"
-#include "services/network/public/mojom/url_response_head.mojom.h"
-
-namespace adblock {
-
-namespace {
-
-const char kDataType[] = "application/json";
-net::NetworkTrafficAnnotationTag kTrafficAnnotation =
- net::DefineNetworkTrafficAnnotation("adblock_telemetry_request", R"(
- semantics {
- sender: "AdblockTelemetryService"
- description:
- "Messages sent to telemetry.eyeo.com to report usage statistics."
- "Contain no user-identifiable data."
- trigger:
- "Periodic, several times a day."
- data:
- "Subject to change: "
- "Dates of first ping, last ping and previous-to-last ping. "
- "A non-persistent, unique ID that disambiguates pings made in the "
- "same day. "
- "Application name and version (ex. Chromium 86.0.4240.183). "
- "Platform name and version (ex. Windows 10). "
- "Whether Acceptable Ads are in use (yes/no)."
- destination: WEBSITE
- }
- policy {
- cookies_allowed: NO
- setting:
- "Enabled or disabled via 'Ad blocking' setting."
- policy_exception_justification:
- "Parent setting may be controlled by policy"
- }
- })");
-
-} // namespace
-
-// Represents an ongoing chain of requests relevant to a Topic.
-// A Topic is and endpoint on the Telemetry server that expects messages
-// about a domain of activity, ex. usage of Acceptable Ads or frequency of
-// filter "hits" per filter list. The browser may report on multiple topics.
-// Messages are sent periodically. The interval of communication and the
-// content of the messages is provided by a TopicProvider.
-class AdblockTelemetryService::Conversation {
- public:
- Conversation(
- std::unique_ptr<TopicProvider> topic_provider,
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
- : topic_provider_(std::move(topic_provider)),
- url_loader_factory_(url_loader_factory) {}
-
- bool IsRequestDue() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- const auto due_time = topic_provider_->GetTimeOfNextRequest();
- if (due_time > base::Time::Now()) {
- VLOG(1) << "[eyeo] Telemetry request for "
- << topic_provider_->GetEndpointURL()
- << " not due yet, should run at " << due_time;
- return false;
- }
- if (IsRequestInFlight()) {
- VLOG(1) << "[eyeo] Telemetry request for "
- << topic_provider_->GetEndpointURL() << " already in-flight";
- return false;
- }
- VLOG(1) << "[eyeo] Telemetry request for "
- << topic_provider_->GetEndpointURL() << " is due";
- return true;
- }
-
- void StartRequest() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- VLOG(1) << "[eyeo] Telemetry request for "
- << topic_provider_->GetEndpointURL() << " starting now";
- topic_provider_->GetPayload(base::BindOnce(&Conversation::MakeRequest,
- weak_ptr_factory_.GetWeakPtr()));
- }
-
- void Stop() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- url_loader_.reset();
- }
-
- const std::unique_ptr<TopicProvider>& GetTopicProvider() const {
- return topic_provider_;
- }
-
- private:
- bool IsRequestInFlight() {
- return url_loader_ != nullptr || weak_ptr_factory_.HasWeakPtrs();
- }
-
- void MakeRequest(std::string payload) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- auto request = std::make_unique<network::ResourceRequest>();
- request->url = topic_provider_->GetEndpointURL();
- VLOG(1) << "[eyeo] Sending request to: " << request->url;
- request->method = net::HttpRequestHeaders::kPostMethod;
- // The server expects authorization via a bearer token. The token may be
- // empty in testing builds.
- const auto auth_token = topic_provider_->GetAuthToken();
- if (!auth_token.empty()) {
- request->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
- "Bearer " + auth_token);
- }
- // Notify the server we're expecting a JSON response.
- request->headers.SetHeader(net::HttpRequestHeaders::kAccept, kDataType);
- // Disallow using cache - identical requests should be physically sent to
- // the server.
- request->load_flags = net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE;
- // Omitting credentials prevents cookies from being sent. The server does
- // not expect or parse cookies, but we want to be on the safe side,
- // privacy-wise.
- request->credentials_mode = network::mojom::CredentialsMode::kOmit;
-
- // If any url_loader_ existed previously, it will be overwritten and its
- // request will be cancelled.
- url_loader_ = network::SimpleURLLoader::Create(std::move(request),
- kTrafficAnnotation);
-
- VLOG(2) << "[eyeo] Payload: " << payload;
- url_loader_->AttachStringForUpload(payload, kDataType);
- // The Telemetry server responds with a JSON that contains a description of
- // any potential error. We want to parse this JSON if possible, we're not
- // content with just an HTTP error code. Process the response content even
- // if the code is not 200.
- url_loader_->SetAllowHttpErrorResults(true);
-
- url_loader_->DownloadToString(
- url_loader_factory_.get(),
- base::BindOnce(&Conversation::OnResponseArrived,
- base::Unretained(this)),
- network::SimpleURLLoader::kMaxBoundedStringDownloadSize - 1);
- }
-
- void OnResponseArrived(std::unique_ptr<std::string> server_response) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- topic_provider_->ParseResponse(std::move(server_response));
- url_loader_.reset();
- }
-
- SEQUENCE_CHECKER(sequence_checker_);
- std::unique_ptr<TopicProvider> topic_provider_;
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
- std::unique_ptr<network::SimpleURLLoader> url_loader_;
- base::WeakPtrFactory<Conversation> weak_ptr_factory_{this};
-};
-
-AdblockTelemetryService::AdblockTelemetryService(
- FilteringConfiguration* filtering_configuration,
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- base::TimeDelta initial_delay,
- base::TimeDelta check_interval)
- : adblock_filtering_configuration_(filtering_configuration),
- url_loader_factory_(url_loader_factory),
- initial_delay_(initial_delay),
- check_interval_(check_interval) {
- DCHECK(adblock_filtering_configuration_);
- adblock_filtering_configuration_->AddObserver(this);
-}
-
-AdblockTelemetryService::~AdblockTelemetryService() {
- DCHECK(adblock_filtering_configuration_);
- adblock_filtering_configuration_->RemoveObserver(this);
-}
-
-void AdblockTelemetryService::AddTopicProvider(
- std::unique_ptr<TopicProvider> topic_provider) {
- ongoing_conversations_.push_back(std::make_unique<Conversation>(
- std::move(topic_provider), url_loader_factory_));
-}
-
-void AdblockTelemetryService::Start() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- OnEnabledStateChangedInternal();
-}
-
-void AdblockTelemetryService::OnEnabledStateChanged(FilteringConfiguration*) {
- OnEnabledStateChangedInternal();
-}
-
-void AdblockTelemetryService::GetTopicProvidersDebugInfo(
- TopicProvidersDebugInfoCallback service_callback) const {
- const auto barrier_callback = base::BarrierCallback<std::string>(
- ongoing_conversations_.size(), std::move(service_callback));
- for (const auto& conversation : ongoing_conversations_) {
- conversation->GetTopicProvider()->FetchDebugInfo(barrier_callback);
- }
-}
-
-void AdblockTelemetryService::OnEnabledStateChangedInternal() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (adblock_filtering_configuration_->IsEnabled() && !timer_.IsRunning()) {
- VLOG(1) << "[eyeo] Starting periodic Telemetry requests";
- timer_.Start(FROM_HERE, initial_delay_,
- base::BindRepeating(&AdblockTelemetryService::RunPeriodicCheck,
- base::Unretained(this)));
- } else if (!adblock_filtering_configuration_->IsEnabled() &&
- timer_.IsRunning()) {
- VLOG(1) << "[eyeo] Stopping periodic Telemetry requests";
- Shutdown();
- }
-}
-
-void AdblockTelemetryService::RunPeriodicCheck() {
- for (auto& conversation : ongoing_conversations_) {
- if (conversation->IsRequestDue()) {
- conversation->StartRequest();
- }
- }
- timer_.Start(FROM_HERE, check_interval_,
- base::BindRepeating(&AdblockTelemetryService::RunPeriodicCheck,
- base::Unretained(this)));
-}
-
-void AdblockTelemetryService::Shutdown() {
- timer_.Stop();
- for (auto& conversation : ongoing_conversations_) {
- conversation->Stop();
- }
-}
-
-} // namespace adblock
diff --git a/components/adblock/core/adblock_telemetry_service.h b/components/adblock/core/adblock_telemetry_service.h
deleted file mode 100644
--- a/components/adblock/core/adblock_telemetry_service.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef COMPONENTS_ADBLOCK_CORE_ADBLOCK_TELEMETRY_SERVICE_H_
-#define COMPONENTS_ADBLOCK_CORE_ADBLOCK_TELEMETRY_SERVICE_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/functional/callback_forward.h"
-#include "base/memory/raw_ptr.h"
-#include "base/sequence_checker.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "components/adblock/core/configuration/filtering_configuration.h"
-#include "components/adblock/core/subscription/subscription_service.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "url/gurl.h"
-
-namespace network {
-class SimpleURLLoader;
-} // namespace network
-
-namespace adblock {
-/**
- * @brief Sends periodic pings to eyeo in order to count active users. Executed
- * from Browser process UI main thread.
- */
-class AdblockTelemetryService : public KeyedService,
- public FilteringConfiguration::Observer {
- public:
- // Provides data and behavior relevant for a Telemetry "topic". A topic could
- // be "counting users" or "reporting filter list hits" for example.
- class TopicProvider {
- public:
- using PayloadCallback = base::OnceCallback<void(std::string payload)>;
- using DebugInfoCallback = base::OnceCallback<void(std::string payload)>;
- virtual ~TopicProvider() = default;
- // Endpoint URL on the Telemetry server onto which requests should be sent.
- virtual GURL GetEndpointURL() const = 0;
- // Authorization bearer token for the endpoint defined by GetEndpointURL().
- virtual std::string GetAuthToken() const = 0;
- // Data uploaded with the request, should be valid for the schema
- // present on the server. Async to allow querying asynchronous data sources.
- virtual void GetPayload(PayloadCallback callback) const = 0;
- // Returns the desired time when AdblockTelemetryService should make the
- // next network request.
- virtual base::Time GetTimeOfNextRequest() const = 0;
- // Parses the response returned by the Telemetry server. |response_content|
- // may be null. Implementation is free to implement a "retry" in case of
- // response errors via GetTimeToNextRequest().
- virtual void ParseResponse(
- std::unique_ptr<std::string> response_content) = 0;
- // Gets debugging info to be logged on chrome://adblock-internals. Do not
- // put any secrets here (tokens, api keys). Asynchronous to allow reusing
- // the async logic of GetPayload, if needed.
- virtual void FetchDebugInfo(DebugInfoCallback callback) const = 0;
- };
- AdblockTelemetryService(
- FilteringConfiguration* filtering_configuration,
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- base::TimeDelta initial_delay,
- base::TimeDelta check_interval);
- ~AdblockTelemetryService() override;
- using TopicProvidersDebugInfoCallback =
- base::OnceCallback<void(std::vector<std::string>)>;
-
- // Add all required topic providers before calling Start().
- void AddTopicProvider(std::unique_ptr<TopicProvider> topic_provider);
-
- // Starts periodic Telemetry requests, provided ad-blocking is enabled.
- // If ad blocking is disabled, the schedule will instead start when
- // ad blocking becomes enabled.
- void Start();
-
- // KeyedService:
- void Shutdown() override;
-
- // FilteringConfiguration::Observer
- void OnEnabledStateChanged(FilteringConfiguration* config) override;
-
- // Collects debug information from all topic providers. Runs |callback| once
- // all topic providers have provided their info.
- void GetTopicProvidersDebugInfo(
- TopicProvidersDebugInfoCallback callback) const;
-
- private:
- void OnEnabledStateChangedInternal();
- void RunPeriodicCheck();
-
- SEQUENCE_CHECKER(sequence_checker_);
- raw_ptr<FilteringConfiguration> adblock_filtering_configuration_;
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
- base::TimeDelta initial_delay_;
- base::TimeDelta check_interval_;
-
- class Conversation;
- std::vector<std::unique_ptr<Conversation>> ongoing_conversations_;
- base::OneShotTimer timer_;
-};
-
-} // namespace adblock
-
-#endif // COMPONENTS_ADBLOCK_CORE_ADBLOCK_TELEMETRY_SERVICE_H_
diff --git a/components/adblock/core/common/BUILD.gn b/components/adblock/core/common/BUILD.gn
--- a/components/adblock/core/common/BUILD.gn
+++ b/components/adblock/core/common/BUILD.gn
@@ -59,14 +59,6 @@ source_set("common") {
config("eyeo_application_config") {
defines = []
-
- if (eyeo_application_name != "") {
- defines += [ "EYEO_APPLICATION_NAME=\"$eyeo_application_name\"" ]
- }
-
- if (eyeo_application_version != "") {
- defines += [ "EYEO_APPLICATION_VERSION=\"$eyeo_application_version\"" ]
- }
}
source_set("utils") {
diff --git a/components/adblock/core/common/adblock_constants.cc b/components/adblock/core/common/adblock_constants.cc
--- a/components/adblock/core/common/adblock_constants.cc
+++ b/components/adblock/core/common/adblock_constants.cc
@@ -23,8 +23,6 @@
namespace adblock {
-const char kSiteKeyHeaderKey[] = "x-adblock-key";
-
const char kAllowlistEverythingFilter[] = "@@*$document";
const char kAdblockFilteringConfigurationName[] = "adblock";
diff --git a/components/adblock/core/common/adblock_constants.h b/components/adblock/core/common/adblock_constants.h
--- a/components/adblock/core/common/adblock_constants.h
+++ b/components/adblock/core/common/adblock_constants.h
@@ -28,7 +28,6 @@ namespace flat {
enum AbpResource : int8_t;
}
-extern const char kSiteKeyHeaderKey[];
extern const char kAllowlistEverythingFilter[];
extern const char kAdblockFilteringConfigurationName[];
diff --git a/components/adblock/core/common/adblock_prefs.cc b/components/adblock/core/common/adblock_prefs.cc
--- a/components/adblock/core/common/adblock_prefs.cc
+++ b/components/adblock/core/common/adblock_prefs.cc
@@ -23,7 +23,9 @@
namespace adblock::common::prefs {
// Whether to block ads
-const char kEnableAdblockLegacy[] = "adblock.enable";
+const char kEnableAdblockLegacy[] = "adblock.enabled";
+
+const char kAllowPrivilegedFilters[] = "adblock.privilegedfilters_enabled";
// Legacy: Whether to allow acceptable ads or block them all.
// Used now just to map CLI switch. Otherwise use kAdblockSubscriptionsLegacy.
@@ -65,47 +67,11 @@ const char kLastUsedSchemaVersion[] = "adblock.last_used_schema_version";
// and for setting query parameters in subscription download requests.
const char kSubscriptionMetadata[] = "adblock.subscription_metadata";
-// Client-generated UUID4 that uniquely identifies the server response that
-// sent kTelemetryLastPingTime. Sent along with other ping times to
-// disambiguate between other clients who send ping requests the same day.
-// Regenerated on every successful response.
-const char kTelemetryLastPingTag[] =
- "adblock.telemetry.activeping.last_ping_tag";
-
-// Server UTC time of last ping response, updated with every successful
-// response. Shall not be compared to client time (even UTC). Sent by the
-// telemetry server, stored as unparsed string (ex. "2022-02-08T09:30:00Z").
-const char kTelemetryLastPingTime[] =
- "adblock.telemetry.activeping.last_ping_time";
-
-// Previous last ping time, gets replaced by kTelemetryLastPingTime when a new
-// successful ping response arrives. Sent in a ping request.
-const char kTelemetryPreviousLastPingTime[] =
- "adblock.telemetry.activeping.previous_last_ping_time";
-
-// Time of first recorded response for a telemetry ping request, sent along
-// with future ping requests, to further disambiguate
-// user-counting without being able to uniquely track a user.
-const char kTelemetryFirstPingTime[] =
- "adblock.telemetry.activeping.first_ping_time";
-
-// Client time, when to perform the next ping?
-// Not sent, used locally to ensure we don't ping too often.
-const char kTelemetryNextPingTime[] =
- "adblock.telemetry.activeping.next_ping_time";
-
-void RegisterTelemetryPrefs(PrefRegistrySimple* registry) {
- registry->RegisterStringPref(kTelemetryLastPingTag, "");
- registry->RegisterStringPref(kTelemetryLastPingTime, "");
- registry->RegisterStringPref(kTelemetryPreviousLastPingTime, "");
- registry->RegisterStringPref(kTelemetryFirstPingTime, "");
- registry->RegisterTimePref(kTelemetryNextPingTime, base::Time());
-}
-
void RegisterProfilePrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(kAllowPrivilegedFilters, false);
registry->RegisterBooleanPref(kEnableAdblockLegacy, true);
- registry->RegisterBooleanPref(kEnableAcceptableAdsLegacy, true);
- registry->RegisterBooleanPref(kAdblockMoreOptionsEnabled, false);
+ registry->RegisterBooleanPref(kEnableAcceptableAdsLegacy, false);
+ registry->RegisterBooleanPref(kAdblockMoreOptionsEnabled, true);
registry->RegisterListPref(kAdblockAllowedDomainsLegacy, {});
registry->RegisterListPref(kAdblockCustomFiltersLegacy, {});
registry->RegisterListPref(kAdblockSubscriptionsLegacy, {});
@@ -114,7 +80,6 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterDictionaryPref(kSubscriptionSignatures);
registry->RegisterStringPref(kLastUsedSchemaVersion, "");
registry->RegisterDictionaryPref(kSubscriptionMetadata);
- RegisterTelemetryPrefs(registry);
VLOG(3) << "[eyeo] Registered prefs";
}
diff --git a/components/adblock/core/common/adblock_prefs.h b/components/adblock/core/common/adblock_prefs.h
--- a/components/adblock/core/common/adblock_prefs.h
+++ b/components/adblock/core/common/adblock_prefs.h
@@ -23,6 +23,7 @@ class PrefRegistrySimple;
namespace adblock::common::prefs {
extern const char kEnableAdblockLegacy[];
+extern const char kAllowPrivilegedFilters[];
extern const char kEnableAcceptableAdsLegacy[];
extern const char kAdblockAllowedDomainsLegacy[];
extern const char kAdblockCustomFiltersLegacy[];
diff --git a/components/adblock/core/common/adblock_utils.cc b/components/adblock/core/common/adblock_utils.cc
--- a/components/adblock/core/common/adblock_utils.cc
+++ b/components/adblock/core/common/adblock_utils.cc
@@ -49,16 +49,6 @@ std::string CreateDomainAllowlistingFilter(const std::string& domain) {
SiteKey GetSitekeyHeader(
const scoped_refptr<net::HttpResponseHeaders>& headers) {
- size_t iterator = 0;
- std::string name;
- std::string value;
- while (headers->EnumerateHeaderLines(&iterator, &name, &value)) {
- std::transform(name.begin(), name.end(), name.begin(),
- [](unsigned char c) { return std::tolower(c); });
- if (name == adblock::kSiteKeyHeaderKey) {
- return SiteKey{value};
- }
- }
return {};
}
@@ -70,19 +60,6 @@ AppInfo::AppInfo(const AppInfo&) = default;
AppInfo GetAppInfo() {
AppInfo info;
-
-#if defined(EYEO_APPLICATION_NAME)
- info.name = EYEO_APPLICATION_NAME;
-#else
- info.name = version_info::GetProductName();
-#endif
-#if defined(EYEO_APPLICATION_VERSION)
- info.version = EYEO_APPLICATION_VERSION;
-#else
- info.version = version_info::GetVersionNumber();
-#endif
- base::ReplaceChars(version_info::GetOSType(), base::kWhitespaceASCII, "",
- &info.client_os);
return info;
}
diff --git a/components/adblock/core/configuration/filtering_configuration.h b/components/adblock/core/configuration/filtering_configuration.h
--- a/components/adblock/core/configuration/filtering_configuration.h
+++ b/components/adblock/core/configuration/filtering_configuration.h
@@ -66,6 +66,9 @@ class FilteringConfiguration {
virtual void SetEnabled(bool enabled) = 0;
virtual bool IsEnabled() const = 0;
+ virtual void SetPrivilegedFiltersEnabled(bool enabled) = 0;
+ virtual bool IsPrivilegedFiltersEnabled() = 0;
+
// Adding an existing filter list, or removing a non-existing filter list, are
// NOPs and do not notify observers.
virtual void AddFilterList(const GURL& url) = 0;
diff --git a/components/adblock/core/configuration/persistent_filtering_configuration.cc b/components/adblock/core/configuration/persistent_filtering_configuration.cc
--- a/components/adblock/core/configuration/persistent_filtering_configuration.cc
+++ b/components/adblock/core/configuration/persistent_filtering_configuration.cc
@@ -25,6 +25,7 @@
#include "components/adblock/core/configuration/filtering_configuration_prefs.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
+#include "components/adblock/core/common/adblock_prefs.h"
#include "base/logging.h"
@@ -135,6 +136,7 @@ const std::string& PersistentFilteringConfiguration::GetName() const {
}
void PersistentFilteringConfiguration::SetEnabled(bool enabled) {
+ pref_service_->SetBoolean(common::prefs::kEnableAdblockLegacy, enabled);
if (IsEnabled() == enabled) {
return;
}
@@ -143,6 +145,14 @@ void PersistentFilteringConfiguration::SetEnabled(bool enabled) {
NotifyEnabledStateChanged();
}
+bool PersistentFilteringConfiguration::IsPrivilegedFiltersEnabled() {
+ return pref_service_->GetBoolean(common::prefs::kAllowPrivilegedFilters);
+}
+
+void PersistentFilteringConfiguration::SetPrivilegedFiltersEnabled(bool enabled) {
+ pref_service_->SetBoolean(common::prefs::kAllowPrivilegedFilters, enabled);
+}
+
bool PersistentFilteringConfiguration::IsEnabled() const {
const auto pref_value = dictionary_.FindBool(kEnabledKey);
DCHECK(pref_value);
diff --git a/components/adblock/core/configuration/persistent_filtering_configuration.h b/components/adblock/core/configuration/persistent_filtering_configuration.h
--- a/components/adblock/core/configuration/persistent_filtering_configuration.h
+++ b/components/adblock/core/configuration/persistent_filtering_configuration.h
@@ -49,6 +49,9 @@ class PersistentFilteringConfiguration final : public FilteringConfiguration {
void SetEnabled(bool enabled) final;
bool IsEnabled() const final;
+ void SetPrivilegedFiltersEnabled(bool enabled) final;
+ bool IsPrivilegedFiltersEnabled() final;
+
void AddFilterList(const GURL& url) final;
void RemoveFilterList(const GURL& url) final;
std::vector<GURL> GetFilterLists() const final;
diff --git a/components/adblock/core/converter/flatbuffer_converter.cc b/components/adblock/core/converter/flatbuffer_converter.cc
--- a/components/adblock/core/converter/flatbuffer_converter.cc
+++ b/components/adblock/core/converter/flatbuffer_converter.cc
@@ -125,7 +125,7 @@ void FlatbufferConverter::ConvertFilter(
std::string(filter_str.data(), filter_str.size()))) {
flatbuffer_serializer.SerializeUrlFilter(std::move(url_filter.value()));
} else {
- VLOG(1) << "[eyeo] Invalid url filter: " << line;
+ LOG(INFO) << "[eyeo] Invalid url filter: " << line;
}
break;
}
diff --git a/components/adblock/core/converter/parser/metadata.cc b/components/adblock/core/converter/parser/metadata.cc
--- a/components/adblock/core/converter/parser/metadata.cc
+++ b/components/adblock/core/converter/parser/metadata.cc
@@ -58,13 +58,6 @@ absl::optional<Metadata> Metadata::FromStream(std::istream& filter_stream) {
if (key == "homepage") {
homepage = value;
} else if (key == "redirect") {
- auto url = GURL(value);
- if (url.is_valid()) {
- redirect_url = url;
- } else {
- VLOG(1) << "[eyeo] Invalid redirect URL: " << value
- << ". Will not redirect.";
- }
} else if (key == "title") {
title = value;
} else if (key == "version") {
@@ -107,6 +100,7 @@ Metadata::~Metadata() = default;
// static
bool Metadata::IsValidAdblockHeader(const std::string& adblock_header) {
+ if ((true)) return true;
static re2::RE2 adblock_header_re("^\\[Adblock.*\\]");
std::string adblock_header_trimmed;
@@ -131,7 +125,7 @@ base::TimeDelta Metadata::ParseExpirationTime(
if (!re2::RE2::FullMatch(expiration_value, expiration_time_re,
&expiration_time, &expiration_unit)) {
- VLOG(1) << "[eyeo] Invalid expiration time format: " << expiration_value
+ LOG(ERROR) << "[eyeo] Invalid expiration time format: " << expiration_value
<< ". Will use default value of "
<< kDefaultExpirationInterval.InDays() << " days.";
return kDefaultExpirationInterval;
diff --git a/components/adblock/core/converter/parser/test/test_rules.txt b/components/adblock/core/converter/parser/test/test_rules.txt
new file mode 100644
--- /dev/null
+++ b/components/adblock/core/converter/parser/test/test_rules.txt
@@ -0,0 +1,21 @@
+! gn gen --args="is_component_build=false is_debug=false target_os=\"linux\"" --filters="//components/adblock/core/converter/parser" out/linux
+! date && autoninja -C out/linux components/adblock/core/converter:adblock_flatbuffer_converter && date
+! out/linux/adblock_flatbuffer_converter components/adblock/core/converter/parser/test/test_rules.txt http://localhost b
+!
+$csp=script-src: 'none',domain=example.org|~example.com
+$csp=base-uri,domain=example.org|~example.com
+$csp=script-src 'self' '*' 'unsafe-inline',domain=pirateproxy.live|thehiddenbay.com|downloadpirate.com|thepiratebay10.org|ukpass.co|linksmore.site
+$csp=worker-src 'none',domain=torlock.com|alltube.pl|alltube.tv|centrum-dramy.pl|coinfaucet.eu|crictime.com|crictime.is|doodcdn.com|estream.to|flashx.co|flashx.to|flashx.tv|gomo.to|hdvid.fun|hdvid.tv|hitomi.la|kinox.to|lewd.ninja|assia1.tv|nflbite.com|pirateproxy.live|plytv.me|potomy.ru|powvideo.cc|powvideo.net|putlocker.to|reactor.cc|rojadirecta.watch|sickrage.ca|streamtape.com|thehiddenbay.com|thepiratebay.org|thepiratebay10.org|tpb.party|uptomega.me|ustream.to|vidoza.co|vidoza.net|wallpoper.com|wearesaudis.net|yazilir.com
+@@1337x.to^$csp=script-src 'self' 'unsafe-inline' 'unsafe-eval' data:
+||bodysize.org^$csp=child-src *
+||convertfiles.com^$csp=script-src 'self' '*' 'unsafe-inline'
+||gelbooru.com^$csp=script-src 'self' '*' 'unsafe-inline' *.gstatic.com *.google.com *.googleapis.com *.bootstrapcdn.com
+||moviewatcher.is^$csp=script-src 'self' '*' 'unsafe-inline'
+||pirateiro.com^$csp=script-src 'self' 'unsafe-inline' https://hcaptcha.com *.hcaptcha.com
+! CSP Yavli
+||activistpost.com^$csp=script-src *.leadpages.net *.gstatic.com *.google.com *.googleapis.com *.playwire.com *.facebook.com *.bootstrapcdn.com
+! kinox
+$csp=script-src 'self' 'unsafe-inline' 'unsafe-eval' data: *.cloudflare.com *.google.com *.addthis.com *.addthisedge.com *.facebook.net *.twitter.com *.jquery.com,domain=kinos.to|kinox.am|kinox.bz|kinox.click|kinox.cloud|kinox.club|kinox.digital|kinox.direct|kinox.express|kinox.fun|kinox.fyi|kinox.gratis|kinox.io|kinox.lol|kinox.me|kinox.mobi|kinox.pub|kinox.sh|kinox.sx|kinox.to|kinox.tube|kinox.tv|kinox.wtf|kinoz.to,~third-party
+parenting.pl,echirurgia.pl,dobreprogramy.pl,abczdrowie.pl,wp.pl#$#override-property-read rekid 0
+parenting.pl,echirurgia.pl,dobreprogramy.pl,abczdrowie.pl,wp.pl#$#override-property-read
+parenting.pl,echirurgia.pl,dobreprogramy.pl,abczdrowie.pl,wp.pl#$#override-property-read 1 pippo
diff --git a/components/adblock/core/converter/parser/url_filter.cc b/components/adblock/core/converter/parser/url_filter.cc
--- a/components/adblock/core/converter/parser/url_filter.cc
+++ b/components/adblock/core/converter/parser/url_filter.cc
@@ -47,6 +47,10 @@ std::string SanitizePipeCharacters(std::string pattern) {
// Skip up to one trailing | characters, this is the right anchor.
bool pattern_has_right_anchor = base::EndsWith(piece, "|");
if (pattern_has_right_anchor) {
+ if (piece.size() == 0) {
+ LOG(ERROR) << "[eyeo] Found invalid pattern string '" << pattern << "'";
+ return std::string();
+ }
piece.remove_suffix(1);
}
if (piece.find('|') == base::StringPiece::npos) {
@@ -108,21 +112,21 @@ absl::optional<UrlFilter> UrlFilter::FromString(std::string filter_str) {
if (options->Csp().has_value() && options->Csp().value().empty() &&
!is_allowing) {
- VLOG(1) << "[eyeo] Invalid CSP filter. Blocking CSP filter requires "
+ LOG(INFO) << "[eyeo] Invalid CSP filter. Blocking CSP filter requires "
"directives";
return {};
}
if (options->Headers().has_value() && options->Headers().value().empty() &&
!is_allowing) {
- VLOG(1) << "[eyeo] Invalid header filter. Blocking header filter "
+ LOG(INFO) << "[eyeo] Invalid header filter. Blocking header filter "
"requires directives";
return {};
}
if (!options->IsSubresource() && !options->ExceptionTypes().empty() &&
!is_allowing) {
- VLOG(1) << "[eyeo] Exception options can only be used with allowing "
+ LOG(INFO) << "[eyeo] Exception options can only be used with allowing "
"filters";
return {};
}
diff --git a/components/adblock/core/converter/parser/url_filter_options.cc b/components/adblock/core/converter/parser/url_filter_options.cc
--- a/components/adblock/core/converter/parser/url_filter_options.cc
+++ b/components/adblock/core/converter/parser/url_filter_options.cc
@@ -88,20 +88,14 @@ absl::optional<UrlFilterOptions> UrlFilterOptions::FromString(
}
domains = DomainOption::FromString(value, kDomainOrSitekeySeparator);
} else if (key == "sitekey") {
- if (value.empty()) {
- VLOG(1) << "[eyeo] Sitekey option has to have a value.";
- return {};
- }
- sitekeys = ParseSitekeys(value);
+ // not supported
+ return {};
} else if (key == "csp") {
- if (!IsValidCsp(value)) {
- VLOG(1) << "[eyeo] Invalid CSP filter directives: " << value;
- return {};
- }
- csp = value;
+ // not supported
+ return {};
} else if (key == "header") {
- ParseHeaders(value);
- headers = value;
+ // not supported
+ return {};
} else {
ContentType content_type = ContentTypeFromString(key);
if (content_type != ContentType::Unknown) {
@@ -184,6 +178,7 @@ absl::optional<UrlFilterOptions::RewriteOption> UrlFilterOptions::ParseRewrite(
// static
SiteKeys UrlFilterOptions::ParseSitekeys(const std::string& sitekey_value) {
SiteKeys sitekeys;
+ if ((true)) return sitekeys;
for (auto& sitekey : base::SplitString(
base::ToUpperASCII(sitekey_value), kDomainOrSitekeySeparator,
base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
@@ -195,6 +190,7 @@ SiteKeys UrlFilterOptions::ParseSitekeys(const std::string& sitekey_value) {
// static
bool UrlFilterOptions::IsValidCsp(const std::string& csp_value) {
+ if ((true)) return false;
static re2::RE2 invalid_csp(
"(;|^) "
"?(base-uri|referrer|report-to|report-uri|upgrade-insecure-requests)\\b");
@@ -205,6 +201,7 @@ bool UrlFilterOptions::IsValidCsp(const std::string& csp_value) {
// static
void UrlFilterOptions::ParseHeaders(std::string& headers_value) {
+ if ((true)) return;
// replace \x2c with actual ,
static re2::RE2 r1("([^\\\\])\\\\x2c");
re2::RE2::GlobalReplace(&headers_value, r1, "\\1,");
diff --git a/components/adblock/core/converter/serializer/flatbuffer_serializer.cc b/components/adblock/core/converter/serializer/flatbuffer_serializer.cc
--- a/components/adblock/core/converter/serializer/flatbuffer_serializer.cc
+++ b/components/adblock/core/converter/serializer/flatbuffer_serializer.cc
@@ -21,6 +21,7 @@
#include "base/notreached.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "components/adblock/core/common/adblock_constants.h"
#include "components/adblock/core/common/regex_filter_pattern.h"
#include "components/adblock/core/converter/parser/filter_classifier.h"
@@ -28,6 +29,60 @@
namespace adblock {
+namespace {
+ const char16_t* kAllowedSnippets[] = {
+ // Debugging Snippets
+ u"log", u"debug", u"trace",
+
+ // Performance Snippets
+ u"race",
+
+ // Conditional Hiding Snippets
+ u"hide-if-contains",
+ u"hide-if-contains-image",
+ u"hide-if-contains-similar-text",
+ u"hide-if-contains-visible-text",
+ u"hide-if-contains-and-matches-style",
+ u"hide-if-matches-computed-xpath",
+ u"hide-if-graph-matches",
+ u"hide-if-has-and-matches-style",
+ u"hide-if-labelled-by",
+ u"hide-if-matches-xpath",
+ u"hide-if-shadow-contains",
+
+ // Behavioral Snippets
+ u"abort-current-inline-script",
+ u"abort-on-property-read",
+ u"abort-on-property-write",
+ u"abort-on-iframe-property-read",
+ u"abort-on-iframe-property-write",
+ u"cookie-remover",
+ u"freeze-element",
+ // u"json-override", // unsupported
+ u"json-prune",
+ u"override-property-read",
+ // u"simulate-event-poc", (deprecated)
+ // u"simulate-mouse-event", // unsupported
+ u"prevent-listener",
+ u"strip-fetch-query-parameter"
+ };
+
+ const char16_t* kAllowedPropertyReadValues[] = {
+ u"false", u"true", u"null", u"noopFunc",
+ u"trueFunc", u"falseFunc", u"emptyArray",
+ u"emptyObj", u"undefined",
+ u"0", u"1"
+ };
+
+ template<int N>
+ bool IsInList(const std::u16string& command, const char16_t*(&list)[N]) {
+ for(int t = 0; t < N; ++t)
+ if (base::EqualsCaseInsensitiveASCII(command, list[t]))
+ return true;
+ return false;
+ }
+}
+
class Buffer : public FlatbufferData {
public:
explicit Buffer(flatbuffers::DetachedBuffer&& buffer)
@@ -118,9 +173,25 @@ void FlatbufferSerializer::SerializeContentFilter(
void FlatbufferSerializer::SerializeSnippetFilter(
const SnippetFilter snippet_filter) {
if (!allow_privileged_) {
- VLOG(1) << "[eyeo] Snippet filters not allowed";
+ LOG(INFO) << "[eyeo] Snippet filters not allowed";
return;
}
+ for (const auto& cur : snippet_filter.snippet_script) {
+ auto command = base::UTF8ToUTF16(cur.front());
+ if (!IsInList(command, kAllowedSnippets)) {
+ LOG(INFO) << "[eyeo] Snippet filter command not allowed: " << command;
+ return;
+ }
+ if (base::EqualsCaseInsensitiveASCII(command, "override-property-read")) {
+ for (auto it = cur.begin()+2; it != cur.end(); ++it) {
+ auto p = base::UTF8ToUTF16(*it);
+ if (!IsInList(p, kAllowedPropertyReadValues)) {
+ LOG(INFO) << "[eyeo] Snippet override-property-read param not allowed: " << *it;
+ return;
+ }
+ }
+ }
+ }
std::vector<flatbuffers::Offset<adblock::flat::SnippetFunctionCall>> offsets;
offsets.reserve(snippet_filter.snippet_script.size());
@@ -141,7 +212,7 @@ void FlatbufferSerializer::SerializeSnippetFilter(
void FlatbufferSerializer::SerializeUrlFilter(const UrlFilter url_filter) {
const auto& options = url_filter.options;
if (!allow_privileged_ && options.Headers().has_value()) {
- VLOG(1) << "[eyeo] Header filters not allowed";
+ LOG(INFO) << "[eyeo] Header filters not allowed";
return;
}
diff --git a/components/adblock/core/sitekey_storage_impl.cc b/components/adblock/core/sitekey_storage_impl.cc
--- a/components/adblock/core/sitekey_storage_impl.cc
+++ b/components/adblock/core/sitekey_storage_impl.cc
@@ -37,6 +37,8 @@ void SitekeyStorageImpl::ProcessResponseHeaders(
const GURL& request_url,
const scoped_refptr<net::HttpResponseHeaders>& headers,
const std::string& user_agent) {
+ // remove Acceptable Ads site key processing
+ if ((true)) return;
if (user_agent.empty()) {
LOG(WARNING) << "[eyeo] No user agent info";
return;
@@ -53,6 +55,7 @@ void SitekeyStorageImpl::ProcessResponseHeaders(
absl::optional<std::pair<GURL, SiteKey>>
SitekeyStorageImpl::FindSiteKeyForAnyUrl(const std::vector<GURL>& urls) const {
+ if ((true)) return {};
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (const auto& url : urls) {
auto elem = url_to_sitekey_map_.find(url);
@@ -66,6 +69,8 @@ SitekeyStorageImpl::FindSiteKeyForAnyUrl(const std::vector<GURL>& urls) const {
void SitekeyStorageImpl::ProcessSiteKey(const GURL& request_url,
const SiteKey& site_key,
const std::string& user_agent) {
+ // remove Acceptable Ads site key processing
+ if ((true)) return; // simple caution, never invoked being private
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!site_key.value().empty());
auto site_key_pair = FindSiteKeyForAnyUrl({request_url});
@@ -116,6 +121,8 @@ bool SitekeyStorageImpl::IsSitekeySignatureValid(
const std::string& public_key_b64,
const std::string& signature_b64,
const std::string& data) const {
+ // remove Acceptable Ads site key
+ if ((true)) return false; // simple caution, never invoked being private
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::string signature;
if (!base::Base64Decode(signature_b64, &signature,
diff --git a/components/adblock/core/subscription/conversion_executors.h b/components/adblock/core/subscription/conversion_executors.h
--- a/components/adblock/core/subscription/conversion_executors.h
+++ b/components/adblock/core/subscription/conversion_executors.h
@@ -40,6 +40,7 @@ class ConversionExecutors {
virtual void ConvertFilterListFile(
const GURL& subscription_url,
const base::FilePath& path,
+ bool allow_privileged_filter,
base::OnceCallback<void(ConversionResult)> result_callback) const = 0;
virtual ~ConversionExecutors() = default;
diff --git a/components/adblock/core/subscription/filtering_configuration_maintainer.h b/components/adblock/core/subscription/filtering_configuration_maintainer.h
--- a/components/adblock/core/subscription/filtering_configuration_maintainer.h
+++ b/components/adblock/core/subscription/filtering_configuration_maintainer.h
@@ -24,6 +24,7 @@
#include "base/memory/scoped_refptr.h"
#include "components/adblock/core/subscription/subscription.h"
#include "components/adblock/core/subscription/subscription_collection.h"
+#include "components/adblock/core/subscription/subscription_persistent_metadata.h"
namespace adblock {
@@ -41,6 +42,9 @@ class FilteringConfigurationMaintainer {
virtual std::unique_ptr<SubscriptionCollection> GetSubscriptionCollection()
const = 0;
+ virtual void StartUpdate() = 0;
+ virtual raw_ptr<SubscriptionPersistentMetadata> GetMetadata() = 0;
+
// Allows inspecting what Subscriptions are currently in use. This includes
// ongoing downloads, preloaded subscriptions and installed subscriptions.
virtual std::vector<scoped_refptr<Subscription>> GetCurrentSubscriptions()
diff --git a/components/adblock/core/subscription/filtering_configuration_maintainer_impl.cc b/components/adblock/core/subscription/filtering_configuration_maintainer_impl.cc
--- a/components/adblock/core/subscription/filtering_configuration_maintainer_impl.cc
+++ b/components/adblock/core/subscription/filtering_configuration_maintainer_impl.cc
@@ -254,6 +254,19 @@ void FilteringConfigurationMaintainerImpl::RemoveDuplicateSubscriptions() {
unique_subscriptions.end());
}
+void FilteringConfigurationMaintainerImpl::StartUpdate() {
+ LOG(INFO) << "[eyeo] Running forced update";
+ for (auto& subscription : current_state_) {
+ const auto& url = subscription->GetSourceUrl();
+ DownloadAndInstallSubscription(url);
+ }
+}
+
+raw_ptr<SubscriptionPersistentMetadata>
+ FilteringConfigurationMaintainerImpl::GetMetadata() {
+ return persistent_metadata_;
+}
+
void FilteringConfigurationMaintainerImpl::RunUpdateCheck() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
VLOG(1) << "[eyeo] Running update check";
@@ -286,7 +299,6 @@ void FilteringConfigurationMaintainerImpl::RunUpdateCheck() {
AcceptableAdsUrl();
}) &&
persistent_metadata_->IsExpired(AcceptableAdsUrl())) {
- PingAcceptableAds();
}
}
@@ -378,15 +390,6 @@ void FilteringConfigurationMaintainerImpl::SubscriptionAddedToStorage(
subscription_updated_callback_.Run(subscription->GetSourceUrl());
}
-void FilteringConfigurationMaintainerImpl::PingAcceptableAds() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(IsInitialized());
- downloader_->DoHeadRequest(
- AcceptableAdsUrl(),
- base::BindOnce(&FilteringConfigurationMaintainerImpl::OnHeadRequestDone,
- weak_ptr_factory_.GetWeakPtr()));
-}
-
void FilteringConfigurationMaintainerImpl::OnHeadRequestDone(
const std::string version) {
if (version.empty()) {
@@ -399,20 +402,20 @@ void FilteringConfigurationMaintainerImpl::OnHeadRequestDone(
void FilteringConfigurationMaintainerImpl::UninstallSubscription(
const GURL& subscription_url) {
- DVLOG(1) << "[eyeo] Removing subscription " << subscription_url;
+ LOG(INFO) << "[eyeo] Removing subscription " << subscription_url;
if (!UninstallSubscriptionInternal(subscription_url)) {
- VLOG(1) << "[eyeo] Nothing to remove, subscription not installed "
+ LOG(INFO) << "[eyeo] Nothing to remove, subscription not installed "
<< subscription_url;
return;
}
- if (subscription_url != AcceptableAdsUrl()) {
+ if ((true) || subscription_url != AcceptableAdsUrl()) {
// Remove metadata associated with the subscription. Retain (forever)
// metadata of the Acceptable Ads subscription even when it's no longer
// installed, to allow continued HEAD-only pings for user counting purposes.
persistent_metadata_->RemoveMetadata(subscription_url);
}
UpdatePreloadedSubscriptionProvider();
- VLOG(1) << "[eyeo] Removed subscription " << subscription_url;
+ LOG(INFO) << "[eyeo] Removed subscription " << subscription_url;
}
bool FilteringConfigurationMaintainerImpl::UninstallSubscriptionInternal(
diff --git a/components/adblock/core/subscription/filtering_configuration_maintainer_impl.h b/components/adblock/core/subscription/filtering_configuration_maintainer_impl.h
--- a/components/adblock/core/subscription/filtering_configuration_maintainer_impl.h
+++ b/components/adblock/core/subscription/filtering_configuration_maintainer_impl.h
@@ -57,6 +57,9 @@ class FilteringConfigurationMaintainerImpl
std::vector<scoped_refptr<Subscription>> GetCurrentSubscriptions()
const override;
+ void StartUpdate() final;
+ raw_ptr<SubscriptionPersistentMetadata> GetMetadata() final;
+
// FilteringConfiguration::Observer:
void OnFilterListsChanged(FilteringConfiguration* config) final;
void OnAllowedDomainsChanged(FilteringConfiguration* config) final;
@@ -84,7 +87,6 @@ class FilteringConfigurationMaintainerImpl
void SubscriptionAddedToStorage(
scoped_refptr<OngoingInstallation> ongoing_installation,
scoped_refptr<InstalledSubscription> subscription);
- void PingAcceptableAds();
void OnHeadRequestDone(const std::string version);
void UninstallSubscription(const GURL& subscription_url);
bool UninstallSubscriptionInternal(const GURL& subscription_url);
diff --git a/components/adblock/core/subscription/ongoing_subscription_request_impl.cc b/components/adblock/core/subscription/ongoing_subscription_request_impl.cc
--- a/components/adblock/core/subscription/ongoing_subscription_request_impl.cc
+++ b/components/adblock/core/subscription/ongoing_subscription_request_impl.cc
@@ -22,6 +22,7 @@
#include "base/strings/string_piece_forward.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/trace_event.h"
+#include "net/base/load_flags.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
@@ -44,11 +45,23 @@ const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
"Application name (ex. Chromium) "
"Application version (93.0.4572.0) "
destination: WEBSITE
+ internal {
+ contacts {
+ email: "uazo@users.noreply.github.com"
+ }
+ contacts {
+ email: "uazo@users.noreply.github.com"
+ }
+ }
+ user_data {
+ type: NONE
+ }
+ last_reviewed: "2023-01-01"
}
policy {
cookies_allowed: NO
setting:
- "You enable or disable this feature via 'Ad blocking' setting."
+ "You enable or disable this feature via 'Adblock Enable' pref."
policy_exception_justification: "Not implemented."
})");
@@ -66,7 +79,7 @@ OngoingSubscriptionRequestImpl::OngoingSubscriptionRequestImpl(
OngoingSubscriptionRequestImpl::~OngoingSubscriptionRequestImpl() {
if (!url_.is_empty()) {
- VLOG(1) << "[eyeo] Cancelling download of " << url_;
+ LOG(INFO) << "[eyeo] Finished download of " << url_;
}
net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
}
@@ -92,7 +105,7 @@ void OngoingSubscriptionRequestImpl::Retry() {
return;
}
backoff_entry_->InformOfRequest(false);
- VLOG(1) << "[eyeo] Will retry downloading " << url_ << " in "
+ LOG(INFO) << "[eyeo] Will retry downloading " << url_ << " in "
<< backoff_entry_->GetTimeUntilRelease();
retry_timer_->Start(
FROM_HERE, backoff_entry_->GetTimeUntilRelease(),
@@ -106,7 +119,7 @@ void OngoingSubscriptionRequestImpl::Redirect(GURL redirect_url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!url_.is_empty()) << "Redirect() called before Start()";
DCHECK(url_ != redirect_url) << "Invalid redirect. Same URL";
- VLOG(1) << "[eyeo] Will redirect " << url_ << " to " << redirect_url;
+ LOG(INFO) << "[eyeo] Will redirect " << url_ << " to " << redirect_url;
++number_of_redirects_;
url_ = std::move(redirect_url);
StartInternal();
@@ -126,10 +139,15 @@ void OngoingSubscriptionRequestImpl::StartInternal() {
// indefinitely.
return;
}
- VLOG(1) << "[eyeo] Downloading " << url_;
+ LOG(INFO) << "[eyeo] Downloading " << url_
+ << " with method " << (method_ == Method::GET ? "get" : "headers only");
auto request = std::make_unique<network::ResourceRequest>();
request->url = url_;
request->method = MethodToString();
+ request->credentials_mode = network::mojom::CredentialsMode::kOmit;
+ request->load_flags = net::LOAD_BYPASS_CACHE |
+ net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_MINIMAL_HEADERS;
loader_ =
network::SimpleURLLoader::Create(std::move(request), kTrafficAnnotation);
@@ -152,11 +170,8 @@ void OngoingSubscriptionRequestImpl::OnDownloadFinished(
base::FilePath downloaded_file) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
TRACE_EVENT_NESTABLE_ASYNC_END0("eyeo", "Downloading subscription", this);
- GURL::Replacements strip_query;
- strip_query.ClearQuery();
- GURL url = url_.ReplaceComponents(strip_query);
response_callback_.Run(
- url, std::move(downloaded_file),
+ url_, std::move(downloaded_file),
loader_->ResponseInfo() ? loader_->ResponseInfo()->headers : nullptr);
// response_callback_ may delete this, do not call any member variables now.
}
diff --git a/components/adblock/core/subscription/preloaded_subscription_provider_impl.cc b/components/adblock/core/subscription/preloaded_subscription_provider_impl.cc
--- a/components/adblock/core/subscription/preloaded_subscription_provider_impl.cc
+++ b/components/adblock/core/subscription/preloaded_subscription_provider_impl.cc
@@ -63,10 +63,10 @@ class PreloadedSubscriptionProviderImpl::SingleSubscriptionProvider {
utils::MakeFlatbufferDataFromResourceBundle(
info_.flatbuffer_resource_id),
Subscription::InstallationState::Preloaded, base::Time());
- VLOG(1) << "[eyeo] Preloaded subscription now in use: "
+ LOG(INFO) << "[eyeo] Preloaded subscription now in use: "
<< subscription_->GetSourceUrl();
} else if (!needs_subscription && subscription_) {
- VLOG(1) << "[eyeo] Preloaded subscription no longer in use: "
+ LOG(INFO) << "[eyeo] Preloaded subscription no longer in use: "
<< subscription_->GetSourceUrl();
subscription_.reset();
}
diff --git a/components/adblock/core/subscription/subscription.cc b/components/adblock/core/subscription/subscription.cc
--- a/components/adblock/core/subscription/subscription.cc
+++ b/components/adblock/core/subscription/subscription.cc
@@ -16,9 +16,28 @@
*/
#include "components/adblock/core/subscription/subscription.h"
+#include "base/notreached.h"
namespace adblock {
Subscription::~Subscription() = default;
+// static
+const std::string Subscription::SubscriptionInstallationStateToString(
+ Subscription::InstallationState state) {
+ using State = 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 "";
+}
+
} // namespace adblock
diff --git a/components/adblock/core/subscription/subscription.h b/components/adblock/core/subscription/subscription.h
--- a/components/adblock/core/subscription/subscription.h
+++ b/components/adblock/core/subscription/subscription.h
@@ -68,6 +68,9 @@ class Subscription : public base::RefCountedThreadSafe<Subscription> {
// Typically, update checks are performed once per expiration interval.
virtual base::TimeDelta GetExpirationInterval() const = 0;
+ const static std::string SubscriptionInstallationStateToString(
+ InstallationState state);
+
protected:
friend class base::RefCountedThreadSafe<Subscription>;
virtual ~Subscription();
diff --git a/components/adblock/core/subscription/subscription_collection_impl.cc b/components/adblock/core/subscription/subscription_collection_impl.cc
--- a/components/adblock/core/subscription/subscription_collection_impl.cc
+++ b/components/adblock/core/subscription/subscription_collection_impl.cc
@@ -342,6 +342,7 @@ std::set<HeaderFilterData> SubscriptionCollectionImpl::GetHeaderFilters(
ContentType content_type,
FilterCategory category) const {
std::set<HeaderFilterData> filters{};
+ if ((true)) return filters;
for (const auto& subscription : subscriptions_) {
subscription->FindHeaderFilters(
request_url, content_type, DocumentDomain(request_url, frame_hierarchy),
diff --git a/components/adblock/core/subscription/subscription_config.cc b/components/adblock/core/subscription/subscription_config.cc
--- a/components/adblock/core/subscription/subscription_config.cc
+++ b/components/adblock/core/subscription/subscription_config.cc
@@ -239,17 +239,17 @@ const std::vector<KnownSubscriptionInfo>& config::GetKnownSubscriptions() {
SubscriptionFirstRunBehavior::SubscribeIfLocaleMatch,
SubscriptionPrivilegedFilterStatus::Forbidden},
{AcceptableAdsUrl(),
- "Acceptable Ads",
+ "Acceptable Ads", // Always disable
{},
SubscriptionUiVisibility::Invisible,
- SubscriptionFirstRunBehavior::Subscribe,
+ SubscriptionFirstRunBehavior::Ignore, // in bromite
SubscriptionPrivilegedFilterStatus::Forbidden},
{AntiCVUrl(),
"ABP filters",
{},
- SubscriptionUiVisibility::Visible,
- SubscriptionFirstRunBehavior::Subscribe,
- SubscriptionPrivilegedFilterStatus::Allowed},
+ SubscriptionUiVisibility::Invisible,
+ SubscriptionFirstRunBehavior::Ignore,
+ SubscriptionPrivilegedFilterStatus::AllowedAndChecked},
{GURL(GetHost() + "i_dont_care_about_cookies.txt"),
"I don't care about cookies",
{},
@@ -280,13 +280,13 @@ const std::vector<KnownSubscriptionInfo>& config::GetKnownSubscriptions() {
{},
SubscriptionUiVisibility::Invisible,
SubscriptionFirstRunBehavior::Ignore,
- SubscriptionPrivilegedFilterStatus::Allowed},
+ SubscriptionPrivilegedFilterStatus::Forbidden},
{TestPagesSubscriptionUrl(),
"ABP Test filters",
{},
SubscriptionUiVisibility::Invisible,
SubscriptionFirstRunBehavior::Ignore,
- SubscriptionPrivilegedFilterStatus::Allowed}
+ SubscriptionPrivilegedFilterStatus::Forbidden}
// You can customize subscriptions available on first run and in settings
// here. Items are displayed in settings in order declared here. See
@@ -315,7 +315,7 @@ bool config::AllowPrivilegedFilters(const GURL& url) {
for (const auto& cur : GetKnownSubscriptions()) {
if (cur.url == url) {
return cur.privileged_status ==
- SubscriptionPrivilegedFilterStatus::Allowed;
+ SubscriptionPrivilegedFilterStatus::AllowedAndChecked;
}
}
@@ -325,9 +325,7 @@ bool config::AllowPrivilegedFilters(const GURL& url) {
const std::vector<PreloadedSubscriptionInfo>&
config::GetPreloadedSubscriptionConfiguration() {
static const std::vector<PreloadedSubscriptionInfo> preloaded_subscriptions =
- {{"*easylist.txt", IDR_ADBLOCK_FLATBUFFER_EASYLIST},
- {"*exceptionrules.txt", IDR_ADBLOCK_FLATBUFFER_EXCEPTIONRULES},
- {"*abp-filters-anti-cv.txt", IDR_ADBLOCK_FLATBUFFER_ANTICV}};
+ {};
return preloaded_subscriptions;
}
diff --git a/components/adblock/core/subscription/subscription_config.h b/components/adblock/core/subscription/subscription_config.h
--- a/components/adblock/core/subscription/subscription_config.h
+++ b/components/adblock/core/subscription/subscription_config.h
@@ -37,7 +37,7 @@ enum class SubscriptionUiVisibility { Visible, Invisible };
enum class SubscriptionFirstRunBehavior {
// Download and install as soon as possible.
- Subscribe,
+ SubscribeAtFirstRun,
// Download and install as soon as possible but only if the device's region
// matches one of the |languages| defined in KnownSubscriptionInfo.
SubscribeIfLocaleMatch,
@@ -48,7 +48,7 @@ enum class SubscriptionFirstRunBehavior {
// Privileged filters include:
// - Snippet filters
// - Header filters
-enum class SubscriptionPrivilegedFilterStatus { Allowed, Forbidden };
+enum class SubscriptionPrivilegedFilterStatus { AllowedAndChecked, Forbidden };
// Description of a subscription that's known to exist in the Internet.
// Can be used to populate a list of proposed or recommended subscriptions in
@@ -72,7 +72,7 @@ struct KnownSubscriptionInfo {
std::vector<std::string> languages;
SubscriptionUiVisibility ui_visibility = SubscriptionUiVisibility::Visible;
SubscriptionFirstRunBehavior first_run =
- SubscriptionFirstRunBehavior::Subscribe;
+ SubscriptionFirstRunBehavior::Ignore;
SubscriptionPrivilegedFilterStatus privileged_status =
SubscriptionPrivilegedFilterStatus::Forbidden;
};
diff --git a/components/adblock/core/subscription/subscription_downloader_impl.cc b/components/adblock/core/subscription/subscription_downloader_impl.cc
--- a/components/adblock/core/subscription/subscription_downloader_impl.cc
+++ b/components/adblock/core/subscription/subscription_downloader_impl.cc
@@ -56,6 +56,7 @@ GURL AddUrlParameters(const GURL& subscription_url,
const SubscriptionPersistentMetadata* persistent_metadata,
const utils::AppInfo& client_metadata,
const bool is_disabled) {
+ if ((true)) return subscription_url;
const std::string query = base::StrCat(
{"addonName=", "eyeo-chromium-sdk", "&addonVersion=", "1.0",
"&application=", base::EscapeQueryParamValue(client_metadata.name, true),
@@ -116,6 +117,8 @@ void SubscriptionDownloaderImpl::StartDownload(
}
void SubscriptionDownloaderImpl::CancelDownload(const GURL& subscription_url) {
+ LOG(WARNING) << "[eyeo] Download cancelled: "
+ << subscription_url;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ongoing_downloads_.erase(subscription_url);
}
@@ -140,10 +143,6 @@ void SubscriptionDownloaderImpl::DoHeadRequest(
bool SubscriptionDownloaderImpl::IsUrlAllowed(
const GURL& subscription_url) const {
- if (net::IsLocalhost(subscription_url)) {
- // We trust all localhost urls, regardless of scheme.
- return true;
- }
if (!subscription_url.SchemeIs("https") &&
!subscription_url.SchemeIs("data")) {
return false;
@@ -193,11 +192,11 @@ void SubscriptionDownloaderImpl::OnDownloadFinished(
persistent_metadata_->IncrementDownloadErrorCount(subscription_url);
if (std::get<RetryPolicy>(download_it->second) ==
RetryPolicy::RetryUntilSucceeded) {
- DLOG(WARNING) << "[eyeo] Failed to retrieve content for "
+ LOG(WARNING) << "[eyeo] Failed to retrieve content for "
<< subscription_url << ", will retry";
std::get<OngoingRequestPtr>(download_it->second)->Retry();
} else {
- DLOG(WARNING) << "[eyeo] Failed to retrieve content for "
+ LOG(WARNING) << "[eyeo] Failed to retrieve content for "
<< subscription_url << ", will abort";
std::move(std::get<DownloadCompletedCallback>(download_it->second))
.Run(nullptr);
@@ -214,8 +213,10 @@ void SubscriptionDownloaderImpl::OnDownloadFinished(
TRACE_ID_LOCAL(GenerateTraceId(subscription_url)), "url",
subscription_url.spec());
+ bool allow_privileged_filter =
+ persistent_metadata_->AllowPrivilegedFilters(subscription_url);
conversion_executor_->ConvertFilterListFile(
- subscription_url, downloaded_file,
+ subscription_url, downloaded_file, allow_privileged_filter,
base::BindOnce(&SubscriptionDownloaderImpl::OnConversionFinished,
weak_ptr_factory_.GetWeakPtr(), subscription_url));
}
@@ -229,14 +230,14 @@ void SubscriptionDownloaderImpl::OnConversionFinished(
TRACE_ID_LOCAL(GenerateTraceId(subscription_url)));
const auto download_it = ongoing_downloads_.find(subscription_url);
if (download_it == ongoing_downloads_.end()) {
- VLOG(1) << "[eyeo] Conversion result discarded, subscription download "
+ LOG(WARNING) << "[eyeo] Conversion result discarded, subscription download "
"was cancelled.";
return;
}
if (absl::holds_alternative<std::unique_ptr<FlatbufferData>>(
converter_result)) {
- VLOG(1) << "[eyeo] Finished converting " << subscription_url
+ LOG(WARNING) << "[eyeo] Finished converting " << subscription_url
<< " successfully";
std::move(std::get<DownloadCompletedCallback>(download_it->second))
.Run(std::move(
@@ -279,7 +280,7 @@ void SubscriptionDownloaderImpl::AbortWithWarning(
if (ongoing_download_it == ongoing_downloads_.end()) {
return;
}
- DLOG(WARNING) << "[eyeo] " << warning << " Aborting download of "
+ LOG(WARNING) << "[eyeo] " << warning << " Aborting download of "
<< ongoing_download_it->first;
std::move(std::get<DownloadCompletedCallback>(ongoing_download_it->second))
.Run(nullptr);
diff --git a/components/adblock/core/subscription/subscription_persistent_metadata.h b/components/adblock/core/subscription/subscription_persistent_metadata.h
--- a/components/adblock/core/subscription/subscription_persistent_metadata.h
+++ b/components/adblock/core/subscription/subscription_persistent_metadata.h
@@ -74,6 +74,7 @@ class SubscriptionPersistentMetadata : public KeyedService {
// Returns the number of successful downloads of this subscription in the
// past.
virtual int GetDownloadSuccessCount(const GURL& subscription_url) const = 0;
+ virtual bool AllowPrivilegedFilters(const GURL& subscription_url) = 0;
// Returns number of consecutive download errors.
virtual int GetDownloadErrorCount(const GURL& subscription_url) const = 0;
diff --git a/components/adblock/core/subscription/subscription_persistent_metadata_impl.cc b/components/adblock/core/subscription/subscription_persistent_metadata_impl.cc
--- a/components/adblock/core/subscription/subscription_persistent_metadata_impl.cc
+++ b/components/adblock/core/subscription/subscription_persistent_metadata_impl.cc
@@ -22,6 +22,7 @@
#include "base/time/time.h"
#include "base/values.h"
#include "components/adblock/core/common/adblock_prefs.h"
+#include "components/adblock/core/subscription/subscription_config.h"
#include "components/prefs/scoped_user_pref_update.h"
namespace adblock {
@@ -146,6 +147,12 @@ void SubscriptionPersistentMetadataImpl::UpdatePrefs() {
prefs_->SetDict(common::prefs::kSubscriptionMetadata, std::move(dict));
}
+bool SubscriptionPersistentMetadataImpl::AllowPrivilegedFilters(
+ const GURL& subscription_url) {
+ return prefs_ && prefs_->GetBoolean(common::prefs::kAllowPrivilegedFilters)
+ && config::AllowPrivilegedFilters(subscription_url);
+}
+
void SubscriptionPersistentMetadataImpl::LoadFromPrefs() {
const base::Value& dict =
prefs_->GetValue(common::prefs::kSubscriptionMetadata);
diff --git a/components/adblock/core/subscription/subscription_persistent_metadata_impl.h b/components/adblock/core/subscription/subscription_persistent_metadata_impl.h
--- a/components/adblock/core/subscription/subscription_persistent_metadata_impl.h
+++ b/components/adblock/core/subscription/subscription_persistent_metadata_impl.h
@@ -43,6 +43,7 @@ class SubscriptionPersistentMetadataImpl final
base::Time GetLastInstallationTime(const GURL& subscription_url) const final;
std::string GetVersion(const GURL& subscription_url) const final;
int GetDownloadSuccessCount(const GURL& subscription_url) const final;
+ bool AllowPrivilegedFilters(const GURL& subscription_url) final;
int GetDownloadErrorCount(const GURL& subscription_url) const final;
void RemoveMetadata(const GURL& subscription_url) final;
diff --git a/components/adblock/core/subscription/subscription_persistent_storage_impl.cc b/components/adblock/core/subscription/subscription_persistent_storage_impl.cc
--- a/components/adblock/core/subscription/subscription_persistent_storage_impl.cc
+++ b/components/adblock/core/subscription/subscription_persistent_storage_impl.cc
@@ -98,7 +98,7 @@ SubscriptionPersistentStorageImpl::ReadSubscriptionsFromDirectory(
const base::FilePath& storage_dir,
SubscriptionValidator::IsSignatureValidThreadSafeCallback
is_signature_valid) {
- DLOG(INFO) << "[eyeo] Reading subscriptions from directory";
+ LOG(INFO) << "[eyeo] Reading subscriptions from directory " << storage_dir;
TRACE_EVENT0("eyeo", "ReadSubscriptionsFromDirectory");
// Does nothing if directory already exists:
base::CreateDirectory(storage_dir);
@@ -116,6 +116,8 @@ SubscriptionPersistentStorageImpl::ReadSubscriptionsFromDirectory(
if (!base::ReadFileToString(flatbuffer_path, &contents)) {
// File could not be read.
base::DeleteFile(flatbuffer_path);
+ LOG(INFO) << "[eyeo] Deleting " << flatbuffer_path.BaseName().AsUTF8Unsafe()
+ << "reason: File could not be read";
continue;
}
TRACE_EVENT_END1("eyeo", "ReadFileToString", "path",
@@ -123,6 +125,8 @@ SubscriptionPersistentStorageImpl::ReadSubscriptionsFromDirectory(
TRACE_EVENT_BEGIN0("eyeo", "VerifySubscriptionBuffer");
if (!is_signature_valid.Run(InMemoryFlatbufferData(std::move(contents)),
flatbuffer_path)) {
+ LOG(INFO) << "[eyeo] Deleting " << flatbuffer_path.BaseName().AsUTF8Unsafe()
+ << "reason: This is not a valid subscription file";
// This is not a valid subscription file, remove it.
base::DeleteFile(flatbuffer_path);
continue;
@@ -130,13 +134,16 @@ SubscriptionPersistentStorageImpl::ReadSubscriptionsFromDirectory(
TRACE_EVENT_END0("eyeo", "VerifySubscriptionBuffer");
auto buffer = std::make_unique<MemoryMappedFlatbufferData>(flatbuffer_path);
if (!buffer->data()) {
+ LOG(INFO) << "[eyeo] Could not create mapped memory region to file content for "
+ << flatbuffer_path.BaseName().AsUTF8Unsafe();
// Could not create mapped memory region to file content.
// TODO(mpawlowski) revert to in-memory buffer?
continue;
}
+ LOG(INFO) << "[eyeo] Loaded " << flatbuffer_path.BaseName().AsUTF8Unsafe();
result.emplace_back(std::move(buffer), std::move(flatbuffer_path));
}
- DLOG(INFO) << "[eyeo] Finished reading and validating subscriptions. Loaded "
+ LOG(INFO) << "[eyeo] Finished reading and validating subscriptions. Loaded "
<< result.size() << " subscriptions.";
return result;
}
diff --git a/components/adblock/core/subscription/subscription_service.h b/components/adblock/core/subscription/subscription_service.h
--- a/components/adblock/core/subscription/subscription_service.h
+++ b/components/adblock/core/subscription/subscription_service.h
@@ -38,6 +38,15 @@ namespace adblock {
// FilteringConfigurations.
class SubscriptionService : public KeyedService {
public:
+ virtual void StartUpdate() = 0;
+ virtual raw_ptr<SubscriptionPersistentMetadata> GetMetadata() = 0;
+ virtual raw_ptr<SubscriptionPersistentMetadata> GetMetadataFor(
+ raw_ptr<FilteringConfiguration> configuration) = 0;
+ virtual void SetPrivilegedFiltersEnabled(bool enabled) = 0;
+ virtual bool IsPrivilegedFiltersEnabled() = 0;
+ virtual std::vector<scoped_refptr<Subscription>> GetCustomSubscriptions(
+ FilteringConfiguration* configuration) const = 0;
+
using Snapshot = std::vector<std::unique_ptr<SubscriptionCollection>>;
class SubscriptionObserver : public base::CheckedObserver {
public:
diff --git a/components/adblock/core/subscription/subscription_service_impl.cc b/components/adblock/core/subscription/subscription_service_impl.cc
--- a/components/adblock/core/subscription/subscription_service_impl.cc
+++ b/components/adblock/core/subscription/subscription_service_impl.cc
@@ -36,9 +36,23 @@
#include "components/adblock/core/subscription/filtering_configuration_maintainer.h"
#include "components/adblock/core/subscription/subscription_collection.h"
#include "components/adblock/core/subscription/subscription_service.h"
+#include "components/adblock/core/subscription/subscription_config.h"
namespace adblock {
+namespace {
+
+bool IsKnownSubscription(
+ const std::vector<KnownSubscriptionInfo>& known_subscriptions,
+ const GURL& url) {
+ return base::ranges::any_of(known_subscriptions,
+ [&](const auto& known_subscription) {
+ return known_subscription.url == url;
+ });
+}
+
+}
+
class EmptySubscription : public Subscription {
public:
EmptySubscription(const GURL& url) : url_(url) {}
@@ -133,6 +147,68 @@ void SubscriptionServiceImpl::UninstallFilteringConfiguration(
maintainers_.erase(it);
}
+void SubscriptionServiceImpl::StartUpdate() {
+ for (auto& entry : maintainers_) {
+ if (!entry.second) {
+ continue; // Configuration is disabled
+ }
+ entry.second->StartUpdate();
+ }
+}
+
+bool SubscriptionServiceImpl::IsPrivilegedFiltersEnabled() {
+ auto* adblock_filtering_configuration = GetAdblockFilteringConfiguration();
+ return adblock_filtering_configuration->IsPrivilegedFiltersEnabled();
+}
+
+void SubscriptionServiceImpl::SetPrivilegedFiltersEnabled(bool enabled) {
+ auto* adblock_filtering_configuration = GetAdblockFilteringConfiguration();
+ adblock_filtering_configuration->SetPrivilegedFiltersEnabled(enabled);
+ auto known_subscriptions = adblock::config::GetKnownSubscriptions();
+ for (const auto& cur : known_subscriptions) {
+ if (config::AllowPrivilegedFilters(cur.url)) {
+ if (enabled)
+ adblock_filtering_configuration->AddFilterList(cur.url);
+ else
+ adblock_filtering_configuration->RemoveFilterList(cur.url);
+ }
+ }
+ StartUpdate();
+}
+
+std::vector<scoped_refptr<Subscription>>
+SubscriptionServiceImpl::GetCustomSubscriptions(
+ FilteringConfiguration* configuration) const {
+ std::vector<scoped_refptr<Subscription>> selected =
+ GetCurrentSubscriptions(configuration);
+
+ auto known_subscriptions = adblock::config::GetKnownSubscriptions();
+ selected.erase(base::ranges::remove_if(selected,
+ [&](const auto& subscription) {
+ return IsKnownSubscription(
+ known_subscriptions,
+ subscription->GetSourceUrl());
+ }),
+ selected.end());
+ return selected;
+}
+
+raw_ptr<SubscriptionPersistentMetadata> SubscriptionServiceImpl::GetMetadata() {
+ return GetMetadataFor(GetAdblockFilteringConfiguration());
+}
+
+raw_ptr<SubscriptionPersistentMetadata> SubscriptionServiceImpl::GetMetadataFor(
+ raw_ptr<FilteringConfiguration> configuration) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ auto it = base::ranges::find_if(maintainers_, [&](const auto& entry) {
+ return entry.first.get() == configuration;
+ });
+ if (it != maintainers_.end() && it->second) {
+ return it->second->GetMetadata();
+ }
+ return nullptr;
+}
+
std::vector<FilteringConfiguration*>
SubscriptionServiceImpl::GetInstalledFilteringConfigurations() {
std::vector<FilteringConfiguration*> result;
@@ -178,7 +254,7 @@ void SubscriptionServiceImpl::OnEnabledStateChanged(
});
DCHECK(it != maintainers_.end()) << "Received OnEnabledStateChanged from "
"unregistered FilteringConfiguration";
- VLOG(1) << "[eyeo] FilteringConfiguration " << config->GetName()
+ LOG(INFO) << "[eyeo] FilteringConfiguration " << config->GetName()
<< (config->IsEnabled() ? " enabled" : " disabled");
if (config->IsEnabled()) {
// Enable the configuration by creating a new
diff --git a/components/adblock/core/subscription/subscription_service_impl.h b/components/adblock/core/subscription/subscription_service_impl.h
--- a/components/adblock/core/subscription/subscription_service_impl.h
+++ b/components/adblock/core/subscription/subscription_service_impl.h
@@ -43,6 +43,15 @@ namespace adblock {
class SubscriptionServiceImpl final : public SubscriptionService,
public FilteringConfiguration::Observer {
public:
+ void StartUpdate() override;
+ raw_ptr<SubscriptionPersistentMetadata> GetMetadata() override;
+ raw_ptr<SubscriptionPersistentMetadata> GetMetadataFor(
+ raw_ptr<FilteringConfiguration> configuration) final;
+ void SetPrivilegedFiltersEnabled(bool enabled) override;
+ bool IsPrivilegedFiltersEnabled() override;
+ std::vector<scoped_refptr<Subscription>> GetCustomSubscriptions(
+ FilteringConfiguration* configuration) const override;
+
// Used to notify this about updates to installed subscriptions.
using SubscriptionUpdatedCallback =
base::RepeatingCallback<void(const GURL& subscription_url)>;
diff --git a/components/adblock/core/subscription/subscription_updater_impl.cc b/components/adblock/core/subscription/subscription_updater_impl.cc
--- a/components/adblock/core/subscription/subscription_updater_impl.cc
+++ b/components/adblock/core/subscription/subscription_updater_impl.cc
@@ -38,7 +38,7 @@ void SubscriptionUpdaterImpl::StartSchedule(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!timer_.IsRunning());
run_update_check_ = std::move(run_update_check);
- VLOG(1) << "[eyeo] Starting update schedule, first check scheduled for "
+ LOG(INFO) << "[eyeo] Starting update schedule, first check scheduled for "
<< base::Time::Now() + initial_delay_;
timer_.Start(FROM_HERE, initial_delay_,
base::BindOnce(&SubscriptionUpdaterImpl::RunUpdateCheck,
@@ -47,14 +47,14 @@ void SubscriptionUpdaterImpl::StartSchedule(
void SubscriptionUpdaterImpl::StopSchedule() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- VLOG(1) << "[eyeo] Stopping update schedule";
+ LOG(INFO) << "[eyeo] Stopping update schedule";
timer_.Stop();
}
void SubscriptionUpdaterImpl::RunUpdateCheck() {
- VLOG(1) << "[eyeo] Running subscription update check";
+ LOG(INFO) << "[eyeo] Running subscription update check";
run_update_check_.Run();
- VLOG(1)
+ LOG(INFO)
<< "[eyeo] Subscription update check completed, next one scheduled for "
<< base::Time::Now() + check_interval_;
timer_.Start(FROM_HERE, check_interval_,
diff --git a/components/adblock/core/subscription/subscription_validator_impl.cc b/components/adblock/core/subscription/subscription_validator_impl.cc
--- a/components/adblock/core/subscription/subscription_validator_impl.cc
+++ b/components/adblock/core/subscription/subscription_validator_impl.cc
@@ -64,11 +64,11 @@ bool IsSignatureValidInternal(
const auto* expected_hash = initial_subscription_signatures.FindString(
path.BaseName().AsUTF8Unsafe());
if (!expected_hash) {
- DLOG(WARNING) << "[eyeo] " << path << " has no matching signature in prefs";
+ LOG(WARNING) << "[eyeo] " << path.BaseName().AsUTF8Unsafe() << " has no matching signature in prefs";
return false;
}
if (*expected_hash != ComputeSubscriptionHash(data)) {
- DLOG(WARNING) << "[eyeo] " << path << " has invalid signature in prefs";
+ LOG(WARNING) << "[eyeo] " << path.BaseName().AsUTF8Unsafe() << " has invalid signature in prefs";
return false;
}
return true;
diff --git a/components/content_settings/core/browser/bromite_content_settings/ads.inc b/components/content_settings/core/browser/bromite_content_settings/ads.inc
new file mode 100644
--- /dev/null
+++ b/components/content_settings/core/browser/bromite_content_settings/ads.inc
@@ -0,0 +1,3 @@
+ content_settings::WebsiteSettingsRegistry::GetInstance()
+ ->GetMutable(ContentSettingsType::ADS)
+ ->set_show_into_info_page();
diff --git a/components/resources/BUILD.gn b/components/resources/BUILD.gn
--- a/components/resources/BUILD.gn
+++ b/components/resources/BUILD.gn
@@ -76,7 +76,6 @@ grit("components_resources") {
deps += [
"//components/resources/adblocking:copy_snippets_lib",
- "//components/resources/adblocking:make_all_preloaded_subscriptions",
]
}
diff --git a/components/resources/adblock_resources.grdp b/components/resources/adblock_resources.grdp
--- a/components/resources/adblock_resources.grdp
+++ b/components/resources/adblock_resources.grdp
@@ -21,7 +21,4 @@
<include name="IDR_ADBLOCK_ELEMHIDE_EMU_JS" file="adblocking/elemhideemu.jst" type="BINDATA" />
<include name="IDR_ADBLOCK_SNIPPETS_JS" file="${root_gen_dir}/components/resources/adblocking/snippets.jst" use_base_dir="false" type="BINDATA" compress="gzip" />
<include name="IDR_ADBLOCK_SNIPPETS_XPATH3_DEP_JS" file="${root_gen_dir}/components/resources/adblocking/snippets-xpath3-dep.jst" use_base_dir="false" type="BINDATA" compress="gzip" />
- <include name="IDR_ADBLOCK_FLATBUFFER_EASYLIST" file="${root_gen_dir}/components/resources/adblocking/easylist.fb" use_base_dir="false" type="BINDATA" compress="gzip" />
- <include name="IDR_ADBLOCK_FLATBUFFER_EXCEPTIONRULES" file="${root_gen_dir}/components/resources/adblocking/exceptionrules.fb" use_base_dir="false" type="BINDATA" compress="gzip" />
- <include name="IDR_ADBLOCK_FLATBUFFER_ANTICV" file="${root_gen_dir}/components/resources/adblocking/anticv.fb" use_base_dir="false" type="BINDATA" compress="gzip" />
</grit-part>
diff --git a/components/resources/adblocking/.gitignore b/components/resources/adblocking/.gitignore
--- a/components/resources/adblocking/.gitignore
+++ b/components/resources/adblocking/.gitignore
@@ -1 +1 @@
-snippets
+#snippets
diff --git a/components/resources/adblocking/BUILD.gn b/components/resources/adblocking/BUILD.gn
--- a/components/resources/adblocking/BUILD.gn
+++ b/components/resources/adblocking/BUILD.gn
@@ -18,7 +18,7 @@ import("//build/compiled_action.gni")
# Converts text-format filter lists into flatbuffers using a standalone
# converter tool.
-template("make_preloaded_subscription") {
+template("make_preloaded_subscription_NO") {
compiled_action(target_name) {
tool = "//components/adblock/core/converter:adblock_flatbuffer_converter"
inputs = [ invoker.input ]
@@ -31,34 +31,6 @@ template("make_preloaded_subscription") {
}
}
-# Note, url is *not* used to download the list during build time, only to
-# identify the subscription. Consider it metadata.
-make_preloaded_subscription("make_easylist") {
- input = "//components/resources/adblocking/easylist.txt.gz"
- url = "https://easylist-downloads.adblockplus.org/easylist.txt"
- output = "${target_gen_dir}/easylist.fb"
-}
-
-make_preloaded_subscription("make_exceptionrules") {
- input = "//components/resources/adblocking/exceptionrules.txt.gz"
- url = "https://easylist-downloads.adblockplus.org/exceptionrules.txt"
- output = "${target_gen_dir}/exceptionrules.fb"
-}
-
-make_preloaded_subscription("make_anticv") {
- input = "//components/resources/adblocking/anticv.txt.gz"
- url = "https://easylist-downloads.adblockplus.org/abp-filters-anti-cv.txt"
- output = "${target_gen_dir}/anticv.fb"
-}
-
-group("make_all_preloaded_subscriptions") {
- deps = [
- ":make_anticv",
- ":make_easylist",
- ":make_exceptionrules",
- ]
-}
-
action("prepare_snippets_deps") {
script = "//tools/eyeo/snippets_deps.py"
inputs = ["//components/resources/adblocking/snippets/dist"]
diff --git a/components/resources/adblocking/elemhide_for_selector.jst b/components/resources/adblocking/elemhide_for_selector.jst
--- a/components/resources/adblocking/elemhide_for_selector.jst
+++ b/components/resources/adblocking/elemhide_for_selector.jst
@@ -43,7 +43,7 @@ if (typeof(elemhideForSelector) !== typeof(Function))
}
else
{
- console.debug("Nothing found for selector " + selector + ", retrying elemhide in 100 millis");
+ //console.debug("Nothing found for selector " + selector + ", retrying elemhide in 100 millis");
setTimeout(elemhideForSelector, 100, url, selector, attempt + 1);
}
}
diff --git a/components/resources/adblocking/elemhideemu.jst b/components/resources/adblocking/elemhideemu.jst
--- a/components/resources/adblocking/elemhideemu.jst
+++ b/components/resources/adblocking/elemhideemu.jst
@@ -1,3 +1,4 @@
+(function() {
/*
* This file is part of eyeo Chromium SDK,
* Copyright (C) 2006-present eyeo GmbH
@@ -1434,3 +1435,4 @@ let elemHideEmulation = new ElemHideEmulation(
);
elemHideEmulation.apply(elemHidingEmulatedPatterns);
+})()
diff --git a/components/resources/adblocking/snippets/dist/isolated-first-xpath3.jst b/components/resources/adblocking/snippets/dist/isolated-first-xpath3.jst
new file mode 100644
--- /dev/null
+++ b/components/resources/adblocking/snippets/dist/isolated-first-xpath3.jst
@@ -0,0 +1,62 @@
+(e, ...t) => {
+/*!
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * @eyeo/snippets 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/snippets 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/snippets. If not, see <http://www.gnu.org/licenses/>.
+ */
+ ((environment, ...filters) => {
+const e=Proxy,{apply:t,bind:n,call:o}=Function,r=o.bind(t),i=o.bind(n),s=o.bind(o),a={get:(e,t)=>i(o,e[t])},c=t=>new e(t,a),l={get:(e,t)=>i(e[t],e)},u=t=>new e(t,l),{assign:d,defineProperties:h,freeze:f,getOwnPropertyDescriptor:p,getOwnPropertyDescriptors:w,getPrototypeOf:m}=u(Object),{hasOwnProperty:g}=c({}),{species:b}=Symbol,y={get(e,t){const n=e[t];class o extends n{}const r=w(n.prototype);delete r.constructor,f(h(o.prototype,r));const i=w(n);return delete i.length,delete i.prototype,i[b]={value:o},f(h(o,i))}},v=t=>new e(t,y),S="undefined"!=typeof environment?environment:{};"undefined"==typeof globalThis&&(window.globalThis=window);const{apply:k,ownKeys:E}=u(Reflect),x="world"in S,M=x&&"ISOLATED"===S.world,C=x&&"MAIN"===S.world,T="object"==typeof chrome&&!!chrome.runtime,W="object"==typeof browser&&!!browser.runtime,L=!C&&(M||T||W),O=e=>L?e:P(e,R(e)),{create:P,defineProperties:D,defineProperty:N,freeze:A,getOwnPropertyDescriptor:I,getOwnPropertyDescriptors:R}=u(Object),V=u(globalThis),$=L?globalThis:v(globalThis),{Map:H,RegExp:_,Set:j,WeakMap:X,WeakSet:F}=$,q=(e,t,n=null)=>{const o=E(t);for(const r of E(e)){if(o.includes(r))continue;const i=I(e,r);if(n&&"value"in i){const{value:e}=i;"function"==typeof e&&(i.value=n(e))}N(t,r,i)}},B=e=>{const t=$[e];class n extends t{}const{toString:o,valueOf:r}=t.prototype;D(n.prototype,{toString:{value:o},valueOf:{value:r}});const i=e.toLowerCase(),s=e=>function(){const t=k(e,this,arguments);return typeof t===i?new n(t):t};return q(t,n,s),q(t.prototype,n.prototype,s),n},z=A({frozen:new X,hidden:new F,iframePropertiesToAbort:{read:new j,write:new j},abortedIframes:new X}),U=new _("^[A-Z]");var G=new Proxy(new H([["chrome",L&&(T&&chrome||W&&browser)||void 0],["isExtensionContext",L],["variables",z],["console",O(console)],["document",globalThis.document],["performance",O(performance)],["JSON",O(JSON)],["Map",H],["Math",O(Math)],["Number",L?Number:B("Number")],["RegExp",_],["Set",j],["String",L?String:B("String")],["WeakMap",X],["WeakSet",F],["MouseEvent",MouseEvent]]),{get(e,t){if(e.has(t))return e.get(t);let n=globalThis[t];return"function"==typeof n&&(n=(U.test(t)?$:V)[t]),e.set(t,n),n},has:(e,t)=>e.has(t)});const J={WeakSet:WeakSet,WeakMap:WeakMap,WeakValue:class{has(){return!1}set(){}}},{apply:Q}=Reflect;const{Map:Y,WeakMap:K,WeakSet:Z,setTimeout:ee}=G;let te=!0,ne=e=>{e.clear(),te=!te};var oe=function(e){const{WeakSet:t,WeakMap:n,WeakValue:o}=this||J,r=new t,i=new n,s=new o;return function(t){if(r.has(t))return t;if(i.has(t))return i.get(t);if(s.has(t))return s.get(t);const n=Q(e,this,arguments);return r.add(n),n!==t&&("object"==typeof t&&t?i:s).set(t,n),n}}.bind({WeakMap:K,WeakSet:Z,WeakValue:class extends Y{set(e,t){return te&&(te=!te,ee(ne,0,this)),super.set(e,t)}}});const{concat:re,includes:ie,join:se,reduce:ae,unshift:ce}=c([]),le=v(globalThis),{Map:ue,WeakMap:de}=le,he=new ue,fe=t=>{const n=(e=>{const t=[];let n=e;for(;n;){if(he.has(n))ce(t,he.get(n));else{const e=w(n);he.set(n,e),ce(t,e)}n=m(n)}return ce(t,{}),r(d,null,t)})("function"==typeof t?t.prototype:t),o={get(e,t){if(t in n){const{value:o,get:r}=n[t];if(r)return s(r,e);if("function"==typeof o)return i(o,e)}return e[t]},set(e,t,o){if(t in n){const{set:r}=n[t];if(r)return s(r,e,o),!0}return e[t]=o,!0}};return t=>new e(t,o)},{isExtensionContext:pe,Array:we,Number:me,String:ge,Object:be}=G,{isArray:ye}=we,{getOwnPropertyDescriptor:ve,setPrototypeOf:Se}=be,{toString:ke}=be.prototype,{slice:Ee}=ge.prototype,{get:xe}=ve(Node.prototype,"nodeType"),Me=pe?{}:{Attr:fe(Attr),CanvasRenderingContext2D:fe(CanvasRenderingContext2D),CSSStyleDeclaration:fe(CSSStyleDeclaration),Document:fe(Document),Element:fe(Element),HTMLCanvasElement:fe(HTMLCanvasElement),HTMLElement:fe(HTMLElement),HTMLImageElement:fe(HTMLImageElement),HTMLScriptElement:fe(HTMLScriptElement),MutationRecord:fe(MutationRecord),Node:fe(Node),ShadowRoot:fe(ShadowRoot),get CSS2Properties(){return Me.CSSStyleDeclaration}},Ce=(e,t)=>{if("Element"!==t&&t in Me)return Me[t](e);if(ye(e))return Se(e,we.prototype);const n=(e=>s(Ee,s(ke,e),8,-1))(e);if(n in Me)return Me[n](e)
+const snippets=fn;
+let context;
+for (const [name, ...args] of filters) {
+if (snippets.hasOwnProperty(name)) {
+try { context = snippets[name].apply(context, args); }
+catch (error) { console.error(error); }
+}
+}
+context = void 0;
+})(e, ...t);
+
+const callback = (environment, ...filters) => {
+const e=Proxy,{apply:t,bind:r,call:n}=Function,o=n.bind(t),i=n.bind(r),s=n.bind(n),a={get:(e,t)=>i(n,e[t])},l=t=>new e(t,a),c=(t,r)=>new e(t,{apply:(e,t,n)=>o(r,t,n)}),u={get:(e,t)=>i(e[t],e)},f=t=>new e(t,u),{assign:p,defineProperties:d,freeze:h,getOwnPropertyDescriptor:w,getOwnPropertyDescriptors:g,getPrototypeOf:y}=f(Object),{hasOwnProperty:m}=l({}),{species:b}=Symbol,v={get(e,t){const r=e[t];class n extends r{}const o=g(r.prototype);delete o.constructor,h(d(n.prototype,o));const i=g(r);return delete i.length,delete i.prototype,i[b]={value:n},h(d(n,i))}},E=t=>new e(t,v),S="undefined"!=typeof environment?environment:{};"undefined"==typeof globalThis&&(window.globalThis=window);const{apply:M,ownKeys:T}=f(Reflect),x="world"in S,O=x&&"ISOLATED"===S.world,P=x&&"MAIN"===S.world,j="object"==typeof chrome&&!!chrome.runtime,N="object"==typeof browser&&!!browser.runtime,L=!P&&(O||j||N),k=e=>L?e:C(e,H(e)),{create:C,defineProperties:A,defineProperty:W,freeze:$,getOwnPropertyDescriptor:D,getOwnPropertyDescriptors:H}=f(Object),z=f(globalThis),R=L?globalThis:E(globalThis),{Map:F,RegExp:I,Set:J,WeakMap:V,WeakSet:B}=R,U=(e,t,r=null)=>{const n=T(t);for(const o of T(e)){if(n.includes(o))continue;const i=D(e,o);if(r&&"value"in i){const{value:e}=i;"function"==typeof e&&(i.value=r(e))}W(t,o,i)}},_=e=>{const t=R[e];class r extends t{}const{toString:n,valueOf:o}=t.prototype;A(r.prototype,{toString:{value:n},valueOf:{value:o}});const i=e.toLowerCase(),s=e=>function(){const t=M(e,this,arguments);return typeof t===i?new r(t):t};return U(t,r,s),U(t.prototype,r.prototype,s),r},X=$({frozen:new V,hidden:new B,iframePropertiesToAbort:{read:new J,write:new J},abortedIframes:new V}),q=new I("^[A-Z]");var G=new Proxy(new F([["chrome",L&&(j&&chrome||N&&browser)||void 0],["isExtensionContext",L],["variables",X],["console",k(console)],["document",globalThis.document],["performance",k(performance)],["JSON",k(JSON)],["Map",F],["Math",k(Math)],["Number",L?Number:_("Number")],["RegExp",I],["Set",J],["String",L?String:_("String")],["WeakMap",V],["WeakSet",B],["MouseEvent",MouseEvent]]),{get(e,t){if(e.has(t))return e.get(t);let r=globalThis[t];return"function"==typeof r&&(r=(q.test(t)?R:z)[t]),e.set(t,r),r},has:(e,t)=>e.has(t)});const K={WeakSet:WeakSet,WeakMap:WeakMap,WeakValue:class{has(){return!1}set(){}}},{apply:Y}=Reflect;const{Map:Z,WeakMap:Q,WeakSet:ee,setTimeout:te}=G;let re=!0,ne=e=>{e.clear(),re=!re};var oe=function(e){const{WeakSet:t,WeakMap:r,WeakValue:n}=this||K,o=new t,i=new r,s=new n;return function(t){if(o.has(t))return t;if(i.has(t))return i.get(t);if(s.has(t))return s.get(t);const r=Y(e,this,arguments);return o.add(r),r!==t&&("object"==typeof t&&t?i:s).set(t,r),r}}.bind({WeakMap:Q,WeakSet:ee,WeakValue:class extends Z{set(e,t){return re&&(re=!re,te(ne,0,this)),super.set(e,t)}}});const{concat:ie,includes:se,join:ae,reduce:le,unshift:ce}=l([]),ue=E(globalThis),{Map:fe,WeakMap:pe}=ue,de=new fe,he=t=>{const r=(e=>{const t=[];let r=e;for(;r;){if(de.has(r))ce(t,de.get(r));else{const e=g(r);de.set(r,e),ce(t,e)}r=y(r)}return ce(t,{}),o(p,null,t)})("function"==typeof t?t.prototype:t),n={get(e,t){if(t in r){const{value:n,get:o}=r[t];if(o)return s(o,e);if("function"==typeof n)return i(n,e)}return e[t]},set(e,t,n){if(t in r){const{set:o}=r[t];if(o)return s(o,e,n),!0}return e[t]=n,!0}};return t=>new e(t,n)},{isExtensionContext:we,Array:ge,Number:ye,String:me,Object:be}=G,{isArray:ve}=ge,{getOwnPropertyDescriptor:Ee,setPrototypeOf:Se}=be,{toString:Me}=be.prototype,{slice:Te}=me.prototype,{get:xe}=Ee(Node.prototype,"nodeType"),Oe=we?{}:{Attr:he(Attr),CanvasRenderingContext2D:he(CanvasRenderingContext2D),CSSStyleDeclaration:he(CSSStyleDeclaration),Document:he(Document),Element:he(Element),HTMLCanvasElement:he(HTMLCanvasElement),HTMLElement:he(HTMLElement),HTMLImageElement:he(HTMLImageElement),HTMLScriptElement:he(HTMLScriptElement),MutationRecord:he(MutationRecord),Node:he(Node),ShadowRoot:he(ShadowRoot),get CSS2Properties(){return Oe.CSSStyleDeclaration}},Pe=(e,t)=>{if("Element"!==t&&t in Oe)return Oe[t](e);if(ve(e))return Se(e,ge.prototype);const r=(e=>s(T
+const snippets=Pr;
+let context;
+for (const [name, ...args] of filters) {
+if (snippets.hasOwnProperty(name)) {
+try { context = snippets[name].apply(context, args); }
+catch (error) { console.error(error); }
+}
+}
+context = void 0;
+};
+const graph = new Map([["abort-current-inline-script",null],["abort-on-iframe-property-read",null],["abort-on-iframe-property-write",null],["abort-on-property-read",null],["abort-on-property-write",null],["cookie-remover",null],["debug",null],["freeze-element",null],["hide-if-shadow-contains",null],["json-override",null],["json-prune",null],["override-property-read",null],["prevent-listener",null],["strip-fetch-query-parameter",null],["trace",null]]);
+callback.get = snippet => graph.get(snippet);
+callback.has = snippet => graph.has(snippet);
+
+ if (t.every(([name]) => !callback.has(name))) return;
+ const append = () => {
+ URL.revokeObjectURL(
+ Object.assign(
+ document.documentElement.appendChild(document.createElement("script")),
+ {async: false, src: URL.createObjectURL(new Blob([
+ "(" + callback + ")(..." + JSON.stringify([e, ...t]) + ")"
+ ]))}
+ ).src
+ );
+ };
+ try { append(); }
+ catch (_) {
+ document.addEventListener("readystatechange", append, {once:true});
+ }
+}
\ No newline at end of file
diff --git a/components/resources/adblocking/snippets/dist/isolated-first-xpath3.source.jst b/components/resources/adblocking/snippets/dist/isolated-first-xpath3.source.jst
new file mode 100644
--- /dev/null
+++ b/components/resources/adblocking/snippets/dist/isolated-first-xpath3.source.jst
@@ -0,0 +1,3696 @@
+(e, ...t) => {
+/*!
+ * snippets v0.8.1 (https://gitlab.com/eyeo/anti-cv/snippets/-/tree/5f54b58f039a604088d65991f2829a9a081832f6/dist)
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * @eyeo/snippets 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/snippets 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/snippets. If not, see <http://www.gnu.org/licenses/>.
+ */
+ ((environment, ...filters) => {
+ /*!
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * @eyeo/snippets 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/snippets 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/snippets. If not, see <http://www.gnu.org/licenses/>.
+ */
+ const $$1 = Proxy;
+
+ const {apply: a, bind: b, call: c} = Function;
+ const apply$2 = c.bind(a);
+ const bind = c.bind(b);
+ const call = c.bind(c);
+
+ const callerHandler = {
+ get(target, name) {
+ return bind(c, target[name]);
+ }
+ };
+
+ const caller = target => new $$1(target, callerHandler);
+
+ const handler$2 = {
+ get(target, name) {
+ return bind(target[name], target);
+ }
+ };
+
+ const bound = target => new $$1(target, handler$2);
+
+ const {
+ assign: assign$1,
+ defineProperties: defineProperties$1,
+ freeze: freeze$1,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$2,
+ getOwnPropertyDescriptors: getOwnPropertyDescriptors$1,
+ getPrototypeOf
+ } = bound(Object);
+
+ const {hasOwnProperty} = caller({});
+
+ const {species} = Symbol;
+
+ const handler$1 = {
+ get(target, name) {
+ const Native = target[name];
+ class Secure extends Native {}
+
+ const proto = getOwnPropertyDescriptors$1(Native.prototype);
+ delete proto.constructor;
+ freeze$1(defineProperties$1(Secure.prototype, proto));
+
+ const statics = getOwnPropertyDescriptors$1(Native);
+ delete statics.length;
+ delete statics.prototype;
+ statics[species] = {value: Secure};
+ return freeze$1(defineProperties$1(Secure, statics));
+ }
+ };
+
+ const secure = target => new $$1(target, handler$1);
+
+ const libEnvironment = typeof environment !== "undefined" ? environment :
+ {};
+
+ if (typeof globalThis === "undefined")
+ window.globalThis = window;
+
+ const {apply: apply$1, ownKeys} = bound(Reflect);
+
+ const worldEnvDefined = "world" in libEnvironment;
+ const isIsolatedWorld = worldEnvDefined && libEnvironment.world === "ISOLATED";
+ const isMainWorld = worldEnvDefined && libEnvironment.world === "MAIN";
+ const isChrome = typeof chrome === "object" && !!chrome.runtime;
+ const isOtherThanChrome = typeof browser === "object" && !!browser.runtime;
+ const isExtensionContext$2 = !isMainWorld &&
+ (isIsolatedWorld || isChrome || isOtherThanChrome);
+ const copyIfExtension = value => isExtensionContext$2 ?
+ value :
+ create(value, getOwnPropertyDescriptors(value));
+
+ const {
+ create,
+ defineProperties,
+ defineProperty,
+ freeze,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$1,
+ getOwnPropertyDescriptors
+ } = bound(Object);
+
+ const invokes = bound(globalThis);
+ const classes = isExtensionContext$2 ? globalThis : secure(globalThis);
+ const {Map: Map$5, RegExp: RegExp$1, Set, WeakMap: WeakMap$3, WeakSet: WeakSet$b} = classes;
+
+ const augment = (source, target, method = null) => {
+ const known = ownKeys(target);
+ for (const key of ownKeys(source)) {
+ if (known.includes(key))
+ continue;
+
+ const descriptor = getOwnPropertyDescriptor$1(source, key);
+ if (method && "value" in descriptor) {
+ const {value} = descriptor;
+ if (typeof value === "function")
+ descriptor.value = method(value);
+ }
+ defineProperty(target, key, descriptor);
+ }
+ };
+
+ const primitive = name => {
+ const Super = classes[name];
+ class Class extends Super {}
+ const {toString, valueOf} = Super.prototype;
+ defineProperties(Class.prototype, {
+ toString: {value: toString},
+ valueOf: {value: valueOf}
+ });
+ const type = name.toLowerCase();
+ const method = callback => function() {
+ const result = apply$1(callback, this, arguments);
+ return typeof result === type ? new Class(result) : result;
+ };
+ augment(Super, Class, method);
+ augment(Super.prototype, Class.prototype, method);
+ return Class;
+ };
+
+ const variables$1 = freeze({
+ frozen: new WeakMap$3(),
+ hidden: new WeakSet$b(),
+ iframePropertiesToAbort: {
+ read: new Set(),
+ write: new Set()
+ },
+ abortedIframes: new WeakMap$3()
+ });
+
+ const startsCapitalized = new RegExp$1("^[A-Z]");
+
+ var env = new Proxy(new Map$5([
+
+ ["chrome", (
+ isExtensionContext$2 && (
+ (isChrome && chrome) ||
+ (isOtherThanChrome && browser)
+ )
+ ) || void 0],
+ ["isExtensionContext", isExtensionContext$2],
+ ["variables", variables$1],
+
+ ["console", copyIfExtension(console)],
+ ["document", globalThis.document],
+ ["performance", copyIfExtension(performance)],
+ ["JSON", copyIfExtension(JSON)],
+ ["Map", Map$5],
+ ["Math", copyIfExtension(Math)],
+ ["Number", isExtensionContext$2 ? Number : primitive("Number")],
+ ["RegExp", RegExp$1],
+ ["Set", Set],
+ ["String", isExtensionContext$2 ? String : primitive("String")],
+ ["WeakMap", WeakMap$3],
+ ["WeakSet", WeakSet$b],
+
+ ["MouseEvent", MouseEvent]
+ ]), {
+ get(map, key) {
+ if (map.has(key))
+ return map.get(key);
+
+ let value = globalThis[key];
+ if (typeof value === "function")
+ value = (startsCapitalized.test(key) ? classes : invokes)[key];
+
+ map.set(key, value);
+ return value;
+ },
+ has(map, key) {
+ return map.has(key);
+ }
+ });
+
+ class WeakValue {
+ has() { return false; }
+ set() {}
+ }
+
+ const helpers = {WeakSet, WeakMap, WeakValue};
+ const {apply} = Reflect;
+
+ function transformOnce (callback) { const {WeakSet, WeakMap, WeakValue} = (this || helpers);
+ const ws = new WeakSet;
+ const wm = new WeakMap;
+ const wv = new WeakValue;
+ return function (any) {
+ if (ws.has(any))
+ return any;
+
+ if (wm.has(any))
+ return wm.get(any);
+
+ if (wv.has(any))
+ return wv.get(any);
+
+ const value = apply(callback, this, arguments);
+ ws.add(value);
+ if (value !== any)
+ (typeof any === 'object' && any ? wm : wv).set(any, value);
+ return value;
+ };
+ }
+
+ const {Map: Map$4, WeakMap: WeakMap$2, WeakSet: WeakSet$a, setTimeout: setTimeout$3} = env;
+
+ let cleanup = true;
+ let cleanUpCallback = map => {
+ map.clear();
+ cleanup = !cleanup;
+ };
+
+ var transformer = transformOnce.bind({
+ WeakMap: WeakMap$2,
+ WeakSet: WeakSet$a,
+
+ WeakValue: class extends Map$4 {
+ set(key, value) {
+ if (cleanup) {
+ cleanup = !cleanup;
+ setTimeout$3(cleanUpCallback, 0, this);
+ }
+ return super.set(key, value);
+ }
+ }
+ });
+
+ const {concat, includes, join, reduce, unshift} = caller([]);
+
+ const globals = secure(globalThis);
+
+ const {
+ Map: Map$3,
+ WeakMap: WeakMap$1
+ } = globals;
+
+ const map = new Map$3;
+ const descriptors = target => {
+ const chain = [];
+ let current = target;
+ while (current) {
+ if (map.has(current))
+ unshift(chain, map.get(current));
+ else {
+ const descriptors = getOwnPropertyDescriptors$1(current);
+ map.set(current, descriptors);
+ unshift(chain, descriptors);
+ }
+ current = getPrototypeOf(current);
+ }
+ unshift(chain, {});
+ return apply$2(assign$1, null, chain);
+ };
+
+ const chain = source => {
+ const target = typeof source === 'function' ? source.prototype : source;
+ const chained = descriptors(target);
+ const handler = {
+ get(target, key) {
+ if (key in chained) {
+ const {value, get} = chained[key];
+ if (get)
+ return call(get, target);
+ if (typeof value === 'function')
+ return bind(value, target);
+ }
+ return target[key];
+ },
+ set(target, key, value) {
+ if (key in chained) {
+ const {set} = chained[key];
+ if (set) {
+ call(set, target, value);
+ return true;
+ }
+ }
+ target[key] = value;
+ return true;
+ }
+ };
+ return target => new $$1(target, handler);
+ };
+
+ const {
+ isExtensionContext: isExtensionContext$1,
+ Array: Array$2,
+ Number: Number$1,
+ String: String$1,
+ Object: Object$2
+ } = env;
+
+ const {isArray} = Array$2;
+ const {getOwnPropertyDescriptor, setPrototypeOf: setPrototypeOf$1} = Object$2;
+
+ const {toString} = Object$2.prototype;
+ const {slice} = String$1.prototype;
+ const getBrand = value => call(slice, call(toString, value), 8, -1);
+
+ const {get: nodeType} = getOwnPropertyDescriptor(Node.prototype, "nodeType");
+
+ const chained = isExtensionContext$1 ? {} : {
+ Attr: chain(Attr),
+ CanvasRenderingContext2D: chain(CanvasRenderingContext2D),
+ CSSStyleDeclaration: chain(CSSStyleDeclaration),
+ Document: chain(Document),
+ Element: chain(Element),
+ HTMLCanvasElement: chain(HTMLCanvasElement),
+ HTMLElement: chain(HTMLElement),
+ HTMLImageElement: chain(HTMLImageElement),
+ HTMLScriptElement: chain(HTMLScriptElement),
+ MutationRecord: chain(MutationRecord),
+ Node: chain(Node),
+ ShadowRoot: chain(ShadowRoot),
+
+ get CSS2Properties() {
+ return chained.CSSStyleDeclaration;
+ }
+ };
+
+ const upgrade = (value, hint) => {
+ if (hint !== "Element" && hint in chained)
+ return chained[hint](value);
+
+ if (isArray(value))
+ return setPrototypeOf$1(value, Array$2.prototype);
+
+ const brand = getBrand(value);
+ if (brand in chained)
+ return chained[brand](value);
+
+ if (brand in env)
+ return setPrototypeOf$1(value, env[brand].prototype);
+
+ if ("nodeType" in value) {
+ switch (call(nodeType, value)) {
+ case 1:
+ if (!(hint in chained))
+ throw new Error("unknown hint " + hint);
+ return chained[hint](value);
+ case 2:
+ return chained.Attr(value);
+ case 3:
+ return chained.Node(value);
+ case 9:
+ return chained.Document(value);
+ }
+ }
+
+ throw new Error("unknown brand " + brand);
+ };
+
+ var $ = isExtensionContext$1 ?
+ value => (value === window || value === globalThis ? env : value) :
+ transformer((value, hint = "Element") => {
+ if (value === window || value === globalThis)
+ return env;
+
+ switch (typeof value) {
+ case "object":
+ return value && upgrade(value, hint);
+
+ case "string":
+ return new String$1(value);
+
+ case "number":
+ return new Number$1(value);
+
+ default:
+ throw new Error("unsupported value");
+ }
+ });
+
+ let {
+ document: document$1,
+ getComputedStyle: getComputedStyle$5,
+ isExtensionContext,
+ variables,
+ Array: Array$1,
+ MutationObserver: MutationObserver$a,
+ Object: Object$1,
+ XPathEvaluator,
+ XPathExpression,
+ XPathResult
+ } = $(window);
+
+ let {querySelectorAll} = document$1;
+ let $$ = querySelectorAll && bind(querySelectorAll, document$1);
+
+ const {assign, setPrototypeOf} = Object$1;
+
+ class $XPathExpression extends XPathExpression {
+ evaluate(...args) {
+ return setPrototypeOf(
+ apply$2(super.evaluate, this, args),
+ XPathResult.prototype
+ );
+ }
+ }
+
+ class $XPathEvaluator extends XPathEvaluator {
+ createExpression(...args) {
+ return setPrototypeOf(
+ apply$2(super.createExpression, this, args),
+ $XPathExpression.prototype
+ );
+ }
+ }
+
+ function hideElement(element) {
+ if (variables.hidden.has(element))
+ return;
+
+ notifyElementHidden(element);
+
+ variables.hidden.add(element);
+
+ let {style} = $(element);
+ let $style = $(style, "CSSStyleDeclaration");
+ let properties = $([]);
+ let {debugCSSProperties} = libEnvironment;
+
+ for (let [key, value] of (debugCSSProperties || [["display", "none"]])) {
+ $style.setProperty(key, value, "important");
+ properties.push([key, $style.getPropertyValue(key)]);
+ }
+
+ new MutationObserver$a(() => {
+ for (let [key, value] of properties) {
+ let propertyValue = $style.getPropertyValue(key);
+ let propertyPriority = $style.getPropertyPriority(key);
+ if (propertyValue != value || propertyPriority != "important")
+ $style.setProperty(key, value, "important");
+ }
+ }).observe(element, {attributes: true,
+ attributeFilter: ["style"]});
+ }
+
+ function notifyElementHidden(element) {
+ if (isExtensionContext && typeof checkElement === "function")
+ checkElement(element);
+ }
+
+ function initQueryAndApply(selector) {
+ let $selector = selector;
+ if ($selector.startsWith("xpath(") &&
+ $selector.endsWith(")")) {
+ let xpathQuery = $selector.slice(6, -1);
+ let evaluator = new $XPathEvaluator();
+ let expression = evaluator.createExpression(xpathQuery, null);
+
+ let flag = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
+
+ return cb => {
+ if (!cb)
+ return;
+ let result = expression.evaluate(document$1, flag, null);
+ let {snapshotLength} = result;
+ for (let i = 0; i < snapshotLength; i++)
+ cb(result.snapshotItem(i));
+ };
+ }
+ return cb => $$(selector).forEach(cb);
+ }
+
+ function initQueryAll(selector) {
+ let $selector = selector;
+ if ($selector.startsWith("xpath(") &&
+ $selector.endsWith(")")) {
+ let queryAndApply = initQueryAndApply(selector);
+ return () => {
+ let elements = $([]);
+ queryAndApply(e => elements.push(e));
+ return elements;
+ };
+ }
+ return () => Array$1.from($$(selector));
+ }
+
+ function hideIfMatches(match, selector, searchSelector, onHideCallback) {
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ let won;
+ const callback = () => {
+ for (const element of $$(searchSelector)) {
+ const closest = $(element).closest(selector);
+ if (closest && match(element, closest)) {
+ won();
+ hideElement(closest);
+ if (typeof onHideCallback === "function")
+ onHideCallback(closest);
+ }
+ }
+ };
+ return assign(
+ new MutationObserver$a(callback),
+ {
+ race(win) {
+ won = win;
+ this.observe(document$1, {childList: true,
+ characterData: true,
+ subtree: true});
+ callback();
+ }
+ }
+ );
+ }
+
+ function isVisible(element, style, closest) {
+ let $style = $(style, "CSSStyleDeclaration");
+ if ($style.getPropertyValue("display") == "none")
+ return false;
+
+ let visibility = $style.getPropertyValue("visibility");
+ if (visibility == "hidden" || visibility == "collapse")
+ return false;
+
+ if (!closest || element == closest)
+ return true;
+
+ let parent = $(element).parentElement;
+ if (!parent)
+ return true;
+
+ return isVisible(parent, getComputedStyle$5(parent), closest);
+ }
+
+ function getComputedCSSText(element) {
+ let style = getComputedStyle$5(element);
+ let {cssText} = style;
+
+ if (cssText)
+ return cssText;
+
+ for (let property of style)
+ cssText += `${property}: ${style[property]}; `;
+
+ return $(cssText).trim();
+ }
+
+ let {Math: Math$2, RegExp} = $(window);
+
+ function regexEscape(string) {
+ return $(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
+ }
+
+ function toRegExp(pattern) {
+ let {length} = pattern;
+
+ if (length > 1 && pattern[0] === "/") {
+ let isCaseSensitive = pattern[length - 1] === "/";
+
+ if (isCaseSensitive || (length > 2 && $(pattern).endsWith("/i"))) {
+ let args = [$(pattern).slice(1, isCaseSensitive ? -1 : -2)];
+ if (!isCaseSensitive)
+ args.push("i");
+
+ return new RegExp(...args);
+ }
+ }
+
+ return new RegExp(regexEscape(pattern));
+ }
+
+ let debugging = false;
+
+ function debug() {
+ return debugging;
+ }
+
+ function setDebug() {
+ debugging = true;
+ }
+
+ const {console: console$1} = $(window);
+
+ const noop = () => {};
+
+ function log(...args) {
+ if (debug())
+ $(args).unshift("%c DEBUG", "font-weight: bold");
+
+ console$1.log(...args);
+ }
+
+ function getDebugger(name) {
+ return bind(debug() ? log : noop, null, name);
+ }
+
+ let {Array, Error: Error$3, Map: Map$2, parseInt: parseInt$2} = $(window);
+
+ let stack = null;
+ let won = null;
+
+ function race(action, winners = "1") {
+ switch (action) {
+ case "start":
+ stack = {
+ winners: parseInt$2(winners, 10) || 1,
+ participants: new Map$2()
+ };
+ won = new Array();
+ break;
+ case "end":
+ case "finish":
+ case "stop":
+ stack = null;
+ for (let win of won)
+ win();
+ won = null;
+ break;
+ default:
+ throw new Error$3(`Invalid action: ${action}`);
+ }
+ }
+
+ function raceWinner(name, lose) {
+
+ if (stack === null)
+ return noop;
+
+ let current = stack;
+ let {participants} = current;
+ participants.set(win, lose);
+
+ return win;
+
+ function win() {
+
+ if (current.winners < 1)
+ return;
+
+ let debugLog = getDebugger("race");
+ debugLog(`${name} won the race`);
+
+ if (current === stack) {
+ won.push(win);
+ }
+ else {
+ participants.delete(win);
+ if (--current.winners < 1) {
+ for (let looser of participants.values())
+ looser();
+
+ participants.clear();
+ }
+ }
+ }
+ }
+
+ function hideIfContains(search, selector = "*", searchSelector = null) {
+ const debugLog = getDebugger("hide-if-contains");
+ const onHideCallback = node => {
+ debugLog("Matched: ", node, " for selector: ", selector, searchSelector);
+ };
+ let re = toRegExp(search);
+
+ const mo = hideIfMatches(element => re.test($(element).textContent),
+ selector,
+ searchSelector,
+ onHideCallback);
+ mo.race(raceWinner(
+ "hide-if-contains",
+ () => {
+ mo.disconnect();
+ }
+ ));
+ }
+
+ const handler = {
+ get(target, name) {
+ const context = target;
+ while (!hasOwnProperty(target, name))
+ target = getPrototypeOf(target);
+ const {get, set} = getOwnPropertyDescriptor$2(target, name);
+ return function () {
+ return arguments.length ?
+ apply$2(set, context, arguments) :
+ call(get, context);
+ };
+ }
+ };
+
+ const accessor = target => new $$1(target, handler);
+
+ $(window);
+
+ accessor(window);
+
+ $(/^\d+$/);
+
+ function getPromiseFromEvent(item, event) {
+ return new Promise(
+ resolve => {
+ const listener = () => {
+ item.removeEventListener(event, listener);
+ resolve();
+ };
+ item.addEventListener(event, listener);
+ }
+ );
+ }
+
+ function waitUntilEvent(
+ debugLog,
+ mainLogic,
+ waitUntil) {
+ if (waitUntil) {
+
+ if (waitUntil === "load") {
+ debugLog("Waiting until window.load");
+
+ window.onload = () => {
+ debugLog("Window.load fired.");
+ mainLogic();
+ };
+ }
+
+ else if (waitUntil === "loading" ||
+ waitUntil === "interactive" ||
+ waitUntil === "complete") {
+ debugLog("Waiting document state until :", waitUntil);
+
+ document.onreadystatechange = () => {
+ debugLog("Document state changed:", document.readyState);
+ if (document.readyState === waitUntil)
+ mainLogic();
+ };
+ }
+
+ else {
+ debugLog("Waiting until ", waitUntil, " event is triggered on document");
+ getPromiseFromEvent(document, waitUntil).then(() => {
+ debugLog(waitUntil, " is triggered on document, starting the snippet");
+ mainLogic();
+ }).catch(err => {
+ debugLog("There was an error while waiting for the event.", err);
+ });
+ }
+ }
+ else {
+
+ mainLogic();
+ }
+ }
+
+ let {MutationObserver: MutationObserver$9, WeakSet: WeakSet$9, getComputedStyle: getComputedStyle$4} = $(window);
+
+ function hideIfContainsAndMatchesStyle(search,
+ selector = "*",
+ searchSelector = null,
+ style = null,
+ searchStyle = null,
+ waitUntil,
+ windowWidthMin = null,
+ windowWidthMax = null
+ ) {
+ const debugLog = getDebugger("hide-if-contains-and-matches-style");
+ const hiddenMap = new WeakSet$9();
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ const searchRegExp = toRegExp(search);
+
+ const styleRegExp = style ? toRegExp(style) : null;
+ const searchStyleRegExp = searchStyle ? toRegExp(searchStyle) : null;
+ const mainLogic = () => {
+ const callback = () => {
+ if ((windowWidthMin && window.innerWidth < windowWidthMin) ||
+ (windowWidthMax && window.innerWidth > windowWidthMax)
+ )
+ return;
+ for (const element of $$(searchSelector)) {
+ if (hiddenMap.has(element))
+ continue;
+ if (searchRegExp.test($(element).textContent)) {
+ if (!searchStyleRegExp ||
+ searchStyleRegExp.test(getComputedCSSText(element))) {
+ const closest = $(element).closest(selector);
+ if (!closest)
+ continue;
+ if (!styleRegExp || styleRegExp.test(getComputedCSSText(closest))) {
+ win();
+ hideElement(closest);
+ hiddenMap.add(element);
+ debugLog("Matched: ",
+ closest,
+ "which contains: ",
+ element,
+ " for params: ",
+ ...arguments);
+ }
+ else {
+ debugLog(
+ "In this element the searchStyle matched but style didn't:\n",
+ closest,
+ getComputedStyle$4(closest),
+ ...arguments);
+ }
+ }
+ else {
+ debugLog(
+ "In this element the searchStyle didn't match:\n",
+ element,
+ getComputedStyle$4(element),
+ ...arguments);
+ }
+ }
+ }
+ };
+
+ const mo = new MutationObserver$9(callback);
+ const win = raceWinner(
+ "hide-if-contains-and-matches-style",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {childList: true, characterData: true, subtree: true});
+ callback();
+ };
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
+ }
+
+ let {
+ clearTimeout,
+ fetch,
+ getComputedStyle: getComputedStyle$3,
+ setTimeout: setTimeout$2,
+ Map: Map$1,
+ MutationObserver: MutationObserver$8,
+ Uint8Array
+ } = $(window);
+
+ function hideIfContainsImage(search, selector, searchSelector) {
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ let searchRegExp = toRegExp(search);
+
+ const debugLog = getDebugger("hide-if-contains-image");
+
+ let callback = () => {
+ for (let element of $$(searchSelector)) {
+ let style = getComputedStyle$3(element);
+ let match = $(style["background-image"]).match(/^url\("(.*)"\)$/);
+ if (match) {
+ fetchContent(match[1]).then(content => {
+ if (searchRegExp.test(uint8ArrayToHex(new Uint8Array(content)))) {
+ let closest = $(element).closest(selector);
+ if (closest) {
+ win();
+ hideElement(closest);
+ debugLog("Matched: ", closest, " for:", ...arguments);
+ }
+ }
+ });
+ }
+ }
+ };
+
+ let mo = new MutationObserver$8(callback);
+ let win = raceWinner(
+ "hide-if-contains-image",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {childList: true, subtree: true});
+ callback();
+ }
+
+ let fetchContentMap = new Map$1();
+
+ function fetchContent(url, {as = "arrayBuffer", cleanup = 60000} = {}) {
+
+ let uid = as + ":" + url;
+ let details = fetchContentMap.get(uid) || {
+ remove: () => fetchContentMap.delete(uid),
+ result: null,
+ timer: 0
+ };
+ clearTimeout(details.timer);
+ details.timer = setTimeout$2(details.remove, cleanup);
+ if (!details.result) {
+ details.result = fetch(url).then(res => res[as]()).catch(details.remove);
+ fetchContentMap.set(uid, details);
+ }
+ return details.result;
+ }
+
+ function toHex(number, length = 2) {
+ let hex = $(number).toString(16);
+
+ if (hex.length < length)
+ hex = $("0").repeat(length - hex.length) + hex;
+
+ return hex;
+ }
+
+ function uint8ArrayToHex(uint8Array) {
+ return uint8Array.reduce((hex, byte) => hex + toHex(byte), "");
+ }
+
+ const {parseFloat: parseFloat$1, Math: Math$1, MutationObserver: MutationObserver$7, WeakSet: WeakSet$8} = $(window);
+ const {min} = Math$1;
+
+ const ld = (a, b) => {
+ const len1 = a.length + 1;
+ const len2 = b.length + 1;
+ const d = [[0]];
+ let i = 0;
+ let I = 0;
+
+ while (++i < len2)
+ d[0][i] = i;
+
+ i = 0;
+ while (++i < len1) {
+ const c = a[I];
+ let j = 0;
+ let J = 0;
+ d[i] = [i];
+ while (++j < len2) {
+ d[i][j] = min(d[I][j] + 1, d[i][J] + 1, d[I][J] + (c != b[J]));
+ ++J;
+ }
+ ++I;
+ }
+ return d[len1 - 1][len2 - 1];
+ };
+
+ function hideIfContainsSimilarText(
+ search, selector,
+ searchSelector = null,
+ ignoreChars = 0,
+ maxSearches = 0
+ ) {
+ const visitedNodes = new WeakSet$8();
+ const debugLog = getDebugger("hide-if-contains-similar-text");
+ const $search = $(search);
+ const {length} = $search;
+ const chars = length + parseFloat$1(ignoreChars) || 0;
+ const find = $([...$search]).sort();
+ const guard = parseFloat$1(maxSearches) || Infinity;
+
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ debugLog("Looking for similar text: " + $search);
+
+ const callback = () => {
+ for (const element of $$(searchSelector)) {
+ if (visitedNodes.has(element))
+ continue;
+
+ visitedNodes.add(element);
+ const {innerText} = $(element);
+ const loop = min(guard, innerText.length - chars + 1);
+ for (let i = 0; i < loop; i++) {
+ const str = $(innerText).substr(i, chars);
+ const distance = ld(find, $([...str]).sort()) - ignoreChars;
+ if (distance <= 0) {
+ const closest = $(element).closest(selector);
+ debugLog("Found similar text: " + $search, closest);
+ if (closest) {
+ win();
+ hideElement(closest);
+ break;
+ }
+ }
+ }
+ }
+ };
+
+ let mo = new MutationObserver$7(callback);
+ let win = raceWinner(
+ "hide-if-contains-similar-text",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {childList: true, characterData: true, subtree: true});
+ callback();
+ }
+
+ let {getComputedStyle: getComputedStyle$2, Map, WeakSet: WeakSet$7, parseFloat} = $(window);
+
+ const {ELEMENT_NODE: ELEMENT_NODE$3, TEXT_NODE} = Node;
+
+ function hideIfContainsVisibleText(search, selector,
+ searchSelector = null,
+ ...attributes) {
+ let entries = $([]);
+ const optionalParameters = new Map([
+ ["-snippet-box-margin", "2"],
+ ["-disable-bg-color-check", "false"],
+ ["-check-is-contained", "false"]
+ ]);
+
+ for (let attr of attributes) {
+ attr = $(attr);
+ let markerIndex = attr.indexOf(":");
+ if (markerIndex < 0)
+ continue;
+
+ let key = attr.slice(0, markerIndex).trim().toString();
+ let value = attr.slice(markerIndex + 1).trim().toString();
+
+ if (key && value) {
+ if (optionalParameters.has(key))
+ optionalParameters.set(key, value);
+ else
+ entries.push([key, value]);
+ }
+ }
+
+ let defaultEntries = $([
+ ["opacity", "0"],
+ ["font-size", "0px"],
+
+ ["color", "rgba(0, 0, 0, 0)"]
+ ]);
+
+ let attributesMap = new Map(defaultEntries.concat(entries));
+
+ function isTextVisible(element, style, {bgColorCheck = true} = {}) {
+ if (!style)
+ style = getComputedStyle$2(element);
+
+ style = $(style);
+
+ for (const [key, value] of attributesMap) {
+ let valueAsRegex = toRegExp(value);
+ if (valueAsRegex.test(style.getPropertyValue(key)))
+ return false;
+ }
+
+ let color = style.getPropertyValue("color");
+ if (bgColorCheck && style.getPropertyValue("background-color") == color)
+ return false;
+
+ return true;
+ }
+
+ function getPseudoContent(element, pseudo, {bgColorCheck = true} = {}) {
+ let style = getComputedStyle$2(element, pseudo);
+ if (!isVisible(element, style) ||
+ !isTextVisible(element, style, {bgColorCheck}))
+ return "";
+
+ let {content} = $(style);
+ if (content && content !== "none") {
+ let strings = $([]);
+
+ content = $(content).trim().replace(
+ /(["'])(?:(?=(\\?))\2.)*?\1/g,
+ value => `\x01${strings.push($(value).slice(1, -1)) - 1}`
+ );
+
+ content = content.replace(
+ /\s*attr\(\s*([^\s,)]+)[^)]*?\)\s*/g,
+ (_, name) => $(element).getAttribute(name) || ""
+ );
+
+ return content.replace(
+ /\x01(\d+)/g,
+ (_, index) => strings[index]);
+ }
+ return "";
+ }
+
+ function isContained(childNode, parentNode, {boxMargin = 2} = {}) {
+ const child = $(childNode).getBoundingClientRect();
+ const parent = $(parentNode).getBoundingClientRect();
+ const stretchedParent = {
+ left: parent.left - boxMargin,
+ right: parent.right + boxMargin,
+ top: parent.top - boxMargin,
+ bottom: parent.bottom + boxMargin
+ };
+ return (
+ (stretchedParent.left <= child.left &&
+ child.left <= stretchedParent.right &&
+ stretchedParent.top <= child.top &&
+ child.top <= stretchedParent.bottom) &&
+ (stretchedParent.top <= child.bottom &&
+ child.bottom <= stretchedParent.bottom &&
+ stretchedParent.left <= child.right &&
+ child.right <= stretchedParent.right)
+ );
+ }
+
+ function getVisibleContent(element,
+ closest,
+ style,
+ parentOverflowNode,
+ originalElement,
+ {
+ boxMargin = 2,
+ bgColorCheck,
+ checkIsContained
+ } = {}) {
+ let checkClosest = !style;
+ if (checkClosest)
+ style = getComputedStyle$2(element);
+
+ if (!isVisible(element, style, checkClosest && closest))
+ return "";
+
+ if (!parentOverflowNode &&
+ (
+ $(style).getPropertyValue("overflow-x") === "hidden" ||
+ $(style).getPropertyValue("overflow-y") === "hidden"
+ )
+ )
+ parentOverflowNode = element;
+
+ let text = getPseudoContent(element, ":before", {bgColorCheck});
+ for (let node of $(element).childNodes) {
+ switch ($(node).nodeType) {
+ case ELEMENT_NODE$3:
+ text += getVisibleContent(node,
+ element,
+ getComputedStyle$2(node),
+ parentOverflowNode,
+ originalElement,
+ {
+ boxMargin,
+ bgColorCheck,
+ checkIsContained
+ }
+ );
+ break;
+ case TEXT_NODE:
+
+ if (parentOverflowNode) {
+ if (isContained(element, parentOverflowNode, {boxMargin}) &&
+ isTextVisible(element, style, {bgColorCheck}))
+ text += $(node).nodeValue;
+ }
+ else if (isTextVisible(element, style, {bgColorCheck})) {
+ if (checkIsContained &&
+ !isContained(element, originalElement, {boxMargin}))
+ continue;
+ text += $(node).nodeValue;
+ }
+ break;
+ }
+ }
+ return text + getPseudoContent(element, ":after", {bgColorCheck});
+ }
+ const boxMarginStr = optionalParameters.get("-snippet-box-margin");
+ const boxMargin = parseFloat(boxMarginStr) || 0;
+
+ const bgColorCheckStr = optionalParameters.get("-disable-bg-color-check");
+ const bgColorCheck = !(bgColorCheckStr === "true");
+
+ const checkIsContainedStr = optionalParameters.get("-check-is-contained");
+ const checkIsContained = (checkIsContainedStr === "true");
+
+ let re = toRegExp(search);
+ let seen = new WeakSet$7();
+
+ const mo = hideIfMatches(
+ (element, closest) => {
+ if (seen.has(element))
+ return false;
+
+ seen.add(element);
+ let text = getVisibleContent(element, closest, null, null, element, {
+ boxMargin,
+ bgColorCheck,
+ checkIsContained
+ }
+ );
+ let result = re.test(text);
+ if (debug() && text.length)
+ log(result, re, text);
+ return result;
+ },
+ selector,
+ searchSelector
+ );
+ mo.race(raceWinner(
+ "hide-if-contains-visible-text",
+ () => {
+ mo.disconnect();
+ }
+ ));
+ }
+
+ let {MutationObserver: MutationObserver$6, WeakSet: WeakSet$6, getComputedStyle: getComputedStyle$1} = $(window);
+
+ function hideIfHasAndMatchesStyle(search,
+ selector = "*",
+ searchSelector = null,
+ style = null,
+ searchStyle = null,
+ waitUntil = null,
+ windowWidthMin = null,
+ windowWidthMax = null
+ ) {
+ const debugLog = getDebugger("hide-if-has-and-matches-style");
+ const hiddenMap = new WeakSet$6();
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ const styleRegExp = style ? toRegExp(style) : null;
+ const searchStyleRegExp = searchStyle ? toRegExp(searchStyle) : null;
+ const mainLogic = () => {
+ const callback = () => {
+ if ((windowWidthMin && window.innerWidth < windowWidthMin) ||
+ (windowWidthMax && window.innerWidth > windowWidthMax)
+ )
+ return;
+ for (const element of $$(searchSelector)) {
+ if (hiddenMap.has(element))
+ continue;
+ if ($(element).querySelector(search) &&
+ (!searchStyleRegExp ||
+ searchStyleRegExp.test(getComputedCSSText(element)))) {
+ const closest = $(element).closest(selector);
+ if (closest && (!styleRegExp ||
+ styleRegExp.test(getComputedCSSText(closest)))) {
+ win();
+ hideElement(closest);
+ hiddenMap.add(element);
+ debugLog("Matched: ",
+ closest,
+ "which contains: ",
+ element,
+ " for params: ",
+ ...arguments);
+ }
+ else {
+ debugLog(
+ "In this element the searchStyle matched but style didn't:\n",
+ closest,
+ getComputedStyle$1(closest),
+ ...arguments);
+ }
+ }
+ else {
+ debugLog(
+ "In this element the searchStyle didn't match:\n",
+ element,
+ getComputedStyle$1(element),
+ ...arguments);
+ }
+ }
+ };
+
+ const mo = new MutationObserver$6(callback);
+ const win = raceWinner(
+ "hide-if-has-and-matches-style",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {childList: true, subtree: true});
+ callback();
+ };
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
+ }
+
+ let {getComputedStyle, MutationObserver: MutationObserver$5, WeakSet: WeakSet$5} = $(window);
+
+ function hideIfLabelledBy(search, selector, searchSelector = null) {
+ let sameSelector = searchSelector == null;
+
+ let searchRegExp = toRegExp(search);
+
+ let matched = new WeakSet$5();
+
+ let callback = () => {
+ for (let node of $$(selector)) {
+ let closest = sameSelector ?
+ node :
+ $(node).closest(searchSelector);
+ if (!closest || !isVisible(node, getComputedStyle(node), closest))
+ continue;
+
+ let attr = $(node).getAttribute("aria-labelledby");
+ let fallback = () => {
+ if (matched.has(closest))
+ return;
+
+ if (searchRegExp.test(
+ $(node).getAttribute("aria-label") || ""
+ )) {
+ win();
+ matched.add(closest);
+ hideElement(closest);
+ }
+ };
+
+ if (attr) {
+ for (let label of $(attr).split(/\s+/)) {
+ let target = $(document).getElementById(label);
+ if (target) {
+ if (!matched.has(target) && searchRegExp.test(target.innerText)) {
+ win();
+ matched.add(target);
+ hideElement(closest);
+ }
+ }
+ else {
+ fallback();
+ }
+ }
+ }
+ else {
+ fallback();
+ }
+ }
+ };
+
+ let mo = new MutationObserver$5(callback);
+ let win = raceWinner(
+ "hide-if-labelled-by",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {characterData: true, childList: true, subtree: true});
+ callback();
+ }
+
+ $(window);
+
+ const noopProfile = {
+ mark() {},
+ end() {},
+ toString() {
+ return "{mark(){},end(){}}";
+ }
+ };
+
+ function profile(id, rate = 10) {
+ return noopProfile;
+ }
+
+ let {MutationObserver: MutationObserver$4, WeakSet: WeakSet$4} = $(window);
+
+ const {ELEMENT_NODE: ELEMENT_NODE$2} = Node;
+
+ function hideIfMatchesXPath(query, scopeQuery) {
+ const {mark, end} = profile();
+ const debugLog = getDebugger("hide-if-matches-xpath");
+
+ const startHidingMutationObserver = scopeNode => {
+ const queryAndApply = initQueryAndApply(`xpath(${query})`);
+ const seenMap = new WeakSet$4();
+ const callback = () => {
+ mark();
+ queryAndApply(node => {
+ if (seenMap.has(node))
+ return false;
+ seenMap.add(node);
+ win();
+ if ($(node).nodeType === ELEMENT_NODE$2)
+ hideElement(node);
+ else
+ $(node).textContent = "";
+ debugLog("Matched: ", node, " for selector: ", query);
+ });
+ end();
+ };
+ const mo = new MutationObserver$4(callback);
+ const win = raceWinner(
+ "hide-if-matches-xpath",
+ () => mo.disconnect()
+ );
+ mo.observe(
+ scopeNode, {characterData: true, childList: true, subtree: true});
+ callback();
+ };
+
+ if (scopeQuery) {
+
+ let count = 0;
+ let scopeMutationObserver;
+ const scopeQueryAndApply = initQueryAndApply(`xpath(${scopeQuery})`);
+ const findMutationScopeNodes = () => {
+ scopeQueryAndApply(scopeNode => {
+
+ startHidingMutationObserver(scopeNode);
+ count++;
+ });
+ if (count > 0)
+ scopeMutationObserver.disconnect();
+ };
+ scopeMutationObserver = new MutationObserver$4(findMutationScopeNodes);
+ scopeMutationObserver.observe(
+ document, {characterData: true, childList: true, subtree: true}
+ );
+ findMutationScopeNodes();
+ }
+ else {
+
+ startHidingMutationObserver(document);
+ }
+ }
+
+ let {MutationObserver: MutationObserver$3, WeakSet: WeakSet$3} = $(window);
+
+ const {ELEMENT_NODE: ELEMENT_NODE$1} = Node;
+
+ function hideIfMatchesComputedXPath(query, searchQuery, searchRegex,
+ waitUntil) {
+ const {mark, end} = profile();
+ const debugLog = getDebugger("hide-if-matches-computed-xpath");
+
+ if (!searchQuery || !query) {
+ debugLog("No query or searchQuery provided.");
+ return;
+ }
+
+ const computeQuery = foundText => query.replace("{{}}", foundText);
+
+ const startHidingMutationObserver = foundText => {
+ const computedQuery = computeQuery(foundText);
+ debugLog("Starting hiding elements that match query: ", computedQuery);
+ const queryAndApply = initQueryAndApply(`xpath(${computedQuery})`);
+ const seenMap = new WeakSet$3();
+ const callback = () => {
+ mark();
+ queryAndApply(node => {
+ if (seenMap.has(node))
+ return false;
+ seenMap.add(node);
+ win();
+ if ($(node).nodeType === ELEMENT_NODE$1)
+ hideElement(node);
+ else
+ $(node).textContent = "";
+ debugLog("Matched: ", node, " for selector: ", query);
+ });
+ end();
+ };
+ const mo = new MutationObserver$3(callback);
+ const win = raceWinner(
+ "hide-if-matches-computed-xpath",
+ () => mo.disconnect()
+ );
+ mo.observe(
+ document, {characterData: true, childList: true, subtree: true});
+ callback();
+ };
+
+ const re = toRegExp(searchRegex);
+
+ const mainLogic = () => {
+ if (searchQuery) {
+ debugLog("Started searching for: ", searchQuery);
+ const seenMap = new WeakSet$3();
+ let searchMO;
+ const searchQueryAndApply = initQueryAndApply(`xpath(${searchQuery})`);
+ const findMutationSearchNodes = () => {
+ searchQueryAndApply(searchNode => {
+ if (seenMap.has(searchNode))
+ return false;
+ seenMap.add(searchNode);
+ debugLog("Found node: ", searchNode);
+ if (searchNode.innerHTML) {
+ debugLog("Searching in: ", searchNode.innerHTML);
+ const foundTextArr = searchNode.innerHTML.match(re);
+ if (foundTextArr && foundTextArr.length) {
+ let foundText = "";
+
+ foundTextArr[1] ? foundText = foundTextArr[1] :
+ foundText = foundTextArr[0];
+ debugLog("Matched search query: ", foundText);
+ startHidingMutationObserver(foundText);
+ }
+ }
+ });
+ };
+
+ searchMO = new MutationObserver$3(findMutationSearchNodes);
+ searchMO.observe(
+ document, {characterData: true, childList: true, subtree: true}
+ );
+ findMutationSearchNodes();
+ }
+ };
+
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
+ }
+
+ let {
+ parseInt: parseInt$1,
+ setTimeout: setTimeout$1,
+ Error: Error$2,
+ MouseEvent: MouseEvent$2,
+ MutationObserver: MutationObserver$2,
+ WeakSet: WeakSet$2
+ } = $(window);
+
+ function simulateEvent(event, selector, delay = "0") {
+ if (!event)
+ throw new Error$2("[simulate-event snippet]: No event type provided.");
+ if (!selector)
+ throw new Error$2("[simulate-event snippet]: No selector provided.");
+
+ let queryAndApply = initQueryAndApply(selector);
+ let delayInMiliseconds = parseInt$1(delay, 10);
+ let dispatchedNodes = new WeakSet$2();
+
+ let observer = new MutationObserver$2(findNodesAndDispatchEvents);
+ observer.observe(document, {childList: true, subtree: true});
+ findNodesAndDispatchEvents();
+
+ function findNodesAndDispatchEvents() {
+ queryAndApply(node => {
+ if (!dispatchedNodes.has(node)) {
+ dispatchedNodes.add(node);
+ setTimeout$1(() => {
+ $(node).dispatchEvent(
+ new MouseEvent$2(event, {bubbles: true, cancelable: true})
+ );
+ }, delayInMiliseconds);
+ }
+ });
+ }
+ }
+
+ let {
+ parseInt,
+ setTimeout,
+ Error: Error$1,
+ MouseEvent: MouseEvent$1,
+ MutationObserver: MutationObserver$1,
+ WeakSet: WeakSet$1
+ } = $(window);
+
+ const VALID_TYPES = ["auxclick", "click", "dblclick", "gotpointercapture",
+ "lostpointercapture", "mouseenter", "mousedown",
+ "mouseleave", "mousemove", "mouseout", "mouseover",
+ "mouseup", "pointerdown", "pointerenter",
+ "pointermove", "pointerover", "pointerout",
+ "pointerup", "pointercancel", "pointerleave"];
+
+ function simulateMouseEvent(...selectors) {
+ const debugLog = getDebugger("simulate-mouse-event");
+ const MAX_ARGS = 7;
+ if (selectors.length < 1)
+ throw new Error$1("[simulate-mouse-event snippet]: No selector provided.");
+ if (selectors.length > MAX_ARGS) {
+
+ selectors = selectors.slice(0, MAX_ARGS);
+ }
+ function parseArg(theRule) {
+ if (!theRule)
+ return null;
+
+ const result = {
+ selector: "",
+ continue: false,
+ trigger: false,
+ event: "click",
+ delay: "500",
+ clicked: false,
+ found: false
+ };
+ const textArr = theRule.split("$");
+ let options = [];
+ if (textArr.length >= 2)
+ options = textArr[1].toLowerCase().split(",");
+
+ [result.selector] = textArr;
+
+ for (const option of options) {
+ if (option === "trigger") {
+ result.trigger = true;
+ }
+ else if (option === "continue") {
+ result.continue = true;
+ }
+ else if (option.startsWith("event")) {
+ const event = option.toLowerCase().split("=");
+ event[1] ? result.event = event[1] : result.event = "click";
+ }
+ else if (option.startsWith("delay")) {
+ const delay = option.toLowerCase().split("=");
+ delay[1] ? result.delay = delay[1] : result.delay = "500";
+ }
+ }
+ if (!VALID_TYPES.includes(result.event)) {
+ debugLog(result.event,
+ " might be misspelled, check for typos.\n",
+ "These are the supported events:",
+ VALID_TYPES);
+ }
+ return result;
+ }
+
+ const parsedArgs = $([]);
+
+ $(selectors).forEach(rule => {
+ const parsedRule = parseArg(rule);
+ parsedArgs.push(parsedRule);
+ });
+
+ function checkIfAllSelectorsFound() {
+ parsedArgs.forEach(arg => {
+ if (!arg.found) {
+ const queryAll = initQueryAll(arg.selector);
+ const elems = queryAll();
+ if (elems.length > 0)
+ arg.found = true;
+ }
+ });
+ return parsedArgs.every(arg => arg.found);
+ }
+
+ function triggerEvent(node, event, delay) {
+
+ if (!node || !event)
+ return;
+
+ if (event === "click" && node.click) {
+ node.click();
+ debugLog(
+ "Clicked on this node:\n", node, "\nwith a delay of", delay, "ms"
+ );
+ }
+ else {
+ node.dispatchEvent(
+ new MouseEvent$1(event, {bubbles: true, cancelable: true})
+ );
+ debugLog(
+ "A",
+ event,
+ "event was dispatched with a delay of",
+ delay,
+ "ms on this node:\n",
+ node
+ );
+ }
+ }
+ let allFound = false;
+
+ const [last] = parsedArgs.slice(-1);
+ last.trigger = true;
+
+ let dispatchedNodes = new WeakSet$1();
+
+ let observer = new MutationObserver$1(findNodesAndDispatchEvents);
+ observer.observe(document, {childList: true, subtree: true});
+ findNodesAndDispatchEvents();
+
+ function findNodesAndDispatchEvents() {
+
+ if (!allFound)
+ allFound = checkIfAllSelectorsFound();
+ if (allFound) {
+ for (const parsedRule of parsedArgs) {
+ const queryAndApply = initQueryAndApply(parsedRule.selector);
+ const delayInMiliseconds = parseInt(parsedRule.delay, 10);
+ if (parsedRule.trigger) {
+ queryAndApply(node => {
+ if (!dispatchedNodes.has(node)) {
+ dispatchedNodes.add(node);
+ if (parsedRule.continue) {
+ setInterval(() => {
+ triggerEvent(node, parsedRule.event, parsedRule.delay);
+ }, delayInMiliseconds);
+ }
+ else {
+ setTimeout(() => {
+ triggerEvent(node, parsedRule.event, parsedRule.delay);
+ }, delayInMiliseconds);
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+
+ const snippets = {
+ log,
+ race,
+ "debug": setDebug,
+ "hide-if-matches-xpath": hideIfMatchesXPath,
+ "hide-if-matches-computed-xpath": hideIfMatchesComputedXPath,
+ "hide-if-contains": hideIfContains,
+ "hide-if-contains-similar-text": hideIfContainsSimilarText,
+ "hide-if-contains-visible-text": hideIfContainsVisibleText,
+ "hide-if-contains-and-matches-style": hideIfContainsAndMatchesStyle,
+ "hide-if-has-and-matches-style": hideIfHasAndMatchesStyle,
+ "hide-if-labelled-by": hideIfLabelledBy,
+ "hide-if-contains-image": hideIfContainsImage,
+ "simulate-event-poc": simulateEvent,
+ "simulate-mouse-event": simulateMouseEvent
+ };
+
+ let {MutationObserver} = $(window);
+
+ const {ELEMENT_NODE} = Node;
+
+ function hideIfMatchesXPath3(query, scopeQuery) {
+ let {mark, end} = profile();
+
+ function queryNodes() {
+ return fontoxpath.evaluateXPathToNodes(query, document, null, null, {
+ language: fontoxpath.evaluateXPath.XQUERY_3_1_LANGUAGE
+ });
+ }
+
+ let debugLog = getDebugger("hide-if-matches-xpath3");
+
+ const startHidingMutationObserver = scopeNode => {
+ const seenMap = new WeakSet();
+ const callback = () => {
+ mark();
+
+ const nodes = queryNodes();
+ for (const node of $(nodes)) {
+ if (seenMap.has(node))
+ return false;
+ seenMap.add(node);
+ win();
+ if ($(node).nodeType === ELEMENT_NODE)
+ hideElement(node);
+ else
+ $(node).textContent = "";
+ debugLog("Matched: ", node, " for selector: ", query);
+ }
+ end();
+ };
+
+ const mo = new MutationObserver(callback);
+ const win = raceWinner(
+ "hide-if-matches-xpath3",
+ () => mo.disconnect()
+ );
+ mo.observe(
+ scopeNode, {characterData: true, childList: true, subtree: true});
+ callback();
+ };
+
+ if (scopeQuery) {
+
+ let count = 0;
+ let scopeMutationObserver;
+ const scopeNodes = queryNodes();
+ const findMutationScopeNodes = () => {
+ for (const scopeNode of $(scopeNodes)) {
+
+ startHidingMutationObserver(scopeNode);
+ count++;
+ }
+ if (count > 0)
+ scopeMutationObserver.disconnect();
+ };
+
+ scopeMutationObserver = new MutationObserver(findMutationScopeNodes);
+ scopeMutationObserver.observe(
+ document, {characterData: true, childList: true, subtree: true}
+ );
+ findMutationScopeNodes();
+ }
+ else {
+
+ startHidingMutationObserver(document);
+ }
+ }
+
+ snippets["hide-if-matches-xpath3"] = hideIfMatchesXPath3;
+ let context;
+ for (const [name, ...args] of filters) {
+ if (snippets.hasOwnProperty(name)) {
+ try { context = snippets[name].apply(context, args); }
+ catch (error) { console.error(error); }
+ }
+ }
+ context = void 0;
+})(e, ...t);
+
+const callback = (environment, ...filters) => {
+ /*!
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * @eyeo/snippets 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/snippets 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/snippets. If not, see <http://www.gnu.org/licenses/>.
+ */
+ const $$1 = Proxy;
+
+ const {apply: a, bind: b, call: c} = Function;
+ const apply$2 = c.bind(a);
+ const bind = c.bind(b);
+ const call = c.bind(c);
+
+ const callerHandler = {
+ get(target, name) {
+ return bind(c, target[name]);
+ }
+ };
+
+ const caller = target => new $$1(target, callerHandler);
+
+ const proxy = (source, target) => new $$1(source, {
+ apply: (_, self, args) => apply$2(target, self, args)
+ });
+
+ const handler$2 = {
+ get(target, name) {
+ return bind(target[name], target);
+ }
+ };
+
+ const bound = target => new $$1(target, handler$2);
+
+ const {
+ assign: assign$1,
+ defineProperties: defineProperties$1,
+ freeze: freeze$1,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$3,
+ getOwnPropertyDescriptors: getOwnPropertyDescriptors$1,
+ getPrototypeOf
+ } = bound(Object);
+
+ const {hasOwnProperty} = caller({});
+
+ const {species} = Symbol;
+
+ const handler$1 = {
+ get(target, name) {
+ const Native = target[name];
+ class Secure extends Native {}
+
+ const proto = getOwnPropertyDescriptors$1(Native.prototype);
+ delete proto.constructor;
+ freeze$1(defineProperties$1(Secure.prototype, proto));
+
+ const statics = getOwnPropertyDescriptors$1(Native);
+ delete statics.length;
+ delete statics.prototype;
+ statics[species] = {value: Secure};
+ return freeze$1(defineProperties$1(Secure, statics));
+ }
+ };
+
+ const secure = target => new $$1(target, handler$1);
+
+ const libEnvironment = typeof environment !== "undefined" ? environment :
+ {};
+
+ if (typeof globalThis === "undefined")
+ window.globalThis = window;
+
+ const {apply: apply$1, ownKeys} = bound(Reflect);
+
+ const worldEnvDefined = "world" in libEnvironment;
+ const isIsolatedWorld = worldEnvDefined && libEnvironment.world === "ISOLATED";
+ const isMainWorld = worldEnvDefined && libEnvironment.world === "MAIN";
+ const isChrome = typeof chrome === "object" && !!chrome.runtime;
+ const isOtherThanChrome = typeof browser === "object" && !!browser.runtime;
+ const isExtensionContext$2 = !isMainWorld &&
+ (isIsolatedWorld || isChrome || isOtherThanChrome);
+ const copyIfExtension = value => isExtensionContext$2 ?
+ value :
+ create(value, getOwnPropertyDescriptors(value));
+
+ const {
+ create,
+ defineProperties,
+ defineProperty,
+ freeze,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$2,
+ getOwnPropertyDescriptors
+ } = bound(Object);
+
+ const invokes = bound(globalThis);
+ const classes = isExtensionContext$2 ? globalThis : secure(globalThis);
+ const {Map: Map$8, RegExp: RegExp$1, Set: Set$2, WeakMap: WeakMap$4, WeakSet: WeakSet$3} = classes;
+
+ const augment = (source, target, method = null) => {
+ const known = ownKeys(target);
+ for (const key of ownKeys(source)) {
+ if (known.includes(key))
+ continue;
+
+ const descriptor = getOwnPropertyDescriptor$2(source, key);
+ if (method && "value" in descriptor) {
+ const {value} = descriptor;
+ if (typeof value === "function")
+ descriptor.value = method(value);
+ }
+ defineProperty(target, key, descriptor);
+ }
+ };
+
+ const primitive = name => {
+ const Super = classes[name];
+ class Class extends Super {}
+ const {toString, valueOf} = Super.prototype;
+ defineProperties(Class.prototype, {
+ toString: {value: toString},
+ valueOf: {value: valueOf}
+ });
+ const type = name.toLowerCase();
+ const method = callback => function() {
+ const result = apply$1(callback, this, arguments);
+ return typeof result === type ? new Class(result) : result;
+ };
+ augment(Super, Class, method);
+ augment(Super.prototype, Class.prototype, method);
+ return Class;
+ };
+
+ const variables$3 = freeze({
+ frozen: new WeakMap$4(),
+ hidden: new WeakSet$3(),
+ iframePropertiesToAbort: {
+ read: new Set$2(),
+ write: new Set$2()
+ },
+ abortedIframes: new WeakMap$4()
+ });
+
+ const startsCapitalized = new RegExp$1("^[A-Z]");
+
+ var env = new Proxy(new Map$8([
+
+ ["chrome", (
+ isExtensionContext$2 && (
+ (isChrome && chrome) ||
+ (isOtherThanChrome && browser)
+ )
+ ) || void 0],
+ ["isExtensionContext", isExtensionContext$2],
+ ["variables", variables$3],
+
+ ["console", copyIfExtension(console)],
+ ["document", globalThis.document],
+ ["performance", copyIfExtension(performance)],
+ ["JSON", copyIfExtension(JSON)],
+ ["Map", Map$8],
+ ["Math", copyIfExtension(Math)],
+ ["Number", isExtensionContext$2 ? Number : primitive("Number")],
+ ["RegExp", RegExp$1],
+ ["Set", Set$2],
+ ["String", isExtensionContext$2 ? String : primitive("String")],
+ ["WeakMap", WeakMap$4],
+ ["WeakSet", WeakSet$3],
+
+ ["MouseEvent", MouseEvent]
+ ]), {
+ get(map, key) {
+ if (map.has(key))
+ return map.get(key);
+
+ let value = globalThis[key];
+ if (typeof value === "function")
+ value = (startsCapitalized.test(key) ? classes : invokes)[key];
+
+ map.set(key, value);
+ return value;
+ },
+ has(map, key) {
+ return map.has(key);
+ }
+ });
+
+ class WeakValue {
+ has() { return false; }
+ set() {}
+ }
+
+ const helpers = {WeakSet, WeakMap, WeakValue};
+ const {apply} = Reflect;
+
+ function transformOnce (callback) { const {WeakSet, WeakMap, WeakValue} = (this || helpers);
+ const ws = new WeakSet;
+ const wm = new WeakMap;
+ const wv = new WeakValue;
+ return function (any) {
+ if (ws.has(any))
+ return any;
+
+ if (wm.has(any))
+ return wm.get(any);
+
+ if (wv.has(any))
+ return wv.get(any);
+
+ const value = apply(callback, this, arguments);
+ ws.add(value);
+ if (value !== any)
+ (typeof any === 'object' && any ? wm : wv).set(any, value);
+ return value;
+ };
+ }
+
+ const {Map: Map$7, WeakMap: WeakMap$3, WeakSet: WeakSet$2, setTimeout} = env;
+
+ let cleanup = true;
+ let cleanUpCallback = map => {
+ map.clear();
+ cleanup = !cleanup;
+ };
+
+ var transformer = transformOnce.bind({
+ WeakMap: WeakMap$3,
+ WeakSet: WeakSet$2,
+
+ WeakValue: class extends Map$7 {
+ set(key, value) {
+ if (cleanup) {
+ cleanup = !cleanup;
+ setTimeout(cleanUpCallback, 0, this);
+ }
+ return super.set(key, value);
+ }
+ }
+ });
+
+ const {concat, includes, join, reduce, unshift} = caller([]);
+
+ const globals = secure(globalThis);
+
+ const {
+ Map: Map$6,
+ WeakMap: WeakMap$2
+ } = globals;
+
+ const map = new Map$6;
+ const descriptors = target => {
+ const chain = [];
+ let current = target;
+ while (current) {
+ if (map.has(current))
+ unshift(chain, map.get(current));
+ else {
+ const descriptors = getOwnPropertyDescriptors$1(current);
+ map.set(current, descriptors);
+ unshift(chain, descriptors);
+ }
+ current = getPrototypeOf(current);
+ }
+ unshift(chain, {});
+ return apply$2(assign$1, null, chain);
+ };
+
+ const chain = source => {
+ const target = typeof source === 'function' ? source.prototype : source;
+ const chained = descriptors(target);
+ const handler = {
+ get(target, key) {
+ if (key in chained) {
+ const {value, get} = chained[key];
+ if (get)
+ return call(get, target);
+ if (typeof value === 'function')
+ return bind(value, target);
+ }
+ return target[key];
+ },
+ set(target, key, value) {
+ if (key in chained) {
+ const {set} = chained[key];
+ if (set) {
+ call(set, target, value);
+ return true;
+ }
+ }
+ target[key] = value;
+ return true;
+ }
+ };
+ return target => new $$1(target, handler);
+ };
+
+ const {
+ isExtensionContext: isExtensionContext$1,
+ Array: Array$2,
+ Number: Number$1,
+ String: String$1,
+ Object: Object$9
+ } = env;
+
+ const {isArray} = Array$2;
+ const {getOwnPropertyDescriptor: getOwnPropertyDescriptor$1, setPrototypeOf: setPrototypeOf$1} = Object$9;
+
+ const {toString: toString$1} = Object$9.prototype;
+ const {slice} = String$1.prototype;
+ const getBrand = value => call(slice, call(toString$1, value), 8, -1);
+
+ const {get: nodeType} = getOwnPropertyDescriptor$1(Node.prototype, "nodeType");
+
+ const chained = isExtensionContext$1 ? {} : {
+ Attr: chain(Attr),
+ CanvasRenderingContext2D: chain(CanvasRenderingContext2D),
+ CSSStyleDeclaration: chain(CSSStyleDeclaration),
+ Document: chain(Document),
+ Element: chain(Element),
+ HTMLCanvasElement: chain(HTMLCanvasElement),
+ HTMLElement: chain(HTMLElement),
+ HTMLImageElement: chain(HTMLImageElement),
+ HTMLScriptElement: chain(HTMLScriptElement),
+ MutationRecord: chain(MutationRecord),
+ Node: chain(Node),
+ ShadowRoot: chain(ShadowRoot),
+
+ get CSS2Properties() {
+ return chained.CSSStyleDeclaration;
+ }
+ };
+
+ const upgrade = (value, hint) => {
+ if (hint !== "Element" && hint in chained)
+ return chained[hint](value);
+
+ if (isArray(value))
+ return setPrototypeOf$1(value, Array$2.prototype);
+
+ const brand = getBrand(value);
+ if (brand in chained)
+ return chained[brand](value);
+
+ if (brand in env)
+ return setPrototypeOf$1(value, env[brand].prototype);
+
+ if ("nodeType" in value) {
+ switch (call(nodeType, value)) {
+ case 1:
+ if (!(hint in chained))
+ throw new Error("unknown hint " + hint);
+ return chained[hint](value);
+ case 2:
+ return chained.Attr(value);
+ case 3:
+ return chained.Node(value);
+ case 9:
+ return chained.Document(value);
+ }
+ }
+
+ throw new Error("unknown brand " + brand);
+ };
+
+ var $ = isExtensionContext$1 ?
+ value => (value === window || value === globalThis ? env : value) :
+ transformer((value, hint = "Element") => {
+ if (value === window || value === globalThis)
+ return env;
+
+ switch (typeof value) {
+ case "object":
+ return value && upgrade(value, hint);
+
+ case "string":
+ return new String$1(value);
+
+ case "number":
+ return new Number$1(value);
+
+ default:
+ throw new Error("unsupported value");
+ }
+ });
+
+ const handler = {
+ get(target, name) {
+ const context = target;
+ while (!hasOwnProperty(target, name))
+ target = getPrototypeOf(target);
+ const {get, set} = getOwnPropertyDescriptor$3(target, name);
+ return function () {
+ return arguments.length ?
+ apply$2(set, context, arguments) :
+ call(get, context);
+ };
+ }
+ };
+
+ const accessor = target => new $$1(target, handler);
+
+ let debugging = false;
+
+ function debug() {
+ return debugging;
+ }
+
+ function setDebug() {
+ debugging = true;
+ }
+
+ const {console: console$3} = $(window);
+
+ const noop = () => {};
+
+ function log(...args) {
+ if (debug())
+ $(args).unshift("%c DEBUG", "font-weight: bold");
+
+ console$3.log(...args);
+ }
+
+ function getDebugger(name) {
+ return bind(debug() ? log : noop, null, name);
+ }
+
+ let {Math: Math$1, RegExp} = $(window);
+
+ function regexEscape(string) {
+ return $(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
+ }
+
+ function toRegExp(pattern) {
+ let {length} = pattern;
+
+ if (length > 1 && pattern[0] === "/") {
+ let isCaseSensitive = pattern[length - 1] === "/";
+
+ if (isCaseSensitive || (length > 2 && $(pattern).endsWith("/i"))) {
+ let args = [$(pattern).slice(1, isCaseSensitive ? -1 : -2)];
+ if (!isCaseSensitive)
+ args.push("i");
+
+ return new RegExp(...args);
+ }
+ }
+
+ return new RegExp(regexEscape(pattern));
+ }
+
+ function randomId() {
+
+ return $(Math$1.floor(Math$1.random() * 2116316160 + 60466176)).toString(36);
+ }
+
+ let {
+ parseFloat,
+ variables: variables$2,
+ Array: Array$1,
+ Error: Error$7,
+ Map: Map$5,
+ Object: Object$8,
+ ReferenceError: ReferenceError$2,
+ Set: Set$1,
+ WeakMap: WeakMap$1
+ } = $(window);
+
+ let {onerror} = accessor(window);
+
+ let NodeProto$1 = Node.prototype;
+ let ElementProto$2 = Element.prototype;
+
+ let propertyAccessors = null;
+
+ function wrapPropertyAccess(object, property, descriptor,
+ setConfigurable = true) {
+ let $property = $(property);
+ let dotIndex = $property.indexOf(".");
+ if (dotIndex == -1) {
+
+ let currentDescriptor = Object$8.getOwnPropertyDescriptor(object, property);
+ if (currentDescriptor && !currentDescriptor.configurable)
+ return;
+
+ let newDescriptor = Object$8.assign({}, descriptor, {
+ configurable: setConfigurable
+ });
+
+ if (!currentDescriptor && !newDescriptor.get && newDescriptor.set) {
+ let propertyValue = object[property];
+ newDescriptor.get = () => propertyValue;
+ }
+
+ Object$8.defineProperty(object, property, newDescriptor);
+ return;
+ }
+
+ let name = $property.slice(0, dotIndex).toString();
+ property = $property.slice(dotIndex + 1).toString();
+ let value = object[name];
+ if (value && (typeof value == "object" || typeof value == "function"))
+ wrapPropertyAccess(value, property, descriptor);
+
+ let currentDescriptor = Object$8.getOwnPropertyDescriptor(object, name);
+ if (currentDescriptor && !currentDescriptor.configurable)
+ return;
+
+ if (!propertyAccessors)
+ propertyAccessors = new WeakMap$1();
+
+ if (!propertyAccessors.has(object))
+ propertyAccessors.set(object, new Map$5());
+
+ let properties = propertyAccessors.get(object);
+ if (properties.has(name)) {
+ properties.get(name).set(property, descriptor);
+ return;
+ }
+
+ let toBeWrapped = new Map$5([[property, descriptor]]);
+ properties.set(name, toBeWrapped);
+ Object$8.defineProperty(object, name, {
+ get: () => value,
+ set(newValue) {
+ value = newValue;
+ if (value && (typeof value == "object" || typeof value == "function")) {
+
+ for (let [prop, desc] of toBeWrapped)
+ wrapPropertyAccess(value, prop, desc);
+ }
+ },
+ configurable: setConfigurable
+ });
+ }
+
+ function overrideOnError(magic) {
+ let prev = onerror();
+ onerror((...args) => {
+ let message = args.length && args[0];
+ if (typeof message == "string" && $(message).includes(magic))
+ return true;
+ if (typeof prev == "function")
+ return apply$2(prev, this, args);
+ });
+ }
+
+ function abortOnRead(loggingPrefix, context, property,
+ setConfigurable = true) {
+ let debugLog = getDebugger(loggingPrefix);
+
+ if (!property) {
+ debugLog("no property to abort on read");
+ return;
+ }
+
+ let rid = randomId();
+
+ function abort() {
+ debugLog(`${property} access aborted`);
+ throw new ReferenceError$2(rid);
+ }
+
+ debugLog(`aborting on ${property} access`);
+
+ wrapPropertyAccess(context,
+ property,
+ {get: abort, set() {}},
+ setConfigurable);
+ overrideOnError(rid);
+ }
+
+ function abortOnWrite(loggingPrefix, context, property,
+ setConfigurable = true) {
+ let debugLog = getDebugger(loggingPrefix);
+
+ if (!property) {
+ debugLog("no property to abort on write");
+ return;
+ }
+
+ let rid = randomId();
+
+ function abort() {
+ debugLog(`setting ${property} aborted`);
+ throw new ReferenceError$2(rid);
+ }
+
+ debugLog(`aborting when setting ${property}`);
+
+ wrapPropertyAccess(context, property, {set: abort}, setConfigurable);
+ overrideOnError(rid);
+ }
+
+ function abortOnIframe(
+ properties,
+ abortRead = false,
+ abortWrite = false
+ ) {
+ let abortedIframes = variables$2.abortedIframes;
+ let iframePropertiesToAbort = variables$2.iframePropertiesToAbort;
+
+ for (let frame of Array$1.from(window.frames)) {
+ if (abortedIframes.has(frame)) {
+ for (let property of properties) {
+ if (abortRead)
+ abortedIframes.get(frame).read.add(property);
+ if (abortWrite)
+ abortedIframes.get(frame).write.add(property);
+ }
+ }
+ }
+
+ for (let property of properties) {
+ if (abortRead)
+ iframePropertiesToAbort.read.add(property);
+ if (abortWrite)
+ iframePropertiesToAbort.write.add(property);
+ }
+
+ queryAndProxyIframe();
+ if (!abortedIframes.has(document)) {
+ abortedIframes.set(document, true);
+ addHooksOnDomAdditions(queryAndProxyIframe);
+ }
+
+ function queryAndProxyIframe() {
+ for (let frame of Array$1.from(window.frames)) {
+
+ if (!abortedIframes.has(frame)) {
+ abortedIframes.set(frame, {
+ read: new Set$1(iframePropertiesToAbort.read),
+ write: new Set$1(iframePropertiesToAbort.write)
+ });
+ }
+
+ let readProps = abortedIframes.get(frame).read;
+ if (readProps.size > 0) {
+ let props = Array$1.from(readProps);
+ readProps.clear();
+ for (let property of props)
+ abortOnRead("abort-on-iframe-property-read", frame, property);
+ }
+
+ let writeProps = abortedIframes.get(frame).write;
+ if (writeProps.size > 0) {
+ let props = Array$1.from(writeProps);
+ writeProps.clear();
+ for (let property of props)
+ abortOnWrite("abort-on-iframe-property-write", frame, property);
+ }
+ }
+ }
+ }
+
+ function addHooksOnDomAdditions(endCallback) {
+ let descriptor;
+
+ wrapAccess(NodeProto$1, ["appendChild", "insertBefore", "replaceChild"]);
+ wrapAccess(ElementProto$2, ["append", "prepend", "replaceWith", "after",
+ "before", "insertAdjacentElement",
+ "insertAdjacentHTML"]);
+
+ descriptor = getInnerHTMLDescriptor(ElementProto$2, "innerHTML");
+ wrapPropertyAccess(ElementProto$2, "innerHTML", descriptor);
+
+ descriptor = getInnerHTMLDescriptor(ElementProto$2, "outerHTML");
+ wrapPropertyAccess(ElementProto$2, "outerHTML", descriptor);
+
+ function wrapAccess(prototype, names) {
+ for (let name of names) {
+ let desc = getAppendChildDescriptor(prototype, name);
+ wrapPropertyAccess(prototype, name, desc);
+ }
+ }
+
+ function getAppendChildDescriptor(target, property) {
+ let currentValue = target[property];
+ return {
+ get() {
+ return function(...args) {
+ let result;
+ result = apply$2(currentValue, this, args);
+ endCallback && endCallback();
+ return result;
+ };
+ }
+ };
+ }
+
+ function getInnerHTMLDescriptor(target, property) {
+ let desc = Object$8.getOwnPropertyDescriptor(target, property);
+ let {set: prevSetter} = desc || {};
+ return {
+ set(val) {
+ let result;
+ result = call(prevSetter, this, val);
+ endCallback && endCallback();
+ return result;
+ }
+ };
+ }
+ }
+
+ let {Object: NativeObject} = window;
+ function findOwner(root, path) {
+ if (!(root instanceof NativeObject))
+ return;
+
+ let object = root;
+ let chain = $(path).split(".");
+
+ if (chain.length === 0)
+ return;
+
+ for (let i = 0; i < chain.length - 1; i++) {
+ let prop = chain[i];
+
+ if (!hasOwnProperty(object, prop))
+ return;
+
+ object = object[prop];
+
+ if (!(object instanceof NativeObject))
+ return;
+ }
+
+ let prop = chain[chain.length - 1];
+
+ if (hasOwnProperty(object, prop))
+ return [object, prop];
+ }
+
+ const decimals = $(/^\d+$/);
+
+ function overrideValue(value) {
+ switch (value) {
+ case "false":
+ return false;
+ case "true":
+ return true;
+ case "null":
+ return null;
+ case "noopFunc":
+ return () => {};
+ case "trueFunc":
+ return () => true;
+ case "falseFunc":
+ return () => false;
+ case "emptyArray":
+ return [];
+ case "emptyObj":
+ return {};
+ case "undefined":
+ return void 0;
+ case "":
+ return value;
+ default:
+ if (decimals.test(value))
+ return parseFloat(value);
+
+ throw new Error$7("[override-property-read snippet]: " +
+ `Value "${value}" is not valid.`);
+ }
+ }
+
+ let {HTMLScriptElement: HTMLScriptElement$1, Object: Object$7, ReferenceError: ReferenceError$1} = $(window);
+ let Script = Object$7.getPrototypeOf(HTMLScriptElement$1);
+
+ function abortCurrentInlineScript(api, search = null) {
+ const debugLog = getDebugger("abort-current-inline-script");
+ const re = search ? toRegExp(search) : null;
+
+ const rid = randomId();
+ const us = $(document).currentScript;
+
+ let object = window;
+ const path = $(api).split(".");
+ const name = $(path).pop();
+
+ for (let node of $(path)) {
+ object = object[node];
+ if (
+ !object || !(typeof object == "object" || typeof object == "function")) {
+ debugLog(path, " is not found");
+ return;
+ }
+ }
+
+ const {get: prevGetter, set: prevSetter} =
+ Object$7.getOwnPropertyDescriptor(object, name) || {};
+
+ let currentValue = object[name];
+ if (typeof currentValue === "undefined")
+ debugLog("The property", name, "doesn't exist yet. Check typos.");
+
+ const abort = () => {
+ const element = $(document).currentScript;
+ if (element instanceof Script &&
+ $(element, "HTMLScriptElement").src == "" &&
+ element != us &&
+ (!re || re.test($(element).textContent))) {
+ debugLog(path, " is aborted \n", element);
+ throw new ReferenceError$1(rid);
+ }
+ };
+
+ const descriptor = {
+ get() {
+ abort();
+
+ if (prevGetter)
+ return call(prevGetter, this);
+
+ return currentValue;
+ },
+ set(value) {
+ abort();
+
+ if (prevSetter)
+ call(prevSetter, this, value);
+ else
+ currentValue = value;
+ }
+ };
+
+ wrapPropertyAccess(object, name, descriptor);
+
+ overrideOnError(rid);
+ }
+
+ function abortOnIframePropertyRead(...properties) {
+ abortOnIframe(properties, true, false);
+ }
+
+ function abortOnIframePropertyWrite(...properties) {
+ abortOnIframe(properties, false, true);
+ }
+
+ function abortOnPropertyRead(property, setConfigurable) {
+ const configurableFlag = !(setConfigurable === "false");
+ abortOnRead("abort-on-property-read", window, property, configurableFlag);
+ }
+
+ function abortOnPropertyWrite(property, setConfigurable) {
+ const configurableFlag = !(setConfigurable === "false");
+ abortOnWrite("abort-on-property-write", window, property, configurableFlag);
+ }
+
+ let {Error: Error$6} = $(window);
+ let {cookie: documentCookies} = accessor(document);
+
+ function cookieRemover(cookie) {
+ if (!cookie)
+ throw new Error$6("[cookie-remover snippet]: No cookie to remove.");
+
+ let debugLog = getDebugger("cookie-remover");
+ let re = toRegExp(cookie);
+
+ if (!$(/^http|^about/).test(location.protocol)) {
+ debugLog("Snippet only works for http or https and about.");
+ return;
+ }
+
+ debugLog("Parsing cookies for matches");
+
+ for (const pair of $(getCookieMatches())) {
+ let $hostname = $(location.hostname);
+ let name = $(pair).split("=")[0];
+ let expires = "expires=Thu, 01 Jan 1970 00:00:00 GMT";
+ let path = "path=/";
+ let domain = "domain=" + $hostname.slice($hostname.indexOf(".") + 1);
+
+ documentCookies(`${$(name).trim()}=;${expires};${path};${domain}`);
+
+ debugLog(`Set expiration date on ${name}`);
+ }
+
+ function getCookieMatches() {
+ const arr = $(documentCookies()).split(";");
+ return arr.filter(str => re.test($(str).split("=")[0]));
+ }
+ }
+
+ let {
+ document: document$1,
+ getComputedStyle,
+ isExtensionContext,
+ variables: variables$1,
+ Array,
+ MutationObserver: MutationObserver$2,
+ Object: Object$6,
+ XPathEvaluator,
+ XPathExpression,
+ XPathResult
+ } = $(window);
+
+ let {querySelectorAll} = document$1;
+ let $$ = querySelectorAll && bind(querySelectorAll, document$1);
+
+ const {assign, setPrototypeOf} = Object$6;
+
+ class $XPathExpression extends XPathExpression {
+ evaluate(...args) {
+ return setPrototypeOf(
+ apply$2(super.evaluate, this, args),
+ XPathResult.prototype
+ );
+ }
+ }
+
+ class $XPathEvaluator extends XPathEvaluator {
+ createExpression(...args) {
+ return setPrototypeOf(
+ apply$2(super.createExpression, this, args),
+ $XPathExpression.prototype
+ );
+ }
+ }
+
+ function hideElement(element) {
+ if (variables$1.hidden.has(element))
+ return;
+
+ notifyElementHidden(element);
+
+ variables$1.hidden.add(element);
+
+ let {style} = $(element);
+ let $style = $(style, "CSSStyleDeclaration");
+ let properties = $([]);
+ let {debugCSSProperties} = libEnvironment;
+
+ for (let [key, value] of (debugCSSProperties || [["display", "none"]])) {
+ $style.setProperty(key, value, "important");
+ properties.push([key, $style.getPropertyValue(key)]);
+ }
+
+ new MutationObserver$2(() => {
+ for (let [key, value] of properties) {
+ let propertyValue = $style.getPropertyValue(key);
+ let propertyPriority = $style.getPropertyPriority(key);
+ if (propertyValue != value || propertyPriority != "important")
+ $style.setProperty(key, value, "important");
+ }
+ }).observe(element, {attributes: true,
+ attributeFilter: ["style"]});
+ }
+
+ function notifyElementHidden(element) {
+ if (isExtensionContext && typeof checkElement === "function")
+ checkElement(element);
+ }
+
+ function initQueryAndApply(selector) {
+ let $selector = selector;
+ if ($selector.startsWith("xpath(") &&
+ $selector.endsWith(")")) {
+ let xpathQuery = $selector.slice(6, -1);
+ let evaluator = new $XPathEvaluator();
+ let expression = evaluator.createExpression(xpathQuery, null);
+
+ let flag = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
+
+ return cb => {
+ if (!cb)
+ return;
+ let result = expression.evaluate(document$1, flag, null);
+ let {snapshotLength} = result;
+ for (let i = 0; i < snapshotLength; i++)
+ cb(result.snapshotItem(i));
+ };
+ }
+ return cb => $$(selector).forEach(cb);
+ }
+
+ function initQueryAll(selector) {
+ let $selector = selector;
+ if ($selector.startsWith("xpath(") &&
+ $selector.endsWith(")")) {
+ let queryAndApply = initQueryAndApply(selector);
+ return () => {
+ let elements = $([]);
+ queryAndApply(e => elements.push(e));
+ return elements;
+ };
+ }
+ return () => Array.from($$(selector));
+ }
+
+ let {ELEMENT_NODE, TEXT_NODE, prototype: NodeProto} = Node;
+ let {prototype: ElementProto$1} = Element;
+ let {prototype: HTMLElementProto} = HTMLElement;
+
+ let {
+ console: console$2,
+ variables,
+ DOMParser,
+ Error: Error$5,
+ MutationObserver: MutationObserver$1,
+ Object: Object$5,
+ ReferenceError
+ } = $(window);
+
+ let {getOwnPropertyDescriptor} = Object$5;
+
+ function freezeElement(selector, options = "", ...exceptions) {
+ let observer;
+ let subtree = false;
+ let shouldAbort = false;
+ let exceptionSelectors = $(exceptions).filter(e => !isRegex(e));
+ let regexExceptions = $(exceptions).filter(e => isRegex(e)).map(toRegExp);
+ let rid = randomId();
+ let targetNodes;
+ let queryAll = initQueryAll(selector);
+
+ checkOptions();
+ let data = {
+ selector,
+ shouldAbort,
+ rid,
+ exceptionSelectors,
+ regexExceptions,
+ changeId: 0
+ };
+ if (!variables.frozen.has(document)) {
+ variables.frozen.set(document, true);
+ proxyNativeProperties();
+ }
+ observer = new MutationObserver$1(searchAndAttach);
+ observer.observe(document, {childList: true, subtree: true});
+ searchAndAttach();
+
+ function isRegex(s) {
+ return s.length >= 2 && s[0] == "/" && s[s.length - 1] == "/";
+ }
+
+ function checkOptions() {
+ let optionsChunks = $(options).split("+");
+ if (optionsChunks.length === 1 && optionsChunks[0] === "")
+ optionsChunks = [];
+ for (let chunk of optionsChunks) {
+ switch (chunk) {
+ case "subtree":
+ subtree = true;
+ break;
+ case "abort":
+ shouldAbort = true;
+ break;
+ default:
+ throw new Error$5("[freeze] Unknown option passed to the snippet." +
+ " [selector]: " + selector +
+ " [option]: " + chunk);
+ }
+ }
+ }
+
+ function proxyNativeProperties() {
+ let descriptor;
+
+ descriptor = getAppendChildDescriptor(
+ NodeProto, "appendChild", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "appendChild", descriptor);
+
+ descriptor = getAppendChildDescriptor(
+ NodeProto, "insertBefore", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "insertBefore", descriptor);
+
+ descriptor = getAppendChildDescriptor(
+ NodeProto, "replaceChild", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "replaceChild", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1, "append", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(ElementProto$1, "append", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1, "prepend", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(ElementProto$1, "prepend", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1,
+ "replaceWith",
+ isFrozenOrHasFrozenParent,
+ getSnippetDataFromNodeOrParent
+ );
+ wrapPropertyAccess(ElementProto$1, "replaceWith", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1,
+ "after",
+ isFrozenOrHasFrozenParent,
+ getSnippetDataFromNodeOrParent
+ );
+ wrapPropertyAccess(ElementProto$1, "after", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1,
+ "before",
+ isFrozenOrHasFrozenParent,
+ getSnippetDataFromNodeOrParent
+ );
+ wrapPropertyAccess(ElementProto$1, "before", descriptor);
+
+ descriptor = getInsertAdjacentDescriptor(
+ ElementProto$1,
+ "insertAdjacentElement",
+ isFrozenAndInsideTarget,
+ getSnippetDataBasedOnTarget
+ );
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentElement", descriptor);
+
+ descriptor = getInsertAdjacentDescriptor(
+ ElementProto$1,
+ "insertAdjacentHTML",
+ isFrozenAndInsideTarget,
+ getSnippetDataBasedOnTarget
+ );
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentHTML", descriptor);
+
+ descriptor = getInsertAdjacentDescriptor(
+ ElementProto$1,
+ "insertAdjacentText",
+ isFrozenAndInsideTarget,
+ getSnippetDataBasedOnTarget
+ );
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentText", descriptor);
+
+ descriptor = getInnerHTMLDescriptor(
+ ElementProto$1, "innerHTML", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(ElementProto$1, "innerHTML", descriptor);
+
+ descriptor = getInnerHTMLDescriptor(
+ ElementProto$1,
+ "outerHTML",
+ isFrozenOrHasFrozenParent,
+ getSnippetDataFromNodeOrParent
+ );
+ wrapPropertyAccess(ElementProto$1, "outerHTML", descriptor);
+
+ descriptor = getTextContentDescriptor(
+ NodeProto, "textContent", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "textContent", descriptor);
+
+ descriptor = getTextContentDescriptor(
+ HTMLElementProto, "innerText", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(HTMLElementProto, "innerText", descriptor);
+
+ descriptor = getTextContentDescriptor(
+ NodeProto, "nodeValue", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "nodeValue", descriptor);
+
+ function isFrozen(node) {
+ return node && variables.frozen.has(node);
+ }
+
+ function isFrozenOrHasFrozenParent(node) {
+ try {
+ return node &&
+ (variables.frozen.has(node) ||
+ variables.frozen.has($(node).parentNode));
+ }
+ catch (error) {
+ return false;
+ }
+ }
+
+ function isFrozenAndInsideTarget(node, isInsideTarget) {
+ try {
+ return node &&
+ (variables.frozen.has(node) && isInsideTarget ||
+ variables.frozen.has($(node).parentNode) &&
+ !isInsideTarget);
+ }
+ catch (error) {
+ return false;
+ }
+ }
+
+ function getSnippetData(node) {
+ return variables.frozen.get(node);
+ }
+
+ function getSnippetDataFromNodeOrParent(node) {
+ try {
+ if (variables.frozen.has(node))
+ return variables.frozen.get(node);
+ let parent = $(node).parentNode;
+ return variables.frozen.get(parent);
+ }
+ catch (error) {}
+ }
+
+ function getSnippetDataBasedOnTarget(node, isInsideTarget) {
+ try {
+ if (variables.frozen.has(node) && isInsideTarget)
+ return variables.frozen.get(node);
+ let parent = $(node).parentNode;
+ return variables.frozen.get(parent);
+ }
+ catch (error) {}
+ }
+ }
+
+ function searchAndAttach() {
+ targetNodes = queryAll();
+ markNodes(targetNodes, false);
+ }
+
+ function markNodes(nodes, isChild = true) {
+ for (let node of nodes) {
+ if (!variables.frozen.has(node)) {
+ variables.frozen.set(node, data);
+ if (!isChild && subtree) {
+ new MutationObserver$1(mutationsList => {
+ for (let mutation of $(mutationsList))
+ markNodes($(mutation, "MutationRecord").addedNodes);
+ }).observe(node, {childList: true, subtree: true});
+ }
+ if (subtree && $(node).nodeType === ELEMENT_NODE)
+ markNodes($(node).childNodes);
+ }
+ }
+ }
+
+ function logPrefixed(id, ...args) {
+ log(`[freeze][${id}] `, ...args);
+ }
+
+ function logChange(nodeOrDOMString, target, property, snippetData) {
+ let targetSelector = snippetData.selector;
+ let chgId = snippetData.changeId;
+ let isDOMString = typeof nodeOrDOMString == "string";
+ let action = snippetData.shouldAbort ? "aborting" : "watching";
+ console$2.groupCollapsed(`[freeze][${chgId}] ${action}: ${targetSelector}`);
+ switch (property) {
+ case "appendChild":
+ case "append":
+ case "prepend":
+ case "insertBefore":
+ case "replaceChild":
+ case "insertAdjacentElement":
+ case "insertAdjacentHTML":
+ case "insertAdjacentText":
+ case "innerHTML":
+ case "outerHTML":
+ logPrefixed(chgId,
+ isDOMString ? "text: " : "node: ",
+ nodeOrDOMString);
+ logPrefixed(chgId, "added to node: ", target);
+ break;
+ case "replaceWith":
+ case "after":
+ case "before":
+ logPrefixed(chgId,
+ isDOMString ? "text: " : "node: ",
+ nodeOrDOMString);
+ logPrefixed(chgId, "added to node: ", $(target).parentNode);
+ break;
+ case "textContent":
+ case "innerText":
+ case "nodeValue":
+ logPrefixed(chgId, "content of node: ", target);
+ logPrefixed(chgId, "changed to: ", nodeOrDOMString);
+ break;
+ }
+ logPrefixed(chgId, `using the function "${property}"`);
+ console$2.groupEnd();
+ snippetData.changeId++;
+ }
+
+ function isExceptionNode(element, expSelectors) {
+ if (expSelectors) {
+ let $element = $(element);
+ for (let exception of expSelectors) {
+ if ($element.matches(exception))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function isExceptionText(string, regExceptions) {
+ if (regExceptions) {
+ for (let exception of regExceptions) {
+ if (exception.test(string))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function abort(id) {
+ throw new ReferenceError(id);
+ }
+
+ function checkHTML(htmlText, parent, property, snippetData) {
+ let domparser = new DOMParser();
+ let {body} = $(domparser.parseFromString(htmlText, "text/html"));
+ let nodes = $(body).childNodes;
+ let accepted = checkMultiple(nodes, parent, property, snippetData);
+ let content = $(accepted).map(node => {
+ switch ($(node).nodeType) {
+ case ELEMENT_NODE:
+ return $(node).outerHTML;
+ case TEXT_NODE:
+ return $(node).textContent;
+ default:
+ return "";
+ }
+ });
+ return content.join("");
+ }
+
+ function checkMultiple(nodesOrDOMStrings, parent, property, snippetData) {
+ let accepted = $([]);
+ for (let nodeOrDOMString of nodesOrDOMStrings) {
+ if (checkShouldInsert(nodeOrDOMString, parent, property, snippetData))
+ accepted.push(nodeOrDOMString);
+ }
+ return accepted;
+ }
+
+ function checkShouldInsert(nodeOrDOMString, parent, property, snippetData) {
+ let aborting = snippetData.shouldAbort;
+ let regExceptions = snippetData.regexExceptions;
+ let expSelectors = snippetData.exceptionSelectors;
+ let id = snippetData.rid;
+ if (typeof nodeOrDOMString == "string") {
+ let domString = nodeOrDOMString;
+ if (isExceptionText(domString, regExceptions))
+ return true;
+ if (debug())
+ logChange(domString, parent, property, snippetData);
+ if (aborting)
+ abort(id);
+ return debug();
+ }
+
+ let node = nodeOrDOMString;
+ switch ($(node).nodeType) {
+ case ELEMENT_NODE:
+ if (isExceptionNode(node, expSelectors))
+ return true;
+ if (aborting) {
+ if (debug())
+ logChange(node, parent, property, snippetData);
+ abort(id);
+ }
+ if (debug()) {
+ hideElement(node);
+ logChange(node, parent, property, snippetData);
+ return true;
+ }
+ return false;
+ case TEXT_NODE:
+ if (isExceptionText($(node).textContent, regExceptions))
+ return true;
+ if (debug())
+ logChange(node, parent, property, snippetData);
+ if (aborting)
+ abort(id);
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ function getAppendChildDescriptor(target, property, shouldValidate,
+ getSnippetData) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let origin = desc.get && call(desc.get, target) || desc.value;
+ if (!origin)
+ return;
+
+ return {
+ get() {
+ return function(...args) {
+ if (shouldValidate(this)) {
+ let snippetData = getSnippetData(this);
+ if (snippetData) {
+ let incomingNode = args[0];
+ if (!checkShouldInsert(incomingNode, this, property, snippetData))
+ return incomingNode;
+ }
+ }
+ return apply$2(origin, this, args);
+ };
+ }
+ };
+ }
+
+ function getAppendDescriptor(
+ target, property, shouldValidate, getSnippetData
+ ) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let origin = desc.get && call(desc.get, target) || desc.value;
+ if (!origin)
+ return;
+ return {
+ get() {
+ return function(...nodesOrDOMStrings) {
+ if (!shouldValidate(this))
+ return apply$2(origin, this, nodesOrDOMStrings);
+
+ let snippetData = getSnippetData(this);
+ if (!snippetData)
+ return apply$2(origin, this, nodesOrDOMStrings);
+
+ let accepted = checkMultiple(
+ nodesOrDOMStrings, this, property, snippetData
+ );
+ if (accepted.length > 0)
+ return apply$2(origin, this, accepted);
+ };
+ }
+ };
+ }
+
+ function getInsertAdjacentDescriptor(
+ target, property, shouldValidate, getSnippetData
+ ) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let origin = desc.get && call(desc.get, target) || desc.value;
+ if (!origin)
+ return;
+
+ return {
+ get() {
+ return function(...args) {
+ let [position, value] = args;
+ let isInsideTarget =
+ position === "afterbegin" || position === "beforeend";
+ if (shouldValidate(this, isInsideTarget)) {
+ let snippetData = getSnippetData(this, isInsideTarget);
+ if (snippetData) {
+ let parent = isInsideTarget ?
+ this :
+ $(this).parentNode;
+ let finalValue;
+ switch (property) {
+ case "insertAdjacentElement":
+ if (!checkShouldInsert(value, parent, property, snippetData))
+ return value;
+ break;
+
+ case "insertAdjacentHTML":
+ finalValue = checkHTML(value, parent, property, snippetData);
+ if (finalValue)
+ return call(origin, this, position, finalValue);
+
+ return;
+
+ case "insertAdjacentText":
+ if (!checkShouldInsert(value, parent, property, snippetData))
+ return;
+ break;
+ }
+ }
+ }
+ return apply$2(origin, this, args);
+ };
+ }
+ };
+ }
+
+ function getInnerHTMLDescriptor(
+ target, property, shouldValidate, getSnippetData
+ ) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let {set: prevSetter} = desc;
+ if (!prevSetter)
+ return;
+
+ return {
+ set(htmlText) {
+ if (!shouldValidate(this))
+ return call(prevSetter, this, htmlText);
+
+ let snippetData = getSnippetData(this);
+ if (!snippetData)
+ return call(prevSetter, this, htmlText);
+ let finalValue = checkHTML(htmlText, this, property, snippetData);
+ if (finalValue)
+ return call(prevSetter, this, finalValue);
+ }
+ };
+ }
+
+ function getTextContentDescriptor(
+ target, property, shouldValidate, getSnippetData
+ ) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let {set: prevSetter} = desc;
+ if (!prevSetter)
+ return;
+
+ return {
+ set(domString) {
+ if (!shouldValidate(this))
+ return call(prevSetter, this, domString);
+
+ let snippetData = getSnippetData(this);
+ if (!snippetData)
+ return call(prevSetter, this, domString);
+ if (checkShouldInsert(domString, this, property, snippetData))
+ return call(prevSetter, this, domString);
+ }
+ };
+ }
+ }
+
+ $(window);
+
+ function raceWinner(name, lose) {
+
+ return noop;
+ }
+
+ const {Map: Map$4, MutationObserver, Object: Object$4, Set, WeakSet: WeakSet$1} = $(window);
+
+ let ElementProto = Element.prototype;
+ let {attachShadow} = ElementProto;
+
+ let hiddenShadowRoots = new WeakSet$1();
+ let searches = new Map$4();
+ let observer = null;
+
+ function hideIfShadowContains(search, selector = "*") {
+
+ let key = `${search}\\${selector}`;
+ if (!searches.has(key)) {
+ searches.set(key, [toRegExp(search), selector, raceWinner()
+ ]);
+ }
+
+ const debugLog = getDebugger("hide-if-shadow-contain");
+
+ if (!observer) {
+ observer = new MutationObserver(records => {
+ let visited = new Set();
+ for (let {target} of $(records)) {
+
+ let parent = $(target).parentNode;
+ while (parent)
+ [target, parent] = [parent, $(target).parentNode];
+
+ if (hiddenShadowRoots.has(target))
+ continue;
+
+ if (visited.has(target))
+ continue;
+
+ visited.add(target);
+ for (let [re, selfOrParent, win] of searches.values()) {
+ if (re.test($(target).textContent)) {
+ let closest = $(target.host).closest(selfOrParent);
+ if (closest) {
+ win();
+
+ $(target).appendChild(
+ document.createElement("style")
+ ).textContent = ":host {display: none !important}";
+
+ hideElement(closest);
+
+ hiddenShadowRoots.add(target);
+ debugLog("Hiding: ", closest, " for params: ", ...arguments);
+ }
+ }
+ }
+ }
+ });
+
+ Object$4.defineProperty(ElementProto, "attachShadow", {
+
+ value: proxy(attachShadow, function() {
+
+ let root = apply$2(attachShadow, this, arguments);
+ debugLog("attachShadow is called for: ", root);
+
+ observer.observe(root, {
+ childList: true,
+ characterData: true,
+ subtree: true
+ });
+
+ return root;
+ })
+ });
+ }
+ }
+
+ const {Error: Error$4, JSON: JSON$2, Map: Map$3, Object: Object$3} = $(window);
+
+ let paths$1 = null;
+
+ function jsonOverride(rawOverridePaths, value,
+ rawNeedlePaths = "", filter = "") {
+ if (!rawOverridePaths)
+ throw new Error$4("[json-override snippet]: Missing paths to override.");
+
+ if (typeof value == "undefined")
+ throw new Error$4("[json-override snippet]: No value to override with.");
+
+ if (!paths$1) {
+ let debugLog = getDebugger("json-override");
+
+ let {parse} = JSON$2;
+ paths$1 = new Map$3();
+
+ Object$3.defineProperty(window.JSON, "parse", {
+ value: proxy(parse, function(str) {
+ let result = apply$2(parse, this, arguments);
+
+ for (let {prune, needle, filter: flt, value: val} of paths$1.values()) {
+ if (flt && !flt.test(str))
+ continue;
+
+ if ($(needle).some(path => !findOwner(result, path)))
+ return result;
+
+ for (let path of prune) {
+ let details = findOwner(result, path);
+ if (typeof details != "undefined") {
+ debugLog(`Found ${path} replaced it with ${val}`);
+ details[0][details[1]] = overrideValue(val);
+ }
+ }
+ }
+
+ return result;
+ })
+ });
+ debugLog("Wrapped JSON.parse for override");
+ }
+
+ paths$1.set(rawOverridePaths, {
+ prune: $(rawOverridePaths).split(/ +/),
+ needle: rawNeedlePaths.length ? $(rawNeedlePaths).split(/ +/) : [],
+ filter: filter ? toRegExp(filter) : null,
+ value
+ });
+ }
+
+ let {Error: Error$3, JSON: JSON$1, Map: Map$2, Object: Object$2} = $(window);
+
+ let paths = null;
+
+ function jsonPrune(rawPrunePaths, rawNeedlePaths = "") {
+ if (!rawPrunePaths)
+ throw new Error$3("Missing paths to prune");
+
+ if (!paths) {
+ let debugLog = getDebugger("json-prune");
+
+ let {parse} = JSON$1;
+ paths = new Map$2();
+
+ Object$2.defineProperty(window.JSON, "parse", {
+ value: proxy(parse, function() {
+ let result = apply$2(parse, this, arguments);
+
+ for (let {prune, needle} of paths.values()) {
+ if ($(needle).some(path => !findOwner(result, path)))
+ return result;
+
+ for (let path of prune) {
+ let details = findOwner(result, path);
+ if (typeof details != "undefined") {
+ debugLog(`Found ${path} and deleted`);
+ delete details[0][details[1]];
+ }
+ }
+ }
+
+ return result;
+ })
+ });
+ debugLog("Wrapped JSON.parse for prune");
+ }
+
+ paths.set(rawPrunePaths, {
+ prune: $(rawPrunePaths).split(/ +/),
+ needle: rawNeedlePaths.length ? $(rawNeedlePaths).split(/ +/) : []
+ });
+ }
+
+ let {Error: Error$2} = $(window);
+
+ function overridePropertyRead(property, value) {
+ if (!property) {
+ throw new Error$2("[override-property-read snippet]: " +
+ "No property to override.");
+ }
+ if (typeof value === "undefined") {
+ throw new Error$2("[override-property-read snippet]: " +
+ "No value to override with.");
+ }
+
+ let debugLog = getDebugger("override-property-read");
+
+ let cValue = overrideValue(value);
+
+ let newGetter = () => {
+ debugLog(`${property} override done.`);
+ return cValue;
+ };
+
+ debugLog(`Overriding ${property}.`);
+
+ wrapPropertyAccess(window, property, {get: newGetter, set() {}});
+ }
+
+ let {Error: Error$1, Map: Map$1, Object: Object$1, console: console$1} = $(window);
+
+ let {toString} = Function.prototype;
+ let EventTargetProto = EventTarget.prototype;
+ let {addEventListener} = EventTargetProto;
+
+ let events = null;
+
+ function preventListener(event, eventHandler, selector) {
+ if (!event)
+ throw new Error$1("[prevent-listener snippet]: No event type.");
+
+ if (!events) {
+ events = new Map$1();
+
+ let debugLog = getDebugger("[prevent]");
+
+ Object$1.defineProperty(EventTargetProto, "addEventListener", {
+ value: proxy(addEventListener, function(type, listener) {
+ for (let {evt, handlers, selectors} of events.values()) {
+
+ if (!evt.test(type))
+ continue;
+
+ let isElement = this instanceof Element;
+
+ for (let i = 0; i < handlers.length; i++) {
+ let handler = handlers[i];
+ let sel = selectors[i];
+
+ let handlerMatch = () => handler.test(
+ call(
+ toString,
+ typeof listener === "function" ?
+ listener : listener.handleEvent
+ )
+ );
+
+ if (
+ (handler && !handlerMatch()) ||
+ (sel && !(isElement && $(this).matches(sel)))
+ )
+ continue;
+
+ if (debug()) {
+ console$1.groupCollapsed("DEBUG [prevent] was successful");
+ debugLog(`type: ${type} matching ${evt}`);
+ debugLog("handler:", listener);
+ if (handler)
+ debugLog(`matching ${handler}`);
+ if (sel)
+ debugLog("on element: ", this, ` matching ${sel}`);
+ debugLog("was prevented from being added");
+ console$1.groupEnd();
+ }
+ return;
+ }
+ }
+ return apply$2(addEventListener, this, arguments);
+ })
+ });
+
+ debugLog("Wrapped addEventListener");
+ }
+
+ if (!events.has(event))
+ events.set(event, {evt: toRegExp(event), handlers: [], selectors: []});
+
+ let {handlers, selectors} = events.get(event);
+
+ handlers.push(eventHandler ? toRegExp(eventHandler) : null);
+ selectors.push(selector);
+ }
+
+ let {URL, fetch} = $(window);
+
+ let {delete: deleteParam, has: hasParam} = caller(URLSearchParams.prototype);
+
+ let parameters;
+
+ function stripFetchQueryParameter(name, urlPattern = null) {
+ const debugLog = getDebugger("strip-fetch-query-parameter");
+
+ if (!parameters) {
+ parameters = new Map();
+ window.fetch = proxy(fetch, (...args) => {
+ let [source] = args;
+ if (typeof source === "string") {
+ let url = new URL(source);
+ for (let [key, reg] of parameters) {
+ if (!reg || reg.test(source)) {
+ if (hasParam(url.searchParams, key)) {
+ debugLog(`${key} has been stripped from url ${source}`);
+ deleteParam(url.searchParams, key);
+ args[0] = url.href;
+ }
+ }
+ }
+ }
+ return apply$2(fetch, self, args);
+ });
+ }
+ parameters.set(name, urlPattern && toRegExp(urlPattern));
+ }
+
+ function trace(...args) {
+
+ apply$2(log, null, args);
+ }
+
+ const snippets = {
+ "abort-current-inline-script": abortCurrentInlineScript,
+ "abort-on-iframe-property-read": abortOnIframePropertyRead,
+ "abort-on-iframe-property-write": abortOnIframePropertyWrite,
+ "abort-on-property-read": abortOnPropertyRead,
+ "abort-on-property-write": abortOnPropertyWrite,
+ "cookie-remover": cookieRemover,
+ "debug": setDebug,
+ "freeze-element": freezeElement,
+ "hide-if-shadow-contains": hideIfShadowContains,
+ "json-override": jsonOverride,
+ "json-prune": jsonPrune,
+ "override-property-read": overridePropertyRead,
+ "prevent-listener": preventListener,
+ "strip-fetch-query-parameter": stripFetchQueryParameter,
+ "trace": trace
+ };
+ let context;
+ for (const [name, ...args] of filters) {
+ if (snippets.hasOwnProperty(name)) {
+ try { context = snippets[name].apply(context, args); }
+ catch (error) { console.error(error); }
+ }
+ }
+ context = void 0;
+};
+const graph = new Map([["abort-current-inline-script",null],["abort-on-iframe-property-read",null],["abort-on-iframe-property-write",null],["abort-on-property-read",null],["abort-on-property-write",null],["cookie-remover",null],["debug",null],["freeze-element",null],["hide-if-shadow-contains",null],["json-override",null],["json-prune",null],["override-property-read",null],["prevent-listener",null],["strip-fetch-query-parameter",null],["trace",null]]);
+callback.get = snippet => graph.get(snippet);
+callback.has = snippet => graph.has(snippet);
+
+ if (t.every(([name]) => !callback.has(name))) return;
+ const append = () => {
+ URL.revokeObjectURL(
+ Object.assign(
+ document.documentElement.appendChild(document.createElement("script")),
+ {async: false, src: URL.createObjectURL(new Blob([
+ "(" + callback + ")(..." + JSON.stringify([e, ...t]) + ")"
+ ]))}
+ ).src
+ );
+ };
+ try { append(); }
+ catch (_) {
+ document.addEventListener("readystatechange", append, {once:true});
+ }
+}
diff --git a/components/resources/adblocking/snippets/dist/isolated-first.jst b/components/resources/adblocking/snippets/dist/isolated-first.jst
new file mode 100755
--- /dev/null
+++ b/components/resources/adblocking/snippets/dist/isolated-first.jst
@@ -0,0 +1,65 @@
+(e, ...t) => {
+/*!
+ * snippets v0.7.0
+ * https://gitlab.com/eyeo/snippets/-/blob/d7c80795567d9f9c44a6ebaa789baceab85f31f9/dist/isolated-first.jst
+ *
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * @eyeo/snippets 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/snippets 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/snippets. If not, see <http://www.gnu.org/licenses/>.
+ */
+ ((environment, ...filters) => {
+const e=Proxy,{apply:t,bind:n,call:o}=Function,r=o.bind(t),i=o.bind(n),s=o.bind(o),a={get:(e,t)=>i(o,e[t])},c=t=>new e(t,a),l={get:(e,t)=>i(e[t],e)},u=t=>new e(t,l),{assign:d,defineProperties:h,freeze:f,getOwnPropertyDescriptor:p,getOwnPropertyDescriptors:w,getPrototypeOf:g}=u(Object),{hasOwnProperty:m}=c({}),{species:b}=Symbol,y={get(e,t){const n=e[t];class o extends n{}const r=w(n.prototype);delete r.constructor,f(h(o.prototype,r));const i=w(n);return delete i.length,delete i.prototype,i[b]={value:o},f(h(o,i))}},v=t=>new e(t,y),S="undefined"!=typeof environment?environment:{};"undefined"==typeof globalThis&&(window.globalThis=window);const{apply:k,ownKeys:E}=u(Reflect),M="world"in S,x=M&&"ISOLATED"===S.world,C=M&&"MAIN"===S.world,T="object"==typeof chrome&&!!chrome.runtime,W="object"==typeof browser&&!!browser.runtime,O=!C&&(x||T||W),P=e=>O?e:L(e,R(e)),{create:L,defineProperties:D,defineProperty:N,freeze:I,getOwnPropertyDescriptor:A,getOwnPropertyDescriptors:R}=u(Object),V=u(globalThis),$=O?globalThis:v(globalThis),{Map:H,RegExp:j,Set:_,WeakMap:F,WeakSet:q}=$,B=(e,t,n=null)=>{const o=E(t);for(const r of E(e)){if(o.includes(r))continue;const i=A(e,r);if(n&&"value"in i){const{value:e}=i;"function"==typeof e&&(i.value=n(e))}N(t,r,i)}},z=e=>{const t=$[e];class n extends t{}const{toString:o,valueOf:r}=t.prototype;D(n.prototype,{toString:{value:o},valueOf:{value:r}});const i=e.toLowerCase(),s=e=>function(){const t=k(e,this,arguments);return typeof t===i?new n(t):t};return B(t,n,s),B(t.prototype,n.prototype,s),n},X=I({frozen:new F,hidden:new q,iframePropertiesToAbort:{read:new _,write:new _},abortedIframes:new F}),J=new j("^[A-Z]");var U=new Proxy(new H([["chrome",O&&(T&&chrome||W&&browser)||void 0],["isExtensionContext",O],["variables",X],["console",P(console)],["document",globalThis.document],["performance",P(performance)],["JSON",P(JSON)],["Map",H],["Math",P(Math)],["Number",O?Number:z("Number")],["RegExp",j],["Set",_],["String",O?String:z("String")],["WeakMap",F],["WeakSet",q],["MouseEvent",MouseEvent]]),{get(e,t){if(e.has(t))return e.get(t);let n=globalThis[t];return"function"==typeof n&&(n=(J.test(t)?$:V)[t]),e.set(t,n),n},has:(e,t)=>e.has(t)});const G={WeakSet:WeakSet,WeakMap:WeakMap,WeakValue:class{has(){return!1}set(){}}},{apply:K}=Reflect;const{Map:Q,WeakMap:Y,WeakSet:Z,setTimeout:ee}=U;let te=!0,ne=e=>{e.clear(),te=!te};var oe=function(e){const{WeakSet:t,WeakMap:n,WeakValue:o}=this||G,r=new t,i=new n,s=new o;return function(t){if(r.has(t))return t;if(i.has(t))return i.get(t);if(s.has(t))return s.get(t);const n=K(e,this,arguments);return r.add(n),n!==t&&("object"==typeof t&&t?i:s).set(t,n),n}}.bind({WeakMap:Y,WeakSet:Z,WeakValue:class extends Q{set(e,t){return te&&(te=!te,ee(ne,0,this)),super.set(e,t)}}});const{concat:re,includes:ie,join:se,reduce:ae,unshift:ce}=c([]),le=v(globalThis),{Map:ue,WeakMap:de}=le,he=new ue,fe=t=>{const n=(e=>{const t=[];let n=e;for(;n;){if(he.has(n))ce(t,he.get(n));else{const e=w(n);he.set(n,e),ce(t,e)}n=g(n)}return ce(t,{}),r(d,null,t)})("function"==typeof t?t.prototype:t),o={get(e,t){if(t in n){const{value:o,get:r}=n[t];if(r)return s(r,e);if("function"==typeof o)return i(o,e)}return e[t]},set(e,t,o){if(t in n){const{set:r}=n[t];if(r)return s(r,e,o),!0}return e[t]=o,!0}};return t=>new e(t,o)},{isExtensionContext:pe,Array:we,Number:ge,String:me,Object:be}=U,{isArray:ye}=we,{getOwnPropertyDescriptor:ve,setPrototypeOf:Se}=be,{toString:ke}=be.prototype,{slice:Ee}=me.prototype,{get:Me}=ve(Node.prototype,"nodeType"),xe=pe?{}:{Attr:fe(Attr),CanvasRenderingContext2D:fe(CanvasRenderingContext2D),CSSStyleDeclaration:fe(CSSStyleDeclaration),Document:fe(Document),Element:fe(Element),HTMLCanvasElement:fe(HTMLCanvasElement),HTMLElement:fe(HTMLElement),HTMLImageElement:fe(HTMLImageElement),HTMLScriptElement:fe(HTMLScriptElement),MutationRecord:fe(MutationRecord),Node:fe(Node),ShadowRoot:fe(ShadowRoot),get CSS2Properties(){return xe.CSSStyleDeclaration}},Ce=(e,t)=>{if("Element"!==t&&t in xe)return xe[t](e);if(ye(e))return Se(e,we.prototype);const n=(e=>s(Ee,s(ke,e),8,-1))(e);if(n in xe)return xe[n](e)
+const snippets=fn;
+let context;
+for (const [name, ...args] of filters) {
+if (snippets.hasOwnProperty(name)) {
+try { context = snippets[name].apply(context, args); }
+catch (error) { console.error(error); }
+}
+}
+context = void 0;
+})(e, ...t);
+
+const callback = (environment, ...filters) => {
+const e=Proxy,{apply:t,bind:r,call:n}=Function,o=n.bind(t),i=n.bind(r),s=n.bind(n),a={get:(e,t)=>i(n,e[t])},l=t=>new e(t,a),c=(t,r)=>new e(t,{apply:(e,t,n)=>o(r,t,n)}),u={get:(e,t)=>i(e[t],e)},f=t=>new e(t,u),{assign:p,defineProperties:d,freeze:h,getOwnPropertyDescriptor:w,getOwnPropertyDescriptors:g,getPrototypeOf:y}=f(Object),{hasOwnProperty:m}=l({}),{species:b}=Symbol,v={get(e,t){const r=e[t];class n extends r{}const o=g(r.prototype);delete o.constructor,h(d(n.prototype,o));const i=g(r);return delete i.length,delete i.prototype,i[b]={value:n},h(d(n,i))}},E=t=>new e(t,v),S="undefined"!=typeof environment?environment:{};"undefined"==typeof globalThis&&(window.globalThis=window);const{apply:M,ownKeys:T}=f(Reflect),x="world"in S,O=x&&"ISOLATED"===S.world,P=x&&"MAIN"===S.world,j="object"==typeof chrome&&!!chrome.runtime,N="object"==typeof browser&&!!browser.runtime,L=!P&&(O||j||N),k=e=>L?e:C(e,H(e)),{create:C,defineProperties:A,defineProperty:W,freeze:$,getOwnPropertyDescriptor:D,getOwnPropertyDescriptors:H}=f(Object),z=f(globalThis),R=L?globalThis:E(globalThis),{Map:F,RegExp:I,Set:J,WeakMap:V,WeakSet:B}=R,U=(e,t,r=null)=>{const n=T(t);for(const o of T(e)){if(n.includes(o))continue;const i=D(e,o);if(r&&"value"in i){const{value:e}=i;"function"==typeof e&&(i.value=r(e))}W(t,o,i)}},_=e=>{const t=R[e];class r extends t{}const{toString:n,valueOf:o}=t.prototype;A(r.prototype,{toString:{value:n},valueOf:{value:o}});const i=e.toLowerCase(),s=e=>function(){const t=M(e,this,arguments);return typeof t===i?new r(t):t};return U(t,r,s),U(t.prototype,r.prototype,s),r},X=$({frozen:new V,hidden:new B,iframePropertiesToAbort:{read:new J,write:new J},abortedIframes:new V}),q=new I("^[A-Z]");var G=new Proxy(new F([["chrome",L&&(j&&chrome||N&&browser)||void 0],["isExtensionContext",L],["variables",X],["console",k(console)],["document",globalThis.document],["performance",k(performance)],["JSON",k(JSON)],["Map",F],["Math",k(Math)],["Number",L?Number:_("Number")],["RegExp",I],["Set",J],["String",L?String:_("String")],["WeakMap",V],["WeakSet",B],["MouseEvent",MouseEvent]]),{get(e,t){if(e.has(t))return e.get(t);let r=globalThis[t];return"function"==typeof r&&(r=(q.test(t)?R:z)[t]),e.set(t,r),r},has:(e,t)=>e.has(t)});const K={WeakSet:WeakSet,WeakMap:WeakMap,WeakValue:class{has(){return!1}set(){}}},{apply:Y}=Reflect;const{Map:Z,WeakMap:Q,WeakSet:ee,setTimeout:te}=G;let re=!0,ne=e=>{e.clear(),re=!re};var oe=function(e){const{WeakSet:t,WeakMap:r,WeakValue:n}=this||K,o=new t,i=new r,s=new n;return function(t){if(o.has(t))return t;if(i.has(t))return i.get(t);if(s.has(t))return s.get(t);const r=Y(e,this,arguments);return o.add(r),r!==t&&("object"==typeof t&&t?i:s).set(t,r),r}}.bind({WeakMap:Q,WeakSet:ee,WeakValue:class extends Z{set(e,t){return re&&(re=!re,te(ne,0,this)),super.set(e,t)}}});const{concat:ie,includes:se,join:ae,reduce:le,unshift:ce}=l([]),ue=E(globalThis),{Map:fe,WeakMap:pe}=ue,de=new fe,he=t=>{const r=(e=>{const t=[];let r=e;for(;r;){if(de.has(r))ce(t,de.get(r));else{const e=g(r);de.set(r,e),ce(t,e)}r=y(r)}return ce(t,{}),o(p,null,t)})("function"==typeof t?t.prototype:t),n={get(e,t){if(t in r){const{value:n,get:o}=r[t];if(o)return s(o,e);if("function"==typeof n)return i(n,e)}return e[t]},set(e,t,n){if(t in r){const{set:o}=r[t];if(o)return s(o,e,n),!0}return e[t]=n,!0}};return t=>new e(t,n)},{isExtensionContext:we,Array:ge,Number:ye,String:me,Object:be}=G,{isArray:ve}=ge,{getOwnPropertyDescriptor:Ee,setPrototypeOf:Se}=be,{toString:Me}=be.prototype,{slice:Te}=me.prototype,{get:xe}=Ee(Node.prototype,"nodeType"),Oe=we?{}:{Attr:he(Attr),CanvasRenderingContext2D:he(CanvasRenderingContext2D),CSSStyleDeclaration:he(CSSStyleDeclaration),Document:he(Document),Element:he(Element),HTMLCanvasElement:he(HTMLCanvasElement),HTMLElement:he(HTMLElement),HTMLImageElement:he(HTMLImageElement),HTMLScriptElement:he(HTMLScriptElement),MutationRecord:he(MutationRecord),Node:he(Node),ShadowRoot:he(ShadowRoot),get CSS2Properties(){return Oe.CSSStyleDeclaration}},Pe=(e,t)=>{if("Element"!==t&&t in Oe)return Oe[t](e);if(ve(e))return Se(e,ge.prototype);const r=(e=>s(T
+const snippets=Pr;
+let context;
+for (const [name, ...args] of filters) {
+if (snippets.hasOwnProperty(name)) {
+try { context = snippets[name].apply(context, args); }
+catch (error) { console.error(error); }
+}
+}
+context = void 0;
+};
+const graph = new Map([["abort-current-inline-script",null],["abort-on-iframe-property-read",null],["abort-on-iframe-property-write",null],["abort-on-property-read",null],["abort-on-property-write",null],["cookie-remover",null],["debug",null],["freeze-element",null],["hide-if-shadow-contains",null],["json-override",null],["json-prune",null],["override-property-read",null],["prevent-listener",null],["strip-fetch-query-parameter",null],["trace",null]]);
+callback.get = snippet => graph.get(snippet);
+callback.has = snippet => graph.has(snippet);
+
+ if (t.every(([name]) => !callback.has(name))) return;
+ const append = () => {
+ URL.revokeObjectURL(
+ Object.assign(
+ document.documentElement.appendChild(document.createElement("script")),
+ {async: false, src: URL.createObjectURL(new Blob([
+ "(" + callback + ")(..." + JSON.stringify([e, ...t]) + ")"
+ ]))}
+ ).src
+ );
+ };
+ try { append(); }
+ catch (_) {
+ document.addEventListener("readystatechange", append, {once:true});
+ }
+}
diff --git a/components/resources/adblocking/snippets/dist/isolated-first.source.jst b/components/resources/adblocking/snippets/dist/isolated-first.source.jst
new file mode 100755
--- /dev/null
+++ b/components/resources/adblocking/snippets/dist/isolated-first.source.jst
@@ -0,0 +1,3624 @@
+(e, ...t) => {
+/*!
+ * snippets v0.7.0 (https://gitlab.com/eyeo/anti-cv/snippets/-/tree/5f54b58f039a604088d65991f2829a9a081832f6/dist)
+ * https://gitlab.com/eyeo/snippets/-/commit/d7c80795567d9f9c44a6ebaa789baceab85f31f9
+ *
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * @eyeo/snippets 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/snippets 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/snippets. If not, see <http://www.gnu.org/licenses/>.
+ */
+ ((environment, ...filters) => {
+ /*!
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * @eyeo/snippets 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/snippets 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/snippets. If not, see <http://www.gnu.org/licenses/>.
+ */
+ const $$1 = Proxy;
+
+ const {apply: a, bind: b, call: c} = Function;
+ const apply$2 = c.bind(a);
+ const bind = c.bind(b);
+ const call = c.bind(c);
+
+ const callerHandler = {
+ get(target, name) {
+ return bind(c, target[name]);
+ }
+ };
+
+ const caller = target => new $$1(target, callerHandler);
+
+ const handler$2 = {
+ get(target, name) {
+ return bind(target[name], target);
+ }
+ };
+
+ const bound = target => new $$1(target, handler$2);
+
+ const {
+ assign: assign$1,
+ defineProperties: defineProperties$1,
+ freeze: freeze$1,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$2,
+ getOwnPropertyDescriptors: getOwnPropertyDescriptors$1,
+ getPrototypeOf
+ } = bound(Object);
+
+ const {hasOwnProperty} = caller({});
+
+ const {species} = Symbol;
+
+ const handler$1 = {
+ get(target, name) {
+ const Native = target[name];
+ class Secure extends Native {}
+
+ const proto = getOwnPropertyDescriptors$1(Native.prototype);
+ delete proto.constructor;
+ freeze$1(defineProperties$1(Secure.prototype, proto));
+
+ const statics = getOwnPropertyDescriptors$1(Native);
+ delete statics.length;
+ delete statics.prototype;
+ statics[species] = {value: Secure};
+ return freeze$1(defineProperties$1(Secure, statics));
+ }
+ };
+
+ const secure = target => new $$1(target, handler$1);
+
+ const libEnvironment = typeof environment !== "undefined" ? environment :
+ {};
+
+ if (typeof globalThis === "undefined")
+ window.globalThis = window;
+
+ const {apply: apply$1, ownKeys} = bound(Reflect);
+
+ const worldEnvDefined = "world" in libEnvironment;
+ const isIsolatedWorld = worldEnvDefined && libEnvironment.world === "ISOLATED";
+ const isMainWorld = worldEnvDefined && libEnvironment.world === "MAIN";
+ const isChrome = typeof chrome === "object" && !!chrome.runtime;
+ const isOtherThanChrome = typeof browser === "object" && !!browser.runtime;
+ const isExtensionContext$2 = !isMainWorld &&
+ (isIsolatedWorld || isChrome || isOtherThanChrome);
+ const copyIfExtension = value => isExtensionContext$2 ?
+ value :
+ create(value, getOwnPropertyDescriptors(value));
+
+ const {
+ create,
+ defineProperties,
+ defineProperty,
+ freeze,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$1,
+ getOwnPropertyDescriptors
+ } = bound(Object);
+
+ const invokes = bound(globalThis);
+ const classes = isExtensionContext$2 ? globalThis : secure(globalThis);
+ const {Map: Map$5, RegExp: RegExp$1, Set, WeakMap: WeakMap$3, WeakSet: WeakSet$b} = classes;
+
+ const augment = (source, target, method = null) => {
+ const known = ownKeys(target);
+ for (const key of ownKeys(source)) {
+ if (known.includes(key))
+ continue;
+
+ const descriptor = getOwnPropertyDescriptor$1(source, key);
+ if (method && "value" in descriptor) {
+ const {value} = descriptor;
+ if (typeof value === "function")
+ descriptor.value = method(value);
+ }
+ defineProperty(target, key, descriptor);
+ }
+ };
+
+ const primitive = name => {
+ const Super = classes[name];
+ class Class extends Super {}
+ const {toString, valueOf} = Super.prototype;
+ defineProperties(Class.prototype, {
+ toString: {value: toString},
+ valueOf: {value: valueOf}
+ });
+ const type = name.toLowerCase();
+ const method = callback => function() {
+ const result = apply$1(callback, this, arguments);
+ return typeof result === type ? new Class(result) : result;
+ };
+ augment(Super, Class, method);
+ augment(Super.prototype, Class.prototype, method);
+ return Class;
+ };
+
+ const variables$1 = freeze({
+ frozen: new WeakMap$3(),
+ hidden: new WeakSet$b(),
+ iframePropertiesToAbort: {
+ read: new Set(),
+ write: new Set()
+ },
+ abortedIframes: new WeakMap$3()
+ });
+
+ const startsCapitalized = new RegExp$1("^[A-Z]");
+
+ var env = new Proxy(new Map$5([
+
+ ["chrome", (
+ isExtensionContext$2 && (
+ (isChrome && chrome) ||
+ (isOtherThanChrome && browser)
+ )
+ ) || void 0],
+ ["isExtensionContext", isExtensionContext$2],
+ ["variables", variables$1],
+
+ ["console", copyIfExtension(console)],
+ ["document", globalThis.document],
+ ["performance", copyIfExtension(performance)],
+ ["JSON", copyIfExtension(JSON)],
+ ["Map", Map$5],
+ ["Math", copyIfExtension(Math)],
+ ["Number", isExtensionContext$2 ? Number : primitive("Number")],
+ ["RegExp", RegExp$1],
+ ["Set", Set],
+ ["String", isExtensionContext$2 ? String : primitive("String")],
+ ["WeakMap", WeakMap$3],
+ ["WeakSet", WeakSet$b],
+
+ ["MouseEvent", MouseEvent]
+ ]), {
+ get(map, key) {
+ if (map.has(key))
+ return map.get(key);
+
+ let value = globalThis[key];
+ if (typeof value === "function")
+ value = (startsCapitalized.test(key) ? classes : invokes)[key];
+
+ map.set(key, value);
+ return value;
+ },
+ has(map, key) {
+ return map.has(key);
+ }
+ });
+
+ class WeakValue {
+ has() { return false; }
+ set() {}
+ }
+
+ const helpers = {WeakSet, WeakMap, WeakValue};
+ const {apply} = Reflect;
+
+ function transformOnce (callback) { const {WeakSet, WeakMap, WeakValue} = (this || helpers);
+ const ws = new WeakSet;
+ const wm = new WeakMap;
+ const wv = new WeakValue;
+ return function (any) {
+ if (ws.has(any))
+ return any;
+
+ if (wm.has(any))
+ return wm.get(any);
+
+ if (wv.has(any))
+ return wv.get(any);
+
+ const value = apply(callback, this, arguments);
+ ws.add(value);
+ if (value !== any)
+ (typeof any === 'object' && any ? wm : wv).set(any, value);
+ return value;
+ };
+ }
+
+ const {Map: Map$4, WeakMap: WeakMap$2, WeakSet: WeakSet$a, setTimeout: setTimeout$3} = env;
+
+ let cleanup = true;
+ let cleanUpCallback = map => {
+ map.clear();
+ cleanup = !cleanup;
+ };
+
+ var transformer = transformOnce.bind({
+ WeakMap: WeakMap$2,
+ WeakSet: WeakSet$a,
+
+ WeakValue: class extends Map$4 {
+ set(key, value) {
+ if (cleanup) {
+ cleanup = !cleanup;
+ setTimeout$3(cleanUpCallback, 0, this);
+ }
+ return super.set(key, value);
+ }
+ }
+ });
+
+ const {concat, includes, join, reduce, unshift} = caller([]);
+
+ const globals = secure(globalThis);
+
+ const {
+ Map: Map$3,
+ WeakMap: WeakMap$1
+ } = globals;
+
+ const map = new Map$3;
+ const descriptors = target => {
+ const chain = [];
+ let current = target;
+ while (current) {
+ if (map.has(current))
+ unshift(chain, map.get(current));
+ else {
+ const descriptors = getOwnPropertyDescriptors$1(current);
+ map.set(current, descriptors);
+ unshift(chain, descriptors);
+ }
+ current = getPrototypeOf(current);
+ }
+ unshift(chain, {});
+ return apply$2(assign$1, null, chain);
+ };
+
+ const chain = source => {
+ const target = typeof source === 'function' ? source.prototype : source;
+ const chained = descriptors(target);
+ const handler = {
+ get(target, key) {
+ if (key in chained) {
+ const {value, get} = chained[key];
+ if (get)
+ return call(get, target);
+ if (typeof value === 'function')
+ return bind(value, target);
+ }
+ return target[key];
+ },
+ set(target, key, value) {
+ if (key in chained) {
+ const {set} = chained[key];
+ if (set) {
+ call(set, target, value);
+ return true;
+ }
+ }
+ target[key] = value;
+ return true;
+ }
+ };
+ return target => new $$1(target, handler);
+ };
+
+ const {
+ isExtensionContext: isExtensionContext$1,
+ Array: Array$2,
+ Number: Number$1,
+ String: String$1,
+ Object: Object$2
+ } = env;
+
+ const {isArray} = Array$2;
+ const {getOwnPropertyDescriptor, setPrototypeOf: setPrototypeOf$1} = Object$2;
+
+ const {toString} = Object$2.prototype;
+ const {slice} = String$1.prototype;
+ const getBrand = value => call(slice, call(toString, value), 8, -1);
+
+ const {get: nodeType} = getOwnPropertyDescriptor(Node.prototype, "nodeType");
+
+ const chained = isExtensionContext$1 ? {} : {
+ Attr: chain(Attr),
+ CanvasRenderingContext2D: chain(CanvasRenderingContext2D),
+ CSSStyleDeclaration: chain(CSSStyleDeclaration),
+ Document: chain(Document),
+ Element: chain(Element),
+ HTMLCanvasElement: chain(HTMLCanvasElement),
+ HTMLElement: chain(HTMLElement),
+ HTMLImageElement: chain(HTMLImageElement),
+ HTMLScriptElement: chain(HTMLScriptElement),
+ MutationRecord: chain(MutationRecord),
+ Node: chain(Node),
+ ShadowRoot: chain(ShadowRoot),
+
+ get CSS2Properties() {
+ return chained.CSSStyleDeclaration;
+ }
+ };
+
+ const upgrade = (value, hint) => {
+ if (hint !== "Element" && hint in chained)
+ return chained[hint](value);
+
+ if (isArray(value))
+ return setPrototypeOf$1(value, Array$2.prototype);
+
+ const brand = getBrand(value);
+ if (brand in chained)
+ return chained[brand](value);
+
+ if (brand in env)
+ return setPrototypeOf$1(value, env[brand].prototype);
+
+ if ("nodeType" in value) {
+ switch (call(nodeType, value)) {
+ case 1:
+ if (!(hint in chained))
+ throw new Error("unknown hint " + hint);
+ return chained[hint](value);
+ case 2:
+ return chained.Attr(value);
+ case 3:
+ return chained.Node(value);
+ case 9:
+ return chained.Document(value);
+ }
+ }
+
+ throw new Error("unknown brand " + brand);
+ };
+
+ var $ = isExtensionContext$1 ?
+ value => (value === window || value === globalThis ? env : value) :
+ transformer((value, hint = "Element") => {
+ if (value === window || value === globalThis)
+ return env;
+
+ switch (typeof value) {
+ case "object":
+ return value && upgrade(value, hint);
+
+ case "string":
+ return new String$1(value);
+
+ case "number":
+ return new Number$1(value);
+
+ default:
+ throw new Error("unsupported value");
+ }
+ });
+
+ let {
+ document: document$1,
+ getComputedStyle: getComputedStyle$5,
+ isExtensionContext,
+ variables,
+ Array: Array$1,
+ MutationObserver: MutationObserver$9,
+ Object: Object$1,
+ XPathEvaluator,
+ XPathExpression,
+ XPathResult
+ } = $(window);
+
+ let {querySelectorAll} = document$1;
+ let $$ = querySelectorAll && bind(querySelectorAll, document$1);
+
+ const {assign, setPrototypeOf} = Object$1;
+
+ class $XPathExpression extends XPathExpression {
+ evaluate(...args) {
+ return setPrototypeOf(
+ apply$2(super.evaluate, this, args),
+ XPathResult.prototype
+ );
+ }
+ }
+
+ class $XPathEvaluator extends XPathEvaluator {
+ createExpression(...args) {
+ return setPrototypeOf(
+ apply$2(super.createExpression, this, args),
+ $XPathExpression.prototype
+ );
+ }
+ }
+
+ function hideElement(element) {
+ if (variables.hidden.has(element))
+ return;
+
+ notifyElementHidden(element);
+
+ variables.hidden.add(element);
+
+ let {style} = $(element);
+ let $style = $(style, "CSSStyleDeclaration");
+ let properties = $([]);
+ let {debugCSSProperties} = libEnvironment;
+
+ for (let [key, value] of (debugCSSProperties || [["display", "none"]])) {
+ $style.setProperty(key, value, "important");
+ properties.push([key, $style.getPropertyValue(key)]);
+ }
+
+ new MutationObserver$9(() => {
+ for (let [key, value] of properties) {
+ let propertyValue = $style.getPropertyValue(key);
+ let propertyPriority = $style.getPropertyPriority(key);
+ if (propertyValue != value || propertyPriority != "important")
+ $style.setProperty(key, value, "important");
+ }
+ }).observe(element, {attributes: true,
+ attributeFilter: ["style"]});
+ }
+
+ function notifyElementHidden(element) {
+ if (isExtensionContext && typeof checkElement === "function")
+ checkElement(element);
+ }
+
+ function initQueryAndApply(selector) {
+ let $selector = selector;
+ if ($selector.startsWith("xpath(") &&
+ $selector.endsWith(")")) {
+ let xpathQuery = $selector.slice(6, -1);
+ let evaluator = new $XPathEvaluator();
+ let expression = evaluator.createExpression(xpathQuery, null);
+
+ let flag = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
+
+ return cb => {
+ if (!cb)
+ return;
+ let result = expression.evaluate(document$1, flag, null);
+ let {snapshotLength} = result;
+ for (let i = 0; i < snapshotLength; i++)
+ cb(result.snapshotItem(i));
+ };
+ }
+ return cb => $$(selector).forEach(cb);
+ }
+
+ function initQueryAll(selector) {
+ let $selector = selector;
+ if ($selector.startsWith("xpath(") &&
+ $selector.endsWith(")")) {
+ let queryAndApply = initQueryAndApply(selector);
+ return () => {
+ let elements = $([]);
+ queryAndApply(e => elements.push(e));
+ return elements;
+ };
+ }
+ return () => Array$1.from($$(selector));
+ }
+
+ function hideIfMatches(match, selector, searchSelector, onHideCallback) {
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ let won;
+ const callback = () => {
+ for (const element of $$(searchSelector)) {
+ const closest = $(element).closest(selector);
+ if (closest && match(element, closest)) {
+ won();
+ hideElement(closest);
+ if (typeof onHideCallback === "function")
+ onHideCallback(closest);
+ }
+ }
+ };
+ return assign(
+ new MutationObserver$9(callback),
+ {
+ race(win) {
+ won = win;
+ this.observe(document$1, {childList: true,
+ characterData: true,
+ subtree: true});
+ callback();
+ }
+ }
+ );
+ }
+
+ function isVisible(element, style, closest) {
+ let $style = $(style, "CSSStyleDeclaration");
+ if ($style.getPropertyValue("display") == "none")
+ return false;
+
+ let visibility = $style.getPropertyValue("visibility");
+ if (visibility == "hidden" || visibility == "collapse")
+ return false;
+
+ if (!closest || element == closest)
+ return true;
+
+ let parent = $(element).parentElement;
+ if (!parent)
+ return true;
+
+ return isVisible(parent, getComputedStyle$5(parent), closest);
+ }
+
+ function getComputedCSSText(element) {
+ let style = getComputedStyle$5(element);
+ let {cssText} = style;
+
+ if (cssText)
+ return cssText;
+
+ for (let property of style)
+ cssText += `${property}: ${style[property]}; `;
+
+ return $(cssText).trim();
+ }
+
+ let {Math: Math$2, RegExp} = $(window);
+
+ function regexEscape(string) {
+ return $(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
+ }
+
+ function toRegExp(pattern) {
+ let {length} = pattern;
+
+ if (length > 1 && pattern[0] === "/") {
+ let isCaseSensitive = pattern[length - 1] === "/";
+
+ if (isCaseSensitive || (length > 2 && $(pattern).endsWith("/i"))) {
+ let args = [$(pattern).slice(1, isCaseSensitive ? -1 : -2)];
+ if (!isCaseSensitive)
+ args.push("i");
+
+ return new RegExp(...args);
+ }
+ }
+
+ return new RegExp(regexEscape(pattern));
+ }
+
+ let debugging = false;
+
+ function debug() {
+ return debugging;
+ }
+
+ function setDebug() {
+ debugging = true;
+ }
+
+ const {console: console$1} = $(window);
+
+ const noop = () => {};
+
+ function log(...args) {
+ if (debug())
+ $(args).unshift("%c DEBUG", "font-weight: bold");
+
+ console$1.log(...args);
+ }
+
+ function getDebugger(name) {
+ return bind(debug() ? log : noop, null, name);
+ }
+
+ let {Array, Error: Error$3, Map: Map$2, parseInt: parseInt$2} = $(window);
+
+ let stack = null;
+ let won = null;
+
+ function race(action, winners = "1") {
+ switch (action) {
+ case "start":
+ stack = {
+ winners: parseInt$2(winners, 10) || 1,
+ participants: new Map$2()
+ };
+ won = new Array();
+ break;
+ case "end":
+ case "finish":
+ case "stop":
+ stack = null;
+ for (let win of won)
+ win();
+ won = null;
+ break;
+ default:
+ throw new Error$3(`Invalid action: ${action}`);
+ }
+ }
+
+ function raceWinner(name, lose) {
+
+ if (stack === null)
+ return noop;
+
+ let current = stack;
+ let {participants} = current;
+ participants.set(win, lose);
+
+ return win;
+
+ function win() {
+
+ if (current.winners < 1)
+ return;
+
+ let debugLog = getDebugger("race");
+ debugLog(`${name} won the race`);
+
+ if (current === stack) {
+ won.push(win);
+ }
+ else {
+ participants.delete(win);
+ if (--current.winners < 1) {
+ for (let looser of participants.values())
+ looser();
+
+ participants.clear();
+ }
+ }
+ }
+ }
+
+ function hideIfContains(search, selector = "*", searchSelector = null) {
+ const debugLog = getDebugger("hide-if-contains");
+ const onHideCallback = node => {
+ debugLog("Matched: ", node, " for selector: ", selector, searchSelector);
+ };
+ let re = toRegExp(search);
+
+ const mo = hideIfMatches(element => re.test($(element).textContent),
+ selector,
+ searchSelector,
+ onHideCallback);
+ mo.race(raceWinner(
+ "hide-if-contains",
+ () => {
+ mo.disconnect();
+ }
+ ));
+ }
+
+ const handler = {
+ get(target, name) {
+ const context = target;
+ while (!hasOwnProperty(target, name))
+ target = getPrototypeOf(target);
+ const {get, set} = getOwnPropertyDescriptor$2(target, name);
+ return function () {
+ return arguments.length ?
+ apply$2(set, context, arguments) :
+ call(get, context);
+ };
+ }
+ };
+
+ const accessor = target => new $$1(target, handler);
+
+ $(window);
+
+ accessor(window);
+
+ $(/^\d+$/);
+
+ function getPromiseFromEvent(item, event) {
+ return new Promise(
+ resolve => {
+ const listener = () => {
+ item.removeEventListener(event, listener);
+ resolve();
+ };
+ item.addEventListener(event, listener);
+ }
+ );
+ }
+
+ function waitUntilEvent(
+ debugLog,
+ mainLogic,
+ waitUntil) {
+ if (waitUntil) {
+
+ if (waitUntil === "load") {
+ debugLog("Waiting until window.load");
+
+ window.onload = () => {
+ debugLog("Window.load fired.");
+ mainLogic();
+ };
+ }
+
+ else if (waitUntil === "loading" ||
+ waitUntil === "interactive" ||
+ waitUntil === "complete") {
+ debugLog("Waiting document state until :", waitUntil);
+
+ document.onreadystatechange = () => {
+ debugLog("Document state changed:", document.readyState);
+ if (document.readyState === waitUntil)
+ mainLogic();
+ };
+ }
+
+ else {
+ debugLog("Waiting until ", waitUntil, " event is triggered on document");
+ getPromiseFromEvent(document, waitUntil).then(() => {
+ debugLog(waitUntil, " is triggered on document, starting the snippet");
+ mainLogic();
+ }).catch(err => {
+ debugLog("There was an error while waiting for the event.", err);
+ });
+ }
+ }
+ else {
+
+ mainLogic();
+ }
+ }
+
+ let {MutationObserver: MutationObserver$8, WeakSet: WeakSet$9, getComputedStyle: getComputedStyle$4} = $(window);
+
+ function hideIfContainsAndMatchesStyle(search,
+ selector = "*",
+ searchSelector = null,
+ style = null,
+ searchStyle = null,
+ waitUntil,
+ windowWidthMin = null,
+ windowWidthMax = null
+ ) {
+ const debugLog = getDebugger("hide-if-contains-and-matches-style");
+ const hiddenMap = new WeakSet$9();
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ const searchRegExp = toRegExp(search);
+
+ const styleRegExp = style ? toRegExp(style) : null;
+ const searchStyleRegExp = searchStyle ? toRegExp(searchStyle) : null;
+ const mainLogic = () => {
+ const callback = () => {
+ if ((windowWidthMin && window.innerWidth < windowWidthMin) ||
+ (windowWidthMax && window.innerWidth > windowWidthMax)
+ )
+ return;
+ for (const element of $$(searchSelector)) {
+ if (hiddenMap.has(element))
+ continue;
+ if (searchRegExp.test($(element).textContent)) {
+ if (!searchStyleRegExp ||
+ searchStyleRegExp.test(getComputedCSSText(element))) {
+ const closest = $(element).closest(selector);
+ if (!closest)
+ continue;
+ if (!styleRegExp || styleRegExp.test(getComputedCSSText(closest))) {
+ win();
+ hideElement(closest);
+ hiddenMap.add(element);
+ debugLog("Matched: ",
+ closest,
+ "which contains: ",
+ element,
+ " for params: ",
+ ...arguments);
+ }
+ else {
+ debugLog(
+ "In this element the searchStyle matched but style didn't:\n",
+ closest,
+ getComputedStyle$4(closest),
+ ...arguments);
+ }
+ }
+ else {
+ debugLog(
+ "In this element the searchStyle didn't match:\n",
+ element,
+ getComputedStyle$4(element),
+ ...arguments);
+ }
+ }
+ }
+ };
+
+ const mo = new MutationObserver$8(callback);
+ const win = raceWinner(
+ "hide-if-contains-and-matches-style",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {childList: true, characterData: true, subtree: true});
+ callback();
+ };
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
+ }
+
+ let {
+ clearTimeout,
+ fetch,
+ getComputedStyle: getComputedStyle$3,
+ setTimeout: setTimeout$2,
+ Map: Map$1,
+ MutationObserver: MutationObserver$7,
+ Uint8Array
+ } = $(window);
+
+ function hideIfContainsImage(search, selector, searchSelector) {
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ let searchRegExp = toRegExp(search);
+
+ const debugLog = getDebugger("hide-if-contains-image");
+
+ let callback = () => {
+ for (let element of $$(searchSelector)) {
+ let style = getComputedStyle$3(element);
+ let match = $(style["background-image"]).match(/^url\("(.*)"\)$/);
+ if (match) {
+ fetchContent(match[1]).then(content => {
+ if (searchRegExp.test(uint8ArrayToHex(new Uint8Array(content)))) {
+ let closest = $(element).closest(selector);
+ if (closest) {
+ win();
+ hideElement(closest);
+ debugLog("Matched: ", closest, " for:", ...arguments);
+ }
+ }
+ });
+ }
+ }
+ };
+
+ let mo = new MutationObserver$7(callback);
+ let win = raceWinner(
+ "hide-if-contains-image",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {childList: true, subtree: true});
+ callback();
+ }
+
+ let fetchContentMap = new Map$1();
+
+ function fetchContent(url, {as = "arrayBuffer", cleanup = 60000} = {}) {
+
+ let uid = as + ":" + url;
+ let details = fetchContentMap.get(uid) || {
+ remove: () => fetchContentMap.delete(uid),
+ result: null,
+ timer: 0
+ };
+ clearTimeout(details.timer);
+ details.timer = setTimeout$2(details.remove, cleanup);
+ if (!details.result) {
+ details.result = fetch(url).then(res => res[as]()).catch(details.remove);
+ fetchContentMap.set(uid, details);
+ }
+ return details.result;
+ }
+
+ function toHex(number, length = 2) {
+ let hex = $(number).toString(16);
+
+ if (hex.length < length)
+ hex = $("0").repeat(length - hex.length) + hex;
+
+ return hex;
+ }
+
+ function uint8ArrayToHex(uint8Array) {
+ return uint8Array.reduce((hex, byte) => hex + toHex(byte), "");
+ }
+
+ const {parseFloat: parseFloat$1, Math: Math$1, MutationObserver: MutationObserver$6, WeakSet: WeakSet$8} = $(window);
+ const {min} = Math$1;
+
+ const ld = (a, b) => {
+ const len1 = a.length + 1;
+ const len2 = b.length + 1;
+ const d = [[0]];
+ let i = 0;
+ let I = 0;
+
+ while (++i < len2)
+ d[0][i] = i;
+
+ i = 0;
+ while (++i < len1) {
+ const c = a[I];
+ let j = 0;
+ let J = 0;
+ d[i] = [i];
+ while (++j < len2) {
+ d[i][j] = min(d[I][j] + 1, d[i][J] + 1, d[I][J] + (c != b[J]));
+ ++J;
+ }
+ ++I;
+ }
+ return d[len1 - 1][len2 - 1];
+ };
+
+ function hideIfContainsSimilarText(
+ search, selector,
+ searchSelector = null,
+ ignoreChars = 0,
+ maxSearches = 0
+ ) {
+ const visitedNodes = new WeakSet$8();
+ const debugLog = getDebugger("hide-if-contains-similar-text");
+ const $search = $(search);
+ const {length} = $search;
+ const chars = length + parseFloat$1(ignoreChars) || 0;
+ const find = $([...$search]).sort();
+ const guard = parseFloat$1(maxSearches) || Infinity;
+
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ debugLog("Looking for similar text: " + $search);
+
+ const callback = () => {
+ for (const element of $$(searchSelector)) {
+ if (visitedNodes.has(element))
+ continue;
+
+ visitedNodes.add(element);
+ const {innerText} = $(element);
+ const loop = min(guard, innerText.length - chars + 1);
+ for (let i = 0; i < loop; i++) {
+ const str = $(innerText).substr(i, chars);
+ const distance = ld(find, $([...str]).sort()) - ignoreChars;
+ if (distance <= 0) {
+ const closest = $(element).closest(selector);
+ debugLog("Found similar text: " + $search, closest);
+ if (closest) {
+ win();
+ hideElement(closest);
+ break;
+ }
+ }
+ }
+ }
+ };
+
+ let mo = new MutationObserver$6(callback);
+ let win = raceWinner(
+ "hide-if-contains-similar-text",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {childList: true, characterData: true, subtree: true});
+ callback();
+ }
+
+ let {getComputedStyle: getComputedStyle$2, Map, WeakSet: WeakSet$7, parseFloat} = $(window);
+
+ const {ELEMENT_NODE: ELEMENT_NODE$2, TEXT_NODE} = Node;
+
+ function hideIfContainsVisibleText(search, selector,
+ searchSelector = null,
+ ...attributes) {
+ let entries = $([]);
+ const optionalParameters = new Map([
+ ["-snippet-box-margin", "2"],
+ ["-disable-bg-color-check", "false"],
+ ["-check-is-contained", "false"]
+ ]);
+
+ for (let attr of attributes) {
+ attr = $(attr);
+ let markerIndex = attr.indexOf(":");
+ if (markerIndex < 0)
+ continue;
+
+ let key = attr.slice(0, markerIndex).trim().toString();
+ let value = attr.slice(markerIndex + 1).trim().toString();
+
+ if (key && value) {
+ if (optionalParameters.has(key))
+ optionalParameters.set(key, value);
+ else
+ entries.push([key, value]);
+ }
+ }
+
+ let defaultEntries = $([
+ ["opacity", "0"],
+ ["font-size", "0px"],
+
+ ["color", "rgba(0, 0, 0, 0)"]
+ ]);
+
+ let attributesMap = new Map(defaultEntries.concat(entries));
+
+ function isTextVisible(element, style, {bgColorCheck = true} = {}) {
+ if (!style)
+ style = getComputedStyle$2(element);
+
+ style = $(style);
+
+ for (const [key, value] of attributesMap) {
+ let valueAsRegex = toRegExp(value);
+ if (valueAsRegex.test(style.getPropertyValue(key)))
+ return false;
+ }
+
+ let color = style.getPropertyValue("color");
+ if (bgColorCheck && style.getPropertyValue("background-color") == color)
+ return false;
+
+ return true;
+ }
+
+ function getPseudoContent(element, pseudo, {bgColorCheck = true} = {}) {
+ let style = getComputedStyle$2(element, pseudo);
+ if (!isVisible(element, style) ||
+ !isTextVisible(element, style, {bgColorCheck}))
+ return "";
+
+ let {content} = $(style);
+ if (content && content !== "none") {
+ let strings = $([]);
+
+ content = $(content).trim().replace(
+ /(["'])(?:(?=(\\?))\2.)*?\1/g,
+ value => `\x01${strings.push($(value).slice(1, -1)) - 1}`
+ );
+
+ content = content.replace(
+ /\s*attr\(\s*([^\s,)]+)[^)]*?\)\s*/g,
+ (_, name) => $(element).getAttribute(name) || ""
+ );
+
+ return content.replace(
+ /\x01(\d+)/g,
+ (_, index) => strings[index]);
+ }
+ return "";
+ }
+
+ function isContained(childNode, parentNode, {boxMargin = 2} = {}) {
+ const child = $(childNode).getBoundingClientRect();
+ const parent = $(parentNode).getBoundingClientRect();
+ const stretchedParent = {
+ left: parent.left - boxMargin,
+ right: parent.right + boxMargin,
+ top: parent.top - boxMargin,
+ bottom: parent.bottom + boxMargin
+ };
+ return (
+ (stretchedParent.left <= child.left &&
+ child.left <= stretchedParent.right &&
+ stretchedParent.top <= child.top &&
+ child.top <= stretchedParent.bottom) &&
+ (stretchedParent.top <= child.bottom &&
+ child.bottom <= stretchedParent.bottom &&
+ stretchedParent.left <= child.right &&
+ child.right <= stretchedParent.right)
+ );
+ }
+
+ function getVisibleContent(element,
+ closest,
+ style,
+ parentOverflowNode,
+ originalElement,
+ {
+ boxMargin = 2,
+ bgColorCheck,
+ checkIsContained
+ } = {}) {
+ let checkClosest = !style;
+ if (checkClosest)
+ style = getComputedStyle$2(element);
+
+ if (!isVisible(element, style, checkClosest && closest))
+ return "";
+
+ if (!parentOverflowNode &&
+ (
+ $(style).getPropertyValue("overflow-x") === "hidden" ||
+ $(style).getPropertyValue("overflow-y") === "hidden"
+ )
+ )
+ parentOverflowNode = element;
+
+ let text = getPseudoContent(element, ":before", {bgColorCheck});
+ for (let node of $(element).childNodes) {
+ switch ($(node).nodeType) {
+ case ELEMENT_NODE$2:
+ text += getVisibleContent(node,
+ element,
+ getComputedStyle$2(node),
+ parentOverflowNode,
+ originalElement,
+ {
+ boxMargin,
+ bgColorCheck,
+ checkIsContained
+ }
+ );
+ break;
+ case TEXT_NODE:
+
+ if (parentOverflowNode) {
+ if (isContained(element, parentOverflowNode, {boxMargin}) &&
+ isTextVisible(element, style, {bgColorCheck}))
+ text += $(node).nodeValue;
+ }
+ else if (isTextVisible(element, style, {bgColorCheck})) {
+ if (checkIsContained &&
+ !isContained(element, originalElement, {boxMargin}))
+ continue;
+ text += $(node).nodeValue;
+ }
+ break;
+ }
+ }
+ return text + getPseudoContent(element, ":after", {bgColorCheck});
+ }
+ const boxMarginStr = optionalParameters.get("-snippet-box-margin");
+ const boxMargin = parseFloat(boxMarginStr) || 0;
+
+ const bgColorCheckStr = optionalParameters.get("-disable-bg-color-check");
+ const bgColorCheck = !(bgColorCheckStr === "true");
+
+ const checkIsContainedStr = optionalParameters.get("-check-is-contained");
+ const checkIsContained = (checkIsContainedStr === "true");
+
+ let re = toRegExp(search);
+ let seen = new WeakSet$7();
+
+ const mo = hideIfMatches(
+ (element, closest) => {
+ if (seen.has(element))
+ return false;
+
+ seen.add(element);
+ let text = getVisibleContent(element, closest, null, null, element, {
+ boxMargin,
+ bgColorCheck,
+ checkIsContained
+ }
+ );
+ let result = re.test(text);
+ if (debug() && text.length)
+ log(result, re, text);
+ return result;
+ },
+ selector,
+ searchSelector
+ );
+ mo.race(raceWinner(
+ "hide-if-contains-visible-text",
+ () => {
+ mo.disconnect();
+ }
+ ));
+ }
+
+ let {MutationObserver: MutationObserver$5, WeakSet: WeakSet$6, getComputedStyle: getComputedStyle$1} = $(window);
+
+ function hideIfHasAndMatchesStyle(search,
+ selector = "*",
+ searchSelector = null,
+ style = null,
+ searchStyle = null,
+ waitUntil = null,
+ windowWidthMin = null,
+ windowWidthMax = null
+ ) {
+ const debugLog = getDebugger("hide-if-has-and-matches-style");
+ const hiddenMap = new WeakSet$6();
+ if (searchSelector == null)
+ searchSelector = selector;
+
+ const styleRegExp = style ? toRegExp(style) : null;
+ const searchStyleRegExp = searchStyle ? toRegExp(searchStyle) : null;
+ const mainLogic = () => {
+ const callback = () => {
+ if ((windowWidthMin && window.innerWidth < windowWidthMin) ||
+ (windowWidthMax && window.innerWidth > windowWidthMax)
+ )
+ return;
+ for (const element of $$(searchSelector)) {
+ if (hiddenMap.has(element))
+ continue;
+ if ($(element).querySelector(search) &&
+ (!searchStyleRegExp ||
+ searchStyleRegExp.test(getComputedCSSText(element)))) {
+ const closest = $(element).closest(selector);
+ if (closest && (!styleRegExp ||
+ styleRegExp.test(getComputedCSSText(closest)))) {
+ win();
+ hideElement(closest);
+ hiddenMap.add(element);
+ debugLog("Matched: ",
+ closest,
+ "which contains: ",
+ element,
+ " for params: ",
+ ...arguments);
+ }
+ else {
+ debugLog(
+ "In this element the searchStyle matched but style didn't:\n",
+ closest,
+ getComputedStyle$1(closest),
+ ...arguments);
+ }
+ }
+ else {
+ debugLog(
+ "In this element the searchStyle didn't match:\n",
+ element,
+ getComputedStyle$1(element),
+ ...arguments);
+ }
+ }
+ };
+
+ const mo = new MutationObserver$5(callback);
+ const win = raceWinner(
+ "hide-if-has-and-matches-style",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {childList: true, subtree: true});
+ callback();
+ };
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
+ }
+
+ let {getComputedStyle, MutationObserver: MutationObserver$4, WeakSet: WeakSet$5} = $(window);
+
+ function hideIfLabelledBy(search, selector, searchSelector = null) {
+ let sameSelector = searchSelector == null;
+
+ let searchRegExp = toRegExp(search);
+
+ let matched = new WeakSet$5();
+
+ let callback = () => {
+ for (let node of $$(selector)) {
+ let closest = sameSelector ?
+ node :
+ $(node).closest(searchSelector);
+ if (!closest || !isVisible(node, getComputedStyle(node), closest))
+ continue;
+
+ let attr = $(node).getAttribute("aria-labelledby");
+ let fallback = () => {
+ if (matched.has(closest))
+ return;
+
+ if (searchRegExp.test(
+ $(node).getAttribute("aria-label") || ""
+ )) {
+ win();
+ matched.add(closest);
+ hideElement(closest);
+ }
+ };
+
+ if (attr) {
+ for (let label of $(attr).split(/\s+/)) {
+ let target = $(document).getElementById(label);
+ if (target) {
+ if (!matched.has(target) && searchRegExp.test(target.innerText)) {
+ win();
+ matched.add(target);
+ hideElement(closest);
+ }
+ }
+ else {
+ fallback();
+ }
+ }
+ }
+ else {
+ fallback();
+ }
+ }
+ };
+
+ let mo = new MutationObserver$4(callback);
+ let win = raceWinner(
+ "hide-if-labelled-by",
+ () => mo.disconnect()
+ );
+ mo.observe(document, {characterData: true, childList: true, subtree: true});
+ callback();
+ }
+
+ $(window);
+
+ const noopProfile = {
+ mark() {},
+ end() {},
+ toString() {
+ return "{mark(){},end(){}}";
+ }
+ };
+
+ function profile(id, rate = 10) {
+ return noopProfile;
+ }
+
+ let {MutationObserver: MutationObserver$3, WeakSet: WeakSet$4} = $(window);
+
+ const {ELEMENT_NODE: ELEMENT_NODE$1} = Node;
+
+ function hideIfMatchesXPath(query, scopeQuery) {
+ const {mark, end} = profile();
+ const debugLog = getDebugger("hide-if-matches-xpath");
+
+ const startHidingMutationObserver = scopeNode => {
+ const queryAndApply = initQueryAndApply(`xpath(${query})`);
+ const seenMap = new WeakSet$4();
+ const callback = () => {
+ mark();
+ queryAndApply(node => {
+ if (seenMap.has(node))
+ return false;
+ seenMap.add(node);
+ win();
+ if ($(node).nodeType === ELEMENT_NODE$1)
+ hideElement(node);
+ else
+ $(node).textContent = "";
+ debugLog("Matched: ", node, " for selector: ", query);
+ });
+ end();
+ };
+ const mo = new MutationObserver$3(callback);
+ const win = raceWinner(
+ "hide-if-matches-xpath",
+ () => mo.disconnect()
+ );
+ mo.observe(
+ scopeNode, {characterData: true, childList: true, subtree: true});
+ callback();
+ };
+
+ if (scopeQuery) {
+
+ let count = 0;
+ let scopeMutationObserver;
+ const scopeQueryAndApply = initQueryAndApply(`xpath(${scopeQuery})`);
+ const findMutationScopeNodes = () => {
+ scopeQueryAndApply(scopeNode => {
+
+ startHidingMutationObserver(scopeNode);
+ count++;
+ });
+ if (count > 0)
+ scopeMutationObserver.disconnect();
+ };
+ scopeMutationObserver = new MutationObserver$3(findMutationScopeNodes);
+ scopeMutationObserver.observe(
+ document, {characterData: true, childList: true, subtree: true}
+ );
+ findMutationScopeNodes();
+ }
+ else {
+
+ startHidingMutationObserver(document);
+ }
+ }
+
+ let {MutationObserver: MutationObserver$2, WeakSet: WeakSet$3} = $(window);
+
+ const {ELEMENT_NODE} = Node;
+
+ function hideIfMatchesComputedXPath(query, searchQuery, searchRegex,
+ waitUntil) {
+ const {mark, end} = profile();
+ const debugLog = getDebugger("hide-if-matches-computed-xpath");
+
+ if (!searchQuery || !query) {
+ debugLog("No query or searchQuery provided.");
+ return;
+ }
+
+ const computeQuery = foundText => query.replace("{{}}", foundText);
+
+ const startHidingMutationObserver = foundText => {
+ const computedQuery = computeQuery(foundText);
+ debugLog("Starting hiding elements that match query: ", computedQuery);
+ const queryAndApply = initQueryAndApply(`xpath(${computedQuery})`);
+ const seenMap = new WeakSet$3();
+ const callback = () => {
+ mark();
+ queryAndApply(node => {
+ if (seenMap.has(node))
+ return false;
+ seenMap.add(node);
+ win();
+ if ($(node).nodeType === ELEMENT_NODE)
+ hideElement(node);
+ else
+ $(node).textContent = "";
+ debugLog("Matched: ", node, " for selector: ", query);
+ });
+ end();
+ };
+ const mo = new MutationObserver$2(callback);
+ const win = raceWinner(
+ "hide-if-matches-computed-xpath",
+ () => mo.disconnect()
+ );
+ mo.observe(
+ document, {characterData: true, childList: true, subtree: true});
+ callback();
+ };
+
+ const re = toRegExp(searchRegex);
+
+ const mainLogic = () => {
+ if (searchQuery) {
+ debugLog("Started searching for: ", searchQuery);
+ const seenMap = new WeakSet$3();
+ let searchMO;
+ const searchQueryAndApply = initQueryAndApply(`xpath(${searchQuery})`);
+ const findMutationSearchNodes = () => {
+ searchQueryAndApply(searchNode => {
+ if (seenMap.has(searchNode))
+ return false;
+ seenMap.add(searchNode);
+ debugLog("Found node: ", searchNode);
+ if (searchNode.innerHTML) {
+ debugLog("Searching in: ", searchNode.innerHTML);
+ const foundTextArr = searchNode.innerHTML.match(re);
+ if (foundTextArr && foundTextArr.length) {
+ let foundText = "";
+
+ foundTextArr[1] ? foundText = foundTextArr[1] :
+ foundText = foundTextArr[0];
+ debugLog("Matched search query: ", foundText);
+ startHidingMutationObserver(foundText);
+ }
+ }
+ });
+ };
+
+ searchMO = new MutationObserver$2(findMutationSearchNodes);
+ searchMO.observe(
+ document, {characterData: true, childList: true, subtree: true}
+ );
+ findMutationSearchNodes();
+ }
+ };
+
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
+ }
+
+ let {
+ parseInt: parseInt$1,
+ setTimeout: setTimeout$1,
+ Error: Error$2,
+ MouseEvent: MouseEvent$2,
+ MutationObserver: MutationObserver$1,
+ WeakSet: WeakSet$2
+ } = $(window);
+
+ function simulateEvent(event, selector, delay = "0") {
+ if (!event)
+ throw new Error$2("[simulate-event snippet]: No event type provided.");
+ if (!selector)
+ throw new Error$2("[simulate-event snippet]: No selector provided.");
+
+ let queryAndApply = initQueryAndApply(selector);
+ let delayInMiliseconds = parseInt$1(delay, 10);
+ let dispatchedNodes = new WeakSet$2();
+
+ let observer = new MutationObserver$1(findNodesAndDispatchEvents);
+ observer.observe(document, {childList: true, subtree: true});
+ findNodesAndDispatchEvents();
+
+ function findNodesAndDispatchEvents() {
+ queryAndApply(node => {
+ if (!dispatchedNodes.has(node)) {
+ dispatchedNodes.add(node);
+ setTimeout$1(() => {
+ $(node).dispatchEvent(
+ new MouseEvent$2(event, {bubbles: true, cancelable: true})
+ );
+ }, delayInMiliseconds);
+ }
+ });
+ }
+ }
+
+ let {
+ parseInt,
+ setTimeout,
+ Error: Error$1,
+ MouseEvent: MouseEvent$1,
+ MutationObserver,
+ WeakSet: WeakSet$1
+ } = $(window);
+
+ const VALID_TYPES = ["auxclick", "click", "dblclick", "gotpointercapture",
+ "lostpointercapture", "mouseenter", "mousedown",
+ "mouseleave", "mousemove", "mouseout", "mouseover",
+ "mouseup", "pointerdown", "pointerenter",
+ "pointermove", "pointerover", "pointerout",
+ "pointerup", "pointercancel", "pointerleave"];
+
+ function simulateMouseEvent(...selectors) {
+ const debugLog = getDebugger("simulate-mouse-event");
+ const MAX_ARGS = 7;
+ if (selectors.length < 1)
+ throw new Error$1("[simulate-mouse-event snippet]: No selector provided.");
+ if (selectors.length > MAX_ARGS) {
+
+ selectors = selectors.slice(0, MAX_ARGS);
+ }
+ function parseArg(theRule) {
+ if (!theRule)
+ return null;
+
+ const result = {
+ selector: "",
+ continue: false,
+ trigger: false,
+ event: "click",
+ delay: "500",
+ clicked: false,
+ found: false
+ };
+ const textArr = theRule.split("$");
+ let options = [];
+ if (textArr.length >= 2)
+ options = textArr[1].toLowerCase().split(",");
+
+ [result.selector] = textArr;
+
+ for (const option of options) {
+ if (option === "trigger") {
+ result.trigger = true;
+ }
+ else if (option === "continue") {
+ result.continue = true;
+ }
+ else if (option.startsWith("event")) {
+ const event = option.toLowerCase().split("=");
+ event[1] ? result.event = event[1] : result.event = "click";
+ }
+ else if (option.startsWith("delay")) {
+ const delay = option.toLowerCase().split("=");
+ delay[1] ? result.delay = delay[1] : result.delay = "500";
+ }
+ }
+ if (!VALID_TYPES.includes(result.event)) {
+ debugLog(result.event,
+ " might be misspelled, check for typos.\n",
+ "These are the supported events:",
+ VALID_TYPES);
+ }
+ return result;
+ }
+
+ const parsedArgs = $([]);
+
+ $(selectors).forEach(rule => {
+ const parsedRule = parseArg(rule);
+ parsedArgs.push(parsedRule);
+ });
+
+ function checkIfAllSelectorsFound() {
+ parsedArgs.forEach(arg => {
+ if (!arg.found) {
+ const queryAll = initQueryAll(arg.selector);
+ const elems = queryAll();
+ if (elems.length > 0)
+ arg.found = true;
+ }
+ });
+ return parsedArgs.every(arg => arg.found);
+ }
+
+ function triggerEvent(node, event, delay) {
+
+ if (!node || !event)
+ return;
+
+ if (event === "click" && node.click) {
+ node.click();
+ debugLog(
+ "Clicked on this node:\n", node, "\nwith a delay of", delay, "ms"
+ );
+ }
+ else {
+ node.dispatchEvent(
+ new MouseEvent$1(event, {bubbles: true, cancelable: true})
+ );
+ debugLog(
+ "A",
+ event,
+ "event was dispatched with a delay of",
+ delay,
+ "ms on this node:\n",
+ node
+ );
+ }
+ }
+ let allFound = false;
+
+ const [last] = parsedArgs.slice(-1);
+ last.trigger = true;
+
+ let dispatchedNodes = new WeakSet$1();
+
+ let observer = new MutationObserver(findNodesAndDispatchEvents);
+ observer.observe(document, {childList: true, subtree: true});
+ findNodesAndDispatchEvents();
+
+ function findNodesAndDispatchEvents() {
+
+ if (!allFound)
+ allFound = checkIfAllSelectorsFound();
+ if (allFound) {
+ for (const parsedRule of parsedArgs) {
+ const queryAndApply = initQueryAndApply(parsedRule.selector);
+ const delayInMiliseconds = parseInt(parsedRule.delay, 10);
+ if (parsedRule.trigger) {
+ queryAndApply(node => {
+ if (!dispatchedNodes.has(node)) {
+ dispatchedNodes.add(node);
+ if (parsedRule.continue) {
+ setInterval(() => {
+ triggerEvent(node, parsedRule.event, parsedRule.delay);
+ }, delayInMiliseconds);
+ }
+ else {
+ setTimeout(() => {
+ triggerEvent(node, parsedRule.event, parsedRule.delay);
+ }, delayInMiliseconds);
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+
+ const snippets = {
+ log,
+ race,
+ "debug": setDebug,
+ "hide-if-matches-xpath": hideIfMatchesXPath,
+ "hide-if-matches-computed-xpath": hideIfMatchesComputedXPath,
+ "hide-if-contains": hideIfContains,
+ "hide-if-contains-similar-text": hideIfContainsSimilarText,
+ "hide-if-contains-visible-text": hideIfContainsVisibleText,
+ "hide-if-contains-and-matches-style": hideIfContainsAndMatchesStyle,
+ "hide-if-has-and-matches-style": hideIfHasAndMatchesStyle,
+ "hide-if-labelled-by": hideIfLabelledBy,
+ "hide-if-contains-image": hideIfContainsImage,
+ "simulate-event-poc": simulateEvent,
+ "simulate-mouse-event": simulateMouseEvent
+ };
+ let context;
+ for (const [name, ...args] of filters) {
+ if (snippets.hasOwnProperty(name)) {
+ try { context = snippets[name].apply(context, args); }
+ catch (error) { console.error(error); }
+ }
+ }
+ context = void 0;
+})(e, ...t);
+
+const callback = (environment, ...filters) => {
+ /*!
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
+ * Copyright (C) 2006-present eyeo GmbH
+ *
+ * @eyeo/snippets 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/snippets 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/snippets. If not, see <http://www.gnu.org/licenses/>.
+ */
+ const $$1 = Proxy;
+
+ const {apply: a, bind: b, call: c} = Function;
+ const apply$2 = c.bind(a);
+ const bind = c.bind(b);
+ const call = c.bind(c);
+
+ const callerHandler = {
+ get(target, name) {
+ return bind(c, target[name]);
+ }
+ };
+
+ const caller = target => new $$1(target, callerHandler);
+
+ const proxy = (source, target) => new $$1(source, {
+ apply: (_, self, args) => apply$2(target, self, args)
+ });
+
+ const handler$2 = {
+ get(target, name) {
+ return bind(target[name], target);
+ }
+ };
+
+ const bound = target => new $$1(target, handler$2);
+
+ const {
+ assign: assign$1,
+ defineProperties: defineProperties$1,
+ freeze: freeze$1,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$3,
+ getOwnPropertyDescriptors: getOwnPropertyDescriptors$1,
+ getPrototypeOf
+ } = bound(Object);
+
+ const {hasOwnProperty} = caller({});
+
+ const {species} = Symbol;
+
+ const handler$1 = {
+ get(target, name) {
+ const Native = target[name];
+ class Secure extends Native {}
+
+ const proto = getOwnPropertyDescriptors$1(Native.prototype);
+ delete proto.constructor;
+ freeze$1(defineProperties$1(Secure.prototype, proto));
+
+ const statics = getOwnPropertyDescriptors$1(Native);
+ delete statics.length;
+ delete statics.prototype;
+ statics[species] = {value: Secure};
+ return freeze$1(defineProperties$1(Secure, statics));
+ }
+ };
+
+ const secure = target => new $$1(target, handler$1);
+
+ const libEnvironment = typeof environment !== "undefined" ? environment :
+ {};
+
+ if (typeof globalThis === "undefined")
+ window.globalThis = window;
+
+ const {apply: apply$1, ownKeys} = bound(Reflect);
+
+ const worldEnvDefined = "world" in libEnvironment;
+ const isIsolatedWorld = worldEnvDefined && libEnvironment.world === "ISOLATED";
+ const isMainWorld = worldEnvDefined && libEnvironment.world === "MAIN";
+ const isChrome = typeof chrome === "object" && !!chrome.runtime;
+ const isOtherThanChrome = typeof browser === "object" && !!browser.runtime;
+ const isExtensionContext$2 = !isMainWorld &&
+ (isIsolatedWorld || isChrome || isOtherThanChrome);
+ const copyIfExtension = value => isExtensionContext$2 ?
+ value :
+ create(value, getOwnPropertyDescriptors(value));
+
+ const {
+ create,
+ defineProperties,
+ defineProperty,
+ freeze,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$2,
+ getOwnPropertyDescriptors
+ } = bound(Object);
+
+ const invokes = bound(globalThis);
+ const classes = isExtensionContext$2 ? globalThis : secure(globalThis);
+ const {Map: Map$8, RegExp: RegExp$1, Set: Set$2, WeakMap: WeakMap$4, WeakSet: WeakSet$3} = classes;
+
+ const augment = (source, target, method = null) => {
+ const known = ownKeys(target);
+ for (const key of ownKeys(source)) {
+ if (known.includes(key))
+ continue;
+
+ const descriptor = getOwnPropertyDescriptor$2(source, key);
+ if (method && "value" in descriptor) {
+ const {value} = descriptor;
+ if (typeof value === "function")
+ descriptor.value = method(value);
+ }
+ defineProperty(target, key, descriptor);
+ }
+ };
+
+ const primitive = name => {
+ const Super = classes[name];
+ class Class extends Super {}
+ const {toString, valueOf} = Super.prototype;
+ defineProperties(Class.prototype, {
+ toString: {value: toString},
+ valueOf: {value: valueOf}
+ });
+ const type = name.toLowerCase();
+ const method = callback => function() {
+ const result = apply$1(callback, this, arguments);
+ return typeof result === type ? new Class(result) : result;
+ };
+ augment(Super, Class, method);
+ augment(Super.prototype, Class.prototype, method);
+ return Class;
+ };
+
+ const variables$3 = freeze({
+ frozen: new WeakMap$4(),
+ hidden: new WeakSet$3(),
+ iframePropertiesToAbort: {
+ read: new Set$2(),
+ write: new Set$2()
+ },
+ abortedIframes: new WeakMap$4()
+ });
+
+ const startsCapitalized = new RegExp$1("^[A-Z]");
+
+ var env = new Proxy(new Map$8([
+
+ ["chrome", (
+ isExtensionContext$2 && (
+ (isChrome && chrome) ||
+ (isOtherThanChrome && browser)
+ )
+ ) || void 0],
+ ["isExtensionContext", isExtensionContext$2],
+ ["variables", variables$3],
+
+ ["console", copyIfExtension(console)],
+ ["document", globalThis.document],
+ ["performance", copyIfExtension(performance)],
+ ["JSON", copyIfExtension(JSON)],
+ ["Map", Map$8],
+ ["Math", copyIfExtension(Math)],
+ ["Number", isExtensionContext$2 ? Number : primitive("Number")],
+ ["RegExp", RegExp$1],
+ ["Set", Set$2],
+ ["String", isExtensionContext$2 ? String : primitive("String")],
+ ["WeakMap", WeakMap$4],
+ ["WeakSet", WeakSet$3],
+
+ ["MouseEvent", MouseEvent]
+ ]), {
+ get(map, key) {
+ if (map.has(key))
+ return map.get(key);
+
+ let value = globalThis[key];
+ if (typeof value === "function")
+ value = (startsCapitalized.test(key) ? classes : invokes)[key];
+
+ map.set(key, value);
+ return value;
+ },
+ has(map, key) {
+ return map.has(key);
+ }
+ });
+
+ class WeakValue {
+ has() { return false; }
+ set() {}
+ }
+
+ const helpers = {WeakSet, WeakMap, WeakValue};
+ const {apply} = Reflect;
+
+ function transformOnce (callback) { const {WeakSet, WeakMap, WeakValue} = (this || helpers);
+ const ws = new WeakSet;
+ const wm = new WeakMap;
+ const wv = new WeakValue;
+ return function (any) {
+ if (ws.has(any))
+ return any;
+
+ if (wm.has(any))
+ return wm.get(any);
+
+ if (wv.has(any))
+ return wv.get(any);
+
+ const value = apply(callback, this, arguments);
+ ws.add(value);
+ if (value !== any)
+ (typeof any === 'object' && any ? wm : wv).set(any, value);
+ return value;
+ };
+ }
+
+ const {Map: Map$7, WeakMap: WeakMap$3, WeakSet: WeakSet$2, setTimeout} = env;
+
+ let cleanup = true;
+ let cleanUpCallback = map => {
+ map.clear();
+ cleanup = !cleanup;
+ };
+
+ var transformer = transformOnce.bind({
+ WeakMap: WeakMap$3,
+ WeakSet: WeakSet$2,
+
+ WeakValue: class extends Map$7 {
+ set(key, value) {
+ if (cleanup) {
+ cleanup = !cleanup;
+ setTimeout(cleanUpCallback, 0, this);
+ }
+ return super.set(key, value);
+ }
+ }
+ });
+
+ const {concat, includes, join, reduce, unshift} = caller([]);
+
+ const globals = secure(globalThis);
+
+ const {
+ Map: Map$6,
+ WeakMap: WeakMap$2
+ } = globals;
+
+ const map = new Map$6;
+ const descriptors = target => {
+ const chain = [];
+ let current = target;
+ while (current) {
+ if (map.has(current))
+ unshift(chain, map.get(current));
+ else {
+ const descriptors = getOwnPropertyDescriptors$1(current);
+ map.set(current, descriptors);
+ unshift(chain, descriptors);
+ }
+ current = getPrototypeOf(current);
+ }
+ unshift(chain, {});
+ return apply$2(assign$1, null, chain);
+ };
+
+ const chain = source => {
+ const target = typeof source === 'function' ? source.prototype : source;
+ const chained = descriptors(target);
+ const handler = {
+ get(target, key) {
+ if (key in chained) {
+ const {value, get} = chained[key];
+ if (get)
+ return call(get, target);
+ if (typeof value === 'function')
+ return bind(value, target);
+ }
+ return target[key];
+ },
+ set(target, key, value) {
+ if (key in chained) {
+ const {set} = chained[key];
+ if (set) {
+ call(set, target, value);
+ return true;
+ }
+ }
+ target[key] = value;
+ return true;
+ }
+ };
+ return target => new $$1(target, handler);
+ };
+
+ const {
+ isExtensionContext: isExtensionContext$1,
+ Array: Array$2,
+ Number: Number$1,
+ String: String$1,
+ Object: Object$9
+ } = env;
+
+ const {isArray} = Array$2;
+ const {getOwnPropertyDescriptor: getOwnPropertyDescriptor$1, setPrototypeOf: setPrototypeOf$1} = Object$9;
+
+ const {toString: toString$1} = Object$9.prototype;
+ const {slice} = String$1.prototype;
+ const getBrand = value => call(slice, call(toString$1, value), 8, -1);
+
+ const {get: nodeType} = getOwnPropertyDescriptor$1(Node.prototype, "nodeType");
+
+ const chained = isExtensionContext$1 ? {} : {
+ Attr: chain(Attr),
+ CanvasRenderingContext2D: chain(CanvasRenderingContext2D),
+ CSSStyleDeclaration: chain(CSSStyleDeclaration),
+ Document: chain(Document),
+ Element: chain(Element),
+ HTMLCanvasElement: chain(HTMLCanvasElement),
+ HTMLElement: chain(HTMLElement),
+ HTMLImageElement: chain(HTMLImageElement),
+ HTMLScriptElement: chain(HTMLScriptElement),
+ MutationRecord: chain(MutationRecord),
+ Node: chain(Node),
+ ShadowRoot: chain(ShadowRoot),
+
+ get CSS2Properties() {
+ return chained.CSSStyleDeclaration;
+ }
+ };
+
+ const upgrade = (value, hint) => {
+ if (hint !== "Element" && hint in chained)
+ return chained[hint](value);
+
+ if (isArray(value))
+ return setPrototypeOf$1(value, Array$2.prototype);
+
+ const brand = getBrand(value);
+ if (brand in chained)
+ return chained[brand](value);
+
+ if (brand in env)
+ return setPrototypeOf$1(value, env[brand].prototype);
+
+ if ("nodeType" in value) {
+ switch (call(nodeType, value)) {
+ case 1:
+ if (!(hint in chained))
+ throw new Error("unknown hint " + hint);
+ return chained[hint](value);
+ case 2:
+ return chained.Attr(value);
+ case 3:
+ return chained.Node(value);
+ case 9:
+ return chained.Document(value);
+ }
+ }
+
+ throw new Error("unknown brand " + brand);
+ };
+
+ var $ = isExtensionContext$1 ?
+ value => (value === window || value === globalThis ? env : value) :
+ transformer((value, hint = "Element") => {
+ if (value === window || value === globalThis)
+ return env;
+
+ switch (typeof value) {
+ case "object":
+ return value && upgrade(value, hint);
+
+ case "string":
+ return new String$1(value);
+
+ case "number":
+ return new Number$1(value);
+
+ default:
+ throw new Error("unsupported value");
+ }
+ });
+
+ const handler = {
+ get(target, name) {
+ const context = target;
+ while (!hasOwnProperty(target, name))
+ target = getPrototypeOf(target);
+ const {get, set} = getOwnPropertyDescriptor$3(target, name);
+ return function () {
+ return arguments.length ?
+ apply$2(set, context, arguments) :
+ call(get, context);
+ };
+ }
+ };
+
+ const accessor = target => new $$1(target, handler);
+
+ let debugging = false;
+
+ function debug() {
+ return debugging;
+ }
+
+ function setDebug() {
+ debugging = true;
+ }
+
+ const {console: console$3} = $(window);
+
+ const noop = () => {};
+
+ function log(...args) {
+ if (debug())
+ $(args).unshift("%c DEBUG", "font-weight: bold");
+
+ console$3.log(...args);
+ }
+
+ function getDebugger(name) {
+ return bind(debug() ? log : noop, null, name);
+ }
+
+ let {Math: Math$1, RegExp} = $(window);
+
+ function regexEscape(string) {
+ return $(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
+ }
+
+ function toRegExp(pattern) {
+ let {length} = pattern;
+
+ if (length > 1 && pattern[0] === "/") {
+ let isCaseSensitive = pattern[length - 1] === "/";
+
+ if (isCaseSensitive || (length > 2 && $(pattern).endsWith("/i"))) {
+ let args = [$(pattern).slice(1, isCaseSensitive ? -1 : -2)];
+ if (!isCaseSensitive)
+ args.push("i");
+
+ return new RegExp(...args);
+ }
+ }
+
+ return new RegExp(regexEscape(pattern));
+ }
+
+ function randomId() {
+
+ return $(Math$1.floor(Math$1.random() * 2116316160 + 60466176)).toString(36);
+ }
+
+ let {
+ parseFloat,
+ variables: variables$2,
+ Array: Array$1,
+ Error: Error$7,
+ Map: Map$5,
+ Object: Object$8,
+ ReferenceError: ReferenceError$2,
+ Set: Set$1,
+ WeakMap: WeakMap$1
+ } = $(window);
+
+ let {onerror} = accessor(window);
+
+ let NodeProto$1 = Node.prototype;
+ let ElementProto$2 = Element.prototype;
+
+ let propertyAccessors = null;
+
+ function wrapPropertyAccess(object, property, descriptor,
+ setConfigurable = true) {
+ let $property = $(property);
+ let dotIndex = $property.indexOf(".");
+ if (dotIndex == -1) {
+
+ let currentDescriptor = Object$8.getOwnPropertyDescriptor(object, property);
+ if (currentDescriptor && !currentDescriptor.configurable)
+ return;
+
+ let newDescriptor = Object$8.assign({}, descriptor, {
+ configurable: setConfigurable
+ });
+
+ if (!currentDescriptor && !newDescriptor.get && newDescriptor.set) {
+ let propertyValue = object[property];
+ newDescriptor.get = () => propertyValue;
+ }
+
+ Object$8.defineProperty(object, property, newDescriptor);
+ return;
+ }
+
+ let name = $property.slice(0, dotIndex).toString();
+ property = $property.slice(dotIndex + 1).toString();
+ let value = object[name];
+ if (value && (typeof value == "object" || typeof value == "function"))
+ wrapPropertyAccess(value, property, descriptor);
+
+ let currentDescriptor = Object$8.getOwnPropertyDescriptor(object, name);
+ if (currentDescriptor && !currentDescriptor.configurable)
+ return;
+
+ if (!propertyAccessors)
+ propertyAccessors = new WeakMap$1();
+
+ if (!propertyAccessors.has(object))
+ propertyAccessors.set(object, new Map$5());
+
+ let properties = propertyAccessors.get(object);
+ if (properties.has(name)) {
+ properties.get(name).set(property, descriptor);
+ return;
+ }
+
+ let toBeWrapped = new Map$5([[property, descriptor]]);
+ properties.set(name, toBeWrapped);
+ Object$8.defineProperty(object, name, {
+ get: () => value,
+ set(newValue) {
+ value = newValue;
+ if (value && (typeof value == "object" || typeof value == "function")) {
+
+ for (let [prop, desc] of toBeWrapped)
+ wrapPropertyAccess(value, prop, desc);
+ }
+ },
+ configurable: setConfigurable
+ });
+ }
+
+ function overrideOnError(magic) {
+ let prev = onerror();
+ onerror((...args) => {
+ let message = args.length && args[0];
+ if (typeof message == "string" && $(message).includes(magic))
+ return true;
+ if (typeof prev == "function")
+ return apply$2(prev, this, args);
+ });
+ }
+
+ function abortOnRead(loggingPrefix, context, property,
+ setConfigurable = true) {
+ let debugLog = getDebugger(loggingPrefix);
+
+ if (!property) {
+ debugLog("no property to abort on read");
+ return;
+ }
+
+ let rid = randomId();
+
+ function abort() {
+ debugLog(`${property} access aborted`);
+ throw new ReferenceError$2(rid);
+ }
+
+ debugLog(`aborting on ${property} access`);
+
+ wrapPropertyAccess(context,
+ property,
+ {get: abort, set() {}},
+ setConfigurable);
+ overrideOnError(rid);
+ }
+
+ function abortOnWrite(loggingPrefix, context, property,
+ setConfigurable = true) {
+ let debugLog = getDebugger(loggingPrefix);
+
+ if (!property) {
+ debugLog("no property to abort on write");
+ return;
+ }
+
+ let rid = randomId();
+
+ function abort() {
+ debugLog(`setting ${property} aborted`);
+ throw new ReferenceError$2(rid);
+ }
+
+ debugLog(`aborting when setting ${property}`);
+
+ wrapPropertyAccess(context, property, {set: abort}, setConfigurable);
+ overrideOnError(rid);
+ }
+
+ function abortOnIframe(
+ properties,
+ abortRead = false,
+ abortWrite = false
+ ) {
+ let abortedIframes = variables$2.abortedIframes;
+ let iframePropertiesToAbort = variables$2.iframePropertiesToAbort;
+
+ for (let frame of Array$1.from(window.frames)) {
+ if (abortedIframes.has(frame)) {
+ for (let property of properties) {
+ if (abortRead)
+ abortedIframes.get(frame).read.add(property);
+ if (abortWrite)
+ abortedIframes.get(frame).write.add(property);
+ }
+ }
+ }
+
+ for (let property of properties) {
+ if (abortRead)
+ iframePropertiesToAbort.read.add(property);
+ if (abortWrite)
+ iframePropertiesToAbort.write.add(property);
+ }
+
+ queryAndProxyIframe();
+ if (!abortedIframes.has(document)) {
+ abortedIframes.set(document, true);
+ addHooksOnDomAdditions(queryAndProxyIframe);
+ }
+
+ function queryAndProxyIframe() {
+ for (let frame of Array$1.from(window.frames)) {
+
+ if (!abortedIframes.has(frame)) {
+ abortedIframes.set(frame, {
+ read: new Set$1(iframePropertiesToAbort.read),
+ write: new Set$1(iframePropertiesToAbort.write)
+ });
+ }
+
+ let readProps = abortedIframes.get(frame).read;
+ if (readProps.size > 0) {
+ let props = Array$1.from(readProps);
+ readProps.clear();
+ for (let property of props)
+ abortOnRead("abort-on-iframe-property-read", frame, property);
+ }
+
+ let writeProps = abortedIframes.get(frame).write;
+ if (writeProps.size > 0) {
+ let props = Array$1.from(writeProps);
+ writeProps.clear();
+ for (let property of props)
+ abortOnWrite("abort-on-iframe-property-write", frame, property);
+ }
+ }
+ }
+ }
+
+ function addHooksOnDomAdditions(endCallback) {
+ let descriptor;
+
+ wrapAccess(NodeProto$1, ["appendChild", "insertBefore", "replaceChild"]);
+ wrapAccess(ElementProto$2, ["append", "prepend", "replaceWith", "after",
+ "before", "insertAdjacentElement",
+ "insertAdjacentHTML"]);
+
+ descriptor = getInnerHTMLDescriptor(ElementProto$2, "innerHTML");
+ wrapPropertyAccess(ElementProto$2, "innerHTML", descriptor);
+
+ descriptor = getInnerHTMLDescriptor(ElementProto$2, "outerHTML");
+ wrapPropertyAccess(ElementProto$2, "outerHTML", descriptor);
+
+ function wrapAccess(prototype, names) {
+ for (let name of names) {
+ let desc = getAppendChildDescriptor(prototype, name);
+ wrapPropertyAccess(prototype, name, desc);
+ }
+ }
+
+ function getAppendChildDescriptor(target, property) {
+ let currentValue = target[property];
+ return {
+ get() {
+ return function(...args) {
+ let result;
+ result = apply$2(currentValue, this, args);
+ endCallback && endCallback();
+ return result;
+ };
+ }
+ };
+ }
+
+ function getInnerHTMLDescriptor(target, property) {
+ let desc = Object$8.getOwnPropertyDescriptor(target, property);
+ let {set: prevSetter} = desc || {};
+ return {
+ set(val) {
+ let result;
+ result = call(prevSetter, this, val);
+ endCallback && endCallback();
+ return result;
+ }
+ };
+ }
+ }
+
+ let {Object: NativeObject} = window;
+ function findOwner(root, path) {
+ if (!(root instanceof NativeObject))
+ return;
+
+ let object = root;
+ let chain = $(path).split(".");
+
+ if (chain.length === 0)
+ return;
+
+ for (let i = 0; i < chain.length - 1; i++) {
+ let prop = chain[i];
+
+ if (!hasOwnProperty(object, prop))
+ return;
+
+ object = object[prop];
+
+ if (!(object instanceof NativeObject))
+ return;
+ }
+
+ let prop = chain[chain.length - 1];
+
+ if (hasOwnProperty(object, prop))
+ return [object, prop];
+ }
+
+ const decimals = $(/^\d+$/);
+
+ function overrideValue(value) {
+ switch (value) {
+ case "false":
+ return false;
+ case "true":
+ return true;
+ case "null":
+ return null;
+ case "noopFunc":
+ return () => {};
+ case "trueFunc":
+ return () => true;
+ case "falseFunc":
+ return () => false;
+ case "emptyArray":
+ return [];
+ case "emptyObj":
+ return {};
+ case "undefined":
+ return void 0;
+ case "":
+ return value;
+ default:
+ if (decimals.test(value))
+ return parseFloat(value);
+
+ throw new Error$7("[override-property-read snippet]: " +
+ `Value "${value}" is not valid.`);
+ }
+ }
+
+ let {HTMLScriptElement: HTMLScriptElement$1, Object: Object$7, ReferenceError: ReferenceError$1} = $(window);
+ let Script = Object$7.getPrototypeOf(HTMLScriptElement$1);
+
+ function abortCurrentInlineScript(api, search = null) {
+ const debugLog = getDebugger("abort-current-inline-script");
+ const re = search ? toRegExp(search) : null;
+
+ const rid = randomId();
+ const us = $(document).currentScript;
+
+ let object = window;
+ const path = $(api).split(".");
+ const name = $(path).pop();
+
+ for (let node of $(path)) {
+ object = object[node];
+ if (
+ !object || !(typeof object == "object" || typeof object == "function")) {
+ debugLog(path, " is not found");
+ return;
+ }
+ }
+
+ const {get: prevGetter, set: prevSetter} =
+ Object$7.getOwnPropertyDescriptor(object, name) || {};
+
+ let currentValue = object[name];
+ if (typeof currentValue === "undefined")
+ debugLog("The property", name, "doesn't exist yet. Check typos.");
+
+ const abort = () => {
+ const element = $(document).currentScript;
+ if (element instanceof Script &&
+ $(element, "HTMLScriptElement").src == "" &&
+ element != us &&
+ (!re || re.test($(element).textContent))) {
+ debugLog(path, " is aborted \n", element);
+ throw new ReferenceError$1(rid);
+ }
+ };
+
+ const descriptor = {
+ get() {
+ abort();
+
+ if (prevGetter)
+ return call(prevGetter, this);
+
+ return currentValue;
+ },
+ set(value) {
+ abort();
+
+ if (prevSetter)
+ call(prevSetter, this, value);
+ else
+ currentValue = value;
+ }
+ };
+
+ wrapPropertyAccess(object, name, descriptor);
+
+ overrideOnError(rid);
+ }
+
+ function abortOnIframePropertyRead(...properties) {
+ abortOnIframe(properties, true, false);
+ }
+
+ function abortOnIframePropertyWrite(...properties) {
+ abortOnIframe(properties, false, true);
+ }
+
+ function abortOnPropertyRead(property, setConfigurable) {
+ const configurableFlag = !(setConfigurable === "false");
+ abortOnRead("abort-on-property-read", window, property, configurableFlag);
+ }
+
+ function abortOnPropertyWrite(property, setConfigurable) {
+ const configurableFlag = !(setConfigurable === "false");
+ abortOnWrite("abort-on-property-write", window, property, configurableFlag);
+ }
+
+ let {Error: Error$6} = $(window);
+ let {cookie: documentCookies} = accessor(document);
+
+ function cookieRemover(cookie) {
+ if (!cookie)
+ throw new Error$6("[cookie-remover snippet]: No cookie to remove.");
+
+ let debugLog = getDebugger("cookie-remover");
+ let re = toRegExp(cookie);
+
+ if (!$(/^http|^about/).test(location.protocol)) {
+ debugLog("Snippet only works for http or https and about.");
+ return;
+ }
+
+ debugLog("Parsing cookies for matches");
+
+ for (const pair of $(getCookieMatches())) {
+ let $hostname = $(location.hostname);
+ let name = $(pair).split("=")[0];
+ let expires = "expires=Thu, 01 Jan 1970 00:00:00 GMT";
+ let path = "path=/";
+ let domain = "domain=" + $hostname.slice($hostname.indexOf(".") + 1);
+
+ documentCookies(`${$(name).trim()}=;${expires};${path};${domain}`);
+
+ debugLog(`Set expiration date on ${name}`);
+ }
+
+ function getCookieMatches() {
+ const arr = $(documentCookies()).split(";");
+ return arr.filter(str => re.test($(str).split("=")[0]));
+ }
+ }
+
+ let {
+ document: document$1,
+ getComputedStyle,
+ isExtensionContext,
+ variables: variables$1,
+ Array,
+ MutationObserver: MutationObserver$2,
+ Object: Object$6,
+ XPathEvaluator,
+ XPathExpression,
+ XPathResult
+ } = $(window);
+
+ let {querySelectorAll} = document$1;
+ let $$ = querySelectorAll && bind(querySelectorAll, document$1);
+
+ const {assign, setPrototypeOf} = Object$6;
+
+ class $XPathExpression extends XPathExpression {
+ evaluate(...args) {
+ return setPrototypeOf(
+ apply$2(super.evaluate, this, args),
+ XPathResult.prototype
+ );
+ }
+ }
+
+ class $XPathEvaluator extends XPathEvaluator {
+ createExpression(...args) {
+ return setPrototypeOf(
+ apply$2(super.createExpression, this, args),
+ $XPathExpression.prototype
+ );
+ }
+ }
+
+ function hideElement(element) {
+ if (variables$1.hidden.has(element))
+ return;
+
+ notifyElementHidden(element);
+
+ variables$1.hidden.add(element);
+
+ let {style} = $(element);
+ let $style = $(style, "CSSStyleDeclaration");
+ let properties = $([]);
+ let {debugCSSProperties} = libEnvironment;
+
+ for (let [key, value] of (debugCSSProperties || [["display", "none"]])) {
+ $style.setProperty(key, value, "important");
+ properties.push([key, $style.getPropertyValue(key)]);
+ }
+
+ new MutationObserver$2(() => {
+ for (let [key, value] of properties) {
+ let propertyValue = $style.getPropertyValue(key);
+ let propertyPriority = $style.getPropertyPriority(key);
+ if (propertyValue != value || propertyPriority != "important")
+ $style.setProperty(key, value, "important");
+ }
+ }).observe(element, {attributes: true,
+ attributeFilter: ["style"]});
+ }
+
+ function notifyElementHidden(element) {
+ if (isExtensionContext && typeof checkElement === "function")
+ checkElement(element);
+ }
+
+ function initQueryAndApply(selector) {
+ let $selector = selector;
+ if ($selector.startsWith("xpath(") &&
+ $selector.endsWith(")")) {
+ let xpathQuery = $selector.slice(6, -1);
+ let evaluator = new $XPathEvaluator();
+ let expression = evaluator.createExpression(xpathQuery, null);
+
+ let flag = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
+
+ return cb => {
+ if (!cb)
+ return;
+ let result = expression.evaluate(document$1, flag, null);
+ let {snapshotLength} = result;
+ for (let i = 0; i < snapshotLength; i++)
+ cb(result.snapshotItem(i));
+ };
+ }
+ return cb => $$(selector).forEach(cb);
+ }
+
+ function initQueryAll(selector) {
+ let $selector = selector;
+ if ($selector.startsWith("xpath(") &&
+ $selector.endsWith(")")) {
+ let queryAndApply = initQueryAndApply(selector);
+ return () => {
+ let elements = $([]);
+ queryAndApply(e => elements.push(e));
+ return elements;
+ };
+ }
+ return () => Array.from($$(selector));
+ }
+
+ let {ELEMENT_NODE, TEXT_NODE, prototype: NodeProto} = Node;
+ let {prototype: ElementProto$1} = Element;
+ let {prototype: HTMLElementProto} = HTMLElement;
+
+ let {
+ console: console$2,
+ variables,
+ DOMParser,
+ Error: Error$5,
+ MutationObserver: MutationObserver$1,
+ Object: Object$5,
+ ReferenceError
+ } = $(window);
+
+ let {getOwnPropertyDescriptor} = Object$5;
+
+ function freezeElement(selector, options = "", ...exceptions) {
+ let observer;
+ let subtree = false;
+ let shouldAbort = false;
+ let exceptionSelectors = $(exceptions).filter(e => !isRegex(e));
+ let regexExceptions = $(exceptions).filter(e => isRegex(e)).map(toRegExp);
+ let rid = randomId();
+ let targetNodes;
+ let queryAll = initQueryAll(selector);
+
+ checkOptions();
+ let data = {
+ selector,
+ shouldAbort,
+ rid,
+ exceptionSelectors,
+ regexExceptions,
+ changeId: 0
+ };
+ if (!variables.frozen.has(document)) {
+ variables.frozen.set(document, true);
+ proxyNativeProperties();
+ }
+ observer = new MutationObserver$1(searchAndAttach);
+ observer.observe(document, {childList: true, subtree: true});
+ searchAndAttach();
+
+ function isRegex(s) {
+ return s.length >= 2 && s[0] == "/" && s[s.length - 1] == "/";
+ }
+
+ function checkOptions() {
+ let optionsChunks = $(options).split("+");
+ if (optionsChunks.length === 1 && optionsChunks[0] === "")
+ optionsChunks = [];
+ for (let chunk of optionsChunks) {
+ switch (chunk) {
+ case "subtree":
+ subtree = true;
+ break;
+ case "abort":
+ shouldAbort = true;
+ break;
+ default:
+ throw new Error$5("[freeze] Unknown option passed to the snippet." +
+ " [selector]: " + selector +
+ " [option]: " + chunk);
+ }
+ }
+ }
+
+ function proxyNativeProperties() {
+ let descriptor;
+
+ descriptor = getAppendChildDescriptor(
+ NodeProto, "appendChild", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "appendChild", descriptor);
+
+ descriptor = getAppendChildDescriptor(
+ NodeProto, "insertBefore", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "insertBefore", descriptor);
+
+ descriptor = getAppendChildDescriptor(
+ NodeProto, "replaceChild", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "replaceChild", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1, "append", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(ElementProto$1, "append", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1, "prepend", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(ElementProto$1, "prepend", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1,
+ "replaceWith",
+ isFrozenOrHasFrozenParent,
+ getSnippetDataFromNodeOrParent
+ );
+ wrapPropertyAccess(ElementProto$1, "replaceWith", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1,
+ "after",
+ isFrozenOrHasFrozenParent,
+ getSnippetDataFromNodeOrParent
+ );
+ wrapPropertyAccess(ElementProto$1, "after", descriptor);
+
+ descriptor = getAppendDescriptor(
+ ElementProto$1,
+ "before",
+ isFrozenOrHasFrozenParent,
+ getSnippetDataFromNodeOrParent
+ );
+ wrapPropertyAccess(ElementProto$1, "before", descriptor);
+
+ descriptor = getInsertAdjacentDescriptor(
+ ElementProto$1,
+ "insertAdjacentElement",
+ isFrozenAndInsideTarget,
+ getSnippetDataBasedOnTarget
+ );
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentElement", descriptor);
+
+ descriptor = getInsertAdjacentDescriptor(
+ ElementProto$1,
+ "insertAdjacentHTML",
+ isFrozenAndInsideTarget,
+ getSnippetDataBasedOnTarget
+ );
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentHTML", descriptor);
+
+ descriptor = getInsertAdjacentDescriptor(
+ ElementProto$1,
+ "insertAdjacentText",
+ isFrozenAndInsideTarget,
+ getSnippetDataBasedOnTarget
+ );
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentText", descriptor);
+
+ descriptor = getInnerHTMLDescriptor(
+ ElementProto$1, "innerHTML", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(ElementProto$1, "innerHTML", descriptor);
+
+ descriptor = getInnerHTMLDescriptor(
+ ElementProto$1,
+ "outerHTML",
+ isFrozenOrHasFrozenParent,
+ getSnippetDataFromNodeOrParent
+ );
+ wrapPropertyAccess(ElementProto$1, "outerHTML", descriptor);
+
+ descriptor = getTextContentDescriptor(
+ NodeProto, "textContent", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "textContent", descriptor);
+
+ descriptor = getTextContentDescriptor(
+ HTMLElementProto, "innerText", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(HTMLElementProto, "innerText", descriptor);
+
+ descriptor = getTextContentDescriptor(
+ NodeProto, "nodeValue", isFrozen, getSnippetData
+ );
+ wrapPropertyAccess(NodeProto, "nodeValue", descriptor);
+
+ function isFrozen(node) {
+ return node && variables.frozen.has(node);
+ }
+
+ function isFrozenOrHasFrozenParent(node) {
+ try {
+ return node &&
+ (variables.frozen.has(node) ||
+ variables.frozen.has($(node).parentNode));
+ }
+ catch (error) {
+ return false;
+ }
+ }
+
+ function isFrozenAndInsideTarget(node, isInsideTarget) {
+ try {
+ return node &&
+ (variables.frozen.has(node) && isInsideTarget ||
+ variables.frozen.has($(node).parentNode) &&
+ !isInsideTarget);
+ }
+ catch (error) {
+ return false;
+ }
+ }
+
+ function getSnippetData(node) {
+ return variables.frozen.get(node);
+ }
+
+ function getSnippetDataFromNodeOrParent(node) {
+ try {
+ if (variables.frozen.has(node))
+ return variables.frozen.get(node);
+ let parent = $(node).parentNode;
+ return variables.frozen.get(parent);
+ }
+ catch (error) {}
+ }
+
+ function getSnippetDataBasedOnTarget(node, isInsideTarget) {
+ try {
+ if (variables.frozen.has(node) && isInsideTarget)
+ return variables.frozen.get(node);
+ let parent = $(node).parentNode;
+ return variables.frozen.get(parent);
+ }
+ catch (error) {}
+ }
+ }
+
+ function searchAndAttach() {
+ targetNodes = queryAll();
+ markNodes(targetNodes, false);
+ }
+
+ function markNodes(nodes, isChild = true) {
+ for (let node of nodes) {
+ if (!variables.frozen.has(node)) {
+ variables.frozen.set(node, data);
+ if (!isChild && subtree) {
+ new MutationObserver$1(mutationsList => {
+ for (let mutation of $(mutationsList))
+ markNodes($(mutation, "MutationRecord").addedNodes);
+ }).observe(node, {childList: true, subtree: true});
+ }
+ if (subtree && $(node).nodeType === ELEMENT_NODE)
+ markNodes($(node).childNodes);
+ }
+ }
+ }
+
+ function logPrefixed(id, ...args) {
+ log(`[freeze][${id}] `, ...args);
+ }
+
+ function logChange(nodeOrDOMString, target, property, snippetData) {
+ let targetSelector = snippetData.selector;
+ let chgId = snippetData.changeId;
+ let isDOMString = typeof nodeOrDOMString == "string";
+ let action = snippetData.shouldAbort ? "aborting" : "watching";
+ console$2.groupCollapsed(`[freeze][${chgId}] ${action}: ${targetSelector}`);
+ switch (property) {
+ case "appendChild":
+ case "append":
+ case "prepend":
+ case "insertBefore":
+ case "replaceChild":
+ case "insertAdjacentElement":
+ case "insertAdjacentHTML":
+ case "insertAdjacentText":
+ case "innerHTML":
+ case "outerHTML":
+ logPrefixed(chgId,
+ isDOMString ? "text: " : "node: ",
+ nodeOrDOMString);
+ logPrefixed(chgId, "added to node: ", target);
+ break;
+ case "replaceWith":
+ case "after":
+ case "before":
+ logPrefixed(chgId,
+ isDOMString ? "text: " : "node: ",
+ nodeOrDOMString);
+ logPrefixed(chgId, "added to node: ", $(target).parentNode);
+ break;
+ case "textContent":
+ case "innerText":
+ case "nodeValue":
+ logPrefixed(chgId, "content of node: ", target);
+ logPrefixed(chgId, "changed to: ", nodeOrDOMString);
+ break;
+ }
+ logPrefixed(chgId, `using the function "${property}"`);
+ console$2.groupEnd();
+ snippetData.changeId++;
+ }
+
+ function isExceptionNode(element, expSelectors) {
+ if (expSelectors) {
+ let $element = $(element);
+ for (let exception of expSelectors) {
+ if ($element.matches(exception))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function isExceptionText(string, regExceptions) {
+ if (regExceptions) {
+ for (let exception of regExceptions) {
+ if (exception.test(string))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ function abort(id) {
+ throw new ReferenceError(id);
+ }
+
+ function checkHTML(htmlText, parent, property, snippetData) {
+ let domparser = new DOMParser();
+ let {body} = $(domparser.parseFromString(htmlText, "text/html"));
+ let nodes = $(body).childNodes;
+ let accepted = checkMultiple(nodes, parent, property, snippetData);
+ let content = $(accepted).map(node => {
+ switch ($(node).nodeType) {
+ case ELEMENT_NODE:
+ return $(node).outerHTML;
+ case TEXT_NODE:
+ return $(node).textContent;
+ default:
+ return "";
+ }
+ });
+ return content.join("");
+ }
+
+ function checkMultiple(nodesOrDOMStrings, parent, property, snippetData) {
+ let accepted = $([]);
+ for (let nodeOrDOMString of nodesOrDOMStrings) {
+ if (checkShouldInsert(nodeOrDOMString, parent, property, snippetData))
+ accepted.push(nodeOrDOMString);
+ }
+ return accepted;
+ }
+
+ function checkShouldInsert(nodeOrDOMString, parent, property, snippetData) {
+ let aborting = snippetData.shouldAbort;
+ let regExceptions = snippetData.regexExceptions;
+ let expSelectors = snippetData.exceptionSelectors;
+ let id = snippetData.rid;
+ if (typeof nodeOrDOMString == "string") {
+ let domString = nodeOrDOMString;
+ if (isExceptionText(domString, regExceptions))
+ return true;
+ if (debug())
+ logChange(domString, parent, property, snippetData);
+ if (aborting)
+ abort(id);
+ return debug();
+ }
+
+ let node = nodeOrDOMString;
+ switch ($(node).nodeType) {
+ case ELEMENT_NODE:
+ if (isExceptionNode(node, expSelectors))
+ return true;
+ if (aborting) {
+ if (debug())
+ logChange(node, parent, property, snippetData);
+ abort(id);
+ }
+ if (debug()) {
+ hideElement(node);
+ logChange(node, parent, property, snippetData);
+ return true;
+ }
+ return false;
+ case TEXT_NODE:
+ if (isExceptionText($(node).textContent, regExceptions))
+ return true;
+ if (debug())
+ logChange(node, parent, property, snippetData);
+ if (aborting)
+ abort(id);
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ function getAppendChildDescriptor(target, property, shouldValidate,
+ getSnippetData) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let origin = desc.get && call(desc.get, target) || desc.value;
+ if (!origin)
+ return;
+
+ return {
+ get() {
+ return function(...args) {
+ if (shouldValidate(this)) {
+ let snippetData = getSnippetData(this);
+ if (snippetData) {
+ let incomingNode = args[0];
+ if (!checkShouldInsert(incomingNode, this, property, snippetData))
+ return incomingNode;
+ }
+ }
+ return apply$2(origin, this, args);
+ };
+ }
+ };
+ }
+
+ function getAppendDescriptor(
+ target, property, shouldValidate, getSnippetData
+ ) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let origin = desc.get && call(desc.get, target) || desc.value;
+ if (!origin)
+ return;
+ return {
+ get() {
+ return function(...nodesOrDOMStrings) {
+ if (!shouldValidate(this))
+ return apply$2(origin, this, nodesOrDOMStrings);
+
+ let snippetData = getSnippetData(this);
+ if (!snippetData)
+ return apply$2(origin, this, nodesOrDOMStrings);
+
+ let accepted = checkMultiple(
+ nodesOrDOMStrings, this, property, snippetData
+ );
+ if (accepted.length > 0)
+ return apply$2(origin, this, accepted);
+ };
+ }
+ };
+ }
+
+ function getInsertAdjacentDescriptor(
+ target, property, shouldValidate, getSnippetData
+ ) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let origin = desc.get && call(desc.get, target) || desc.value;
+ if (!origin)
+ return;
+
+ return {
+ get() {
+ return function(...args) {
+ let [position, value] = args;
+ let isInsideTarget =
+ position === "afterbegin" || position === "beforeend";
+ if (shouldValidate(this, isInsideTarget)) {
+ let snippetData = getSnippetData(this, isInsideTarget);
+ if (snippetData) {
+ let parent = isInsideTarget ?
+ this :
+ $(this).parentNode;
+ let finalValue;
+ switch (property) {
+ case "insertAdjacentElement":
+ if (!checkShouldInsert(value, parent, property, snippetData))
+ return value;
+ break;
+
+ case "insertAdjacentHTML":
+ finalValue = checkHTML(value, parent, property, snippetData);
+ if (finalValue)
+ return call(origin, this, position, finalValue);
+
+ return;
+
+ case "insertAdjacentText":
+ if (!checkShouldInsert(value, parent, property, snippetData))
+ return;
+ break;
+ }
+ }
+ }
+ return apply$2(origin, this, args);
+ };
+ }
+ };
+ }
+
+ function getInnerHTMLDescriptor(
+ target, property, shouldValidate, getSnippetData
+ ) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let {set: prevSetter} = desc;
+ if (!prevSetter)
+ return;
+
+ return {
+ set(htmlText) {
+ if (!shouldValidate(this))
+ return call(prevSetter, this, htmlText);
+
+ let snippetData = getSnippetData(this);
+ if (!snippetData)
+ return call(prevSetter, this, htmlText);
+ let finalValue = checkHTML(htmlText, this, property, snippetData);
+ if (finalValue)
+ return call(prevSetter, this, finalValue);
+ }
+ };
+ }
+
+ function getTextContentDescriptor(
+ target, property, shouldValidate, getSnippetData
+ ) {
+ let desc = getOwnPropertyDescriptor(target, property) || {};
+ let {set: prevSetter} = desc;
+ if (!prevSetter)
+ return;
+
+ return {
+ set(domString) {
+ if (!shouldValidate(this))
+ return call(prevSetter, this, domString);
+
+ let snippetData = getSnippetData(this);
+ if (!snippetData)
+ return call(prevSetter, this, domString);
+ if (checkShouldInsert(domString, this, property, snippetData))
+ return call(prevSetter, this, domString);
+ }
+ };
+ }
+ }
+
+ $(window);
+
+ function raceWinner(name, lose) {
+
+ return noop;
+ }
+
+ const {Map: Map$4, MutationObserver, Object: Object$4, Set, WeakSet: WeakSet$1} = $(window);
+
+ let ElementProto = Element.prototype;
+ let {attachShadow} = ElementProto;
+
+ let hiddenShadowRoots = new WeakSet$1();
+ let searches = new Map$4();
+ let observer = null;
+
+ function hideIfShadowContains(search, selector = "*") {
+
+ let key = `${search}\\${selector}`;
+ if (!searches.has(key)) {
+ searches.set(key, [toRegExp(search), selector, raceWinner()
+ ]);
+ }
+
+ const debugLog = getDebugger("hide-if-shadow-contain");
+
+ if (!observer) {
+ observer = new MutationObserver(records => {
+ let visited = new Set();
+ for (let {target} of $(records)) {
+
+ let parent = $(target).parentNode;
+ while (parent)
+ [target, parent] = [parent, $(target).parentNode];
+
+ if (hiddenShadowRoots.has(target))
+ continue;
+
+ if (visited.has(target))
+ continue;
+
+ visited.add(target);
+ for (let [re, selfOrParent, win] of searches.values()) {
+ if (re.test($(target).textContent)) {
+ let closest = $(target.host).closest(selfOrParent);
+ if (closest) {
+ win();
+
+ $(target).appendChild(
+ document.createElement("style")
+ ).textContent = ":host {display: none !important}";
+
+ hideElement(closest);
+
+ hiddenShadowRoots.add(target);
+ debugLog("Hiding: ", closest, " for params: ", ...arguments);
+ }
+ }
+ }
+ }
+ });
+
+ Object$4.defineProperty(ElementProto, "attachShadow", {
+
+ value: proxy(attachShadow, function() {
+
+ let root = apply$2(attachShadow, this, arguments);
+ debugLog("attachShadow is called for: ", root);
+
+ observer.observe(root, {
+ childList: true,
+ characterData: true,
+ subtree: true
+ });
+
+ return root;
+ })
+ });
+ }
+ }
+
+ const {Error: Error$4, JSON: JSON$2, Map: Map$3, Object: Object$3} = $(window);
+
+ let paths$1 = null;
+
+ function jsonOverride(rawOverridePaths, value,
+ rawNeedlePaths = "", filter = "") {
+ if (!rawOverridePaths)
+ throw new Error$4("[json-override snippet]: Missing paths to override.");
+
+ if (typeof value == "undefined")
+ throw new Error$4("[json-override snippet]: No value to override with.");
+
+ if (!paths$1) {
+ let debugLog = getDebugger("json-override");
+
+ let {parse} = JSON$2;
+ paths$1 = new Map$3();
+
+ Object$3.defineProperty(window.JSON, "parse", {
+ value: proxy(parse, function(str) {
+ let result = apply$2(parse, this, arguments);
+
+ for (let {prune, needle, filter: flt, value: val} of paths$1.values()) {
+ if (flt && !flt.test(str))
+ continue;
+
+ if ($(needle).some(path => !findOwner(result, path)))
+ return result;
+
+ for (let path of prune) {
+ let details = findOwner(result, path);
+ if (typeof details != "undefined") {
+ debugLog(`Found ${path} replaced it with ${val}`);
+ details[0][details[1]] = overrideValue(val);
+ }
+ }
+ }
+
+ return result;
+ })
+ });
+ debugLog("Wrapped JSON.parse for override");
+ }
+
+ paths$1.set(rawOverridePaths, {
+ prune: $(rawOverridePaths).split(/ +/),
+ needle: rawNeedlePaths.length ? $(rawNeedlePaths).split(/ +/) : [],
+ filter: filter ? toRegExp(filter) : null,
+ value
+ });
+ }
+
+ let {Error: Error$3, JSON: JSON$1, Map: Map$2, Object: Object$2} = $(window);
+
+ let paths = null;
+
+ function jsonPrune(rawPrunePaths, rawNeedlePaths = "") {
+ if (!rawPrunePaths)
+ throw new Error$3("Missing paths to prune");
+
+ if (!paths) {
+ let debugLog = getDebugger("json-prune");
+
+ let {parse} = JSON$1;
+ paths = new Map$2();
+
+ Object$2.defineProperty(window.JSON, "parse", {
+ value: proxy(parse, function() {
+ let result = apply$2(parse, this, arguments);
+
+ for (let {prune, needle} of paths.values()) {
+ if ($(needle).some(path => !findOwner(result, path)))
+ return result;
+
+ for (let path of prune) {
+ let details = findOwner(result, path);
+ if (typeof details != "undefined") {
+ debugLog(`Found ${path} and deleted`);
+ delete details[0][details[1]];
+ }
+ }
+ }
+
+ return result;
+ })
+ });
+ debugLog("Wrapped JSON.parse for prune");
+ }
+
+ paths.set(rawPrunePaths, {
+ prune: $(rawPrunePaths).split(/ +/),
+ needle: rawNeedlePaths.length ? $(rawNeedlePaths).split(/ +/) : []
+ });
+ }
+
+ let {Error: Error$2} = $(window);
+
+ function overridePropertyRead(property, value) {
+ if (!property) {
+ throw new Error$2("[override-property-read snippet]: " +
+ "No property to override.");
+ }
+ if (typeof value === "undefined") {
+ throw new Error$2("[override-property-read snippet]: " +
+ "No value to override with.");
+ }
+
+ let debugLog = getDebugger("override-property-read");
+
+ let cValue = overrideValue(value);
+
+ let newGetter = () => {
+ debugLog(`${property} override done.`);
+ return cValue;
+ };
+
+ debugLog(`Overriding ${property}.`);
+
+ wrapPropertyAccess(window, property, {get: newGetter, set() {}});
+ }
+
+ let {Error: Error$1, Map: Map$1, Object: Object$1, console: console$1} = $(window);
+
+ let {toString} = Function.prototype;
+ let EventTargetProto = EventTarget.prototype;
+ let {addEventListener} = EventTargetProto;
+
+ let events = null;
+
+ function preventListener(event, eventHandler, selector) {
+ if (!event)
+ throw new Error$1("[prevent-listener snippet]: No event type.");
+
+ if (!events) {
+ events = new Map$1();
+
+ let debugLog = getDebugger("[prevent]");
+
+ Object$1.defineProperty(EventTargetProto, "addEventListener", {
+ value: proxy(addEventListener, function(type, listener) {
+ for (let {evt, handlers, selectors} of events.values()) {
+
+ if (!evt.test(type))
+ continue;
+
+ let isElement = this instanceof Element;
+
+ for (let i = 0; i < handlers.length; i++) {
+ let handler = handlers[i];
+ let sel = selectors[i];
+
+ let handlerMatch = () => handler.test(
+ call(
+ toString,
+ typeof listener === "function" ?
+ listener : listener.handleEvent
+ )
+ );
+
+ if (
+ (handler && !handlerMatch()) ||
+ (sel && !(isElement && $(this).matches(sel)))
+ )
+ continue;
+
+ if (debug()) {
+ console$1.groupCollapsed("DEBUG [prevent] was successful");
+ debugLog(`type: ${type} matching ${evt}`);
+ debugLog("handler:", listener);
+ if (handler)
+ debugLog(`matching ${handler}`);
+ if (sel)
+ debugLog("on element: ", this, ` matching ${sel}`);
+ debugLog("was prevented from being added");
+ console$1.groupEnd();
+ }
+ return;
+ }
+ }
+ return apply$2(addEventListener, this, arguments);
+ })
+ });
+
+ debugLog("Wrapped addEventListener");
+ }
+
+ if (!events.has(event))
+ events.set(event, {evt: toRegExp(event), handlers: [], selectors: []});
+
+ let {handlers, selectors} = events.get(event);
+
+ handlers.push(eventHandler ? toRegExp(eventHandler) : null);
+ selectors.push(selector);
+ }
+
+ let {URL, fetch} = $(window);
+
+ let {delete: deleteParam, has: hasParam} = caller(URLSearchParams.prototype);
+
+ let parameters;
+
+ function stripFetchQueryParameter(name, urlPattern = null) {
+ const debugLog = getDebugger("strip-fetch-query-parameter");
+
+ if (!parameters) {
+ parameters = new Map();
+ window.fetch = proxy(fetch, (...args) => {
+ let [source] = args;
+ if (typeof source === "string") {
+ let url = new URL(source);
+ for (let [key, reg] of parameters) {
+ if (!reg || reg.test(source)) {
+ if (hasParam(url.searchParams, key)) {
+ debugLog(`${key} has been stripped from url ${source}`);
+ deleteParam(url.searchParams, key);
+ args[0] = url.href;
+ }
+ }
+ }
+ }
+ return apply$2(fetch, self, args);
+ });
+ }
+ parameters.set(name, urlPattern && toRegExp(urlPattern));
+ }
+
+ function trace(...args) {
+
+ apply$2(log, null, args);
+ }
+
+ const snippets = {
+ "abort-current-inline-script": abortCurrentInlineScript,
+ "abort-on-iframe-property-read": abortOnIframePropertyRead,
+ "abort-on-iframe-property-write": abortOnIframePropertyWrite,
+ "abort-on-property-read": abortOnPropertyRead,
+ "abort-on-property-write": abortOnPropertyWrite,
+ "cookie-remover": cookieRemover,
+ "debug": setDebug,
+ "freeze-element": freezeElement,
+ "hide-if-shadow-contains": hideIfShadowContains,
+ "json-override": jsonOverride,
+ "json-prune": jsonPrune,
+ "override-property-read": overridePropertyRead,
+ "prevent-listener": preventListener,
+ "strip-fetch-query-parameter": stripFetchQueryParameter,
+ "trace": trace
+ };
+ let context;
+ for (const [name, ...args] of filters) {
+ if (snippets.hasOwnProperty(name)) {
+ try { context = snippets[name].apply(context, args); }
+ catch (error) { console.error(error); }
+ }
+ }
+ context = void 0;
+};
+const graph = new Map([["abort-current-inline-script",null],["abort-on-iframe-property-read",null],["abort-on-iframe-property-write",null],["abort-on-property-read",null],["abort-on-property-write",null],["cookie-remover",null],["debug",null],["freeze-element",null],["hide-if-shadow-contains",null],["json-override",null],["json-prune",null],["override-property-read",null],["prevent-listener",null],["strip-fetch-query-parameter",null],["trace",null]]);
+callback.get = snippet => graph.get(snippet);
+callback.has = snippet => graph.has(snippet);
+
+ if (t.every(([name]) => !callback.has(name))) return;
+ const append = () => {
+ URL.revokeObjectURL(
+ Object.assign(
+ document.documentElement.appendChild(document.createElement("script")),
+ {async: false, src: URL.createObjectURL(new Blob([
+ "(" + callback + ")(..." + JSON.stringify([e, ...t]) + ")"
+ ]))}
+ ).src
+ );
+ };
+ try { append(); }
+ catch (_) {
+ document.addEventListener("readystatechange", append, {once:true});
+ }
+}
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -623,6 +623,14 @@ void StyleEngine::UpdateActiveStyleSheetsInShadow(
}
}
+bool StyleEngine::ActiveUserStyleSheetsContainsKey(const StyleSheetKey& injection_key) {
+ for (auto& sheet : injected_user_style_sheets_)
+ if (sheet.first == injection_key)
+ return true;
+
+ return false;
+}
+
void StyleEngine::UpdateActiveUserStyleSheets() {
DCHECK(user_style_dirty_);
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -243,6 +243,7 @@ class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
const ComputedStyle* embedder_style) const;
void ViewportStyleSettingChanged();
+ bool ActiveUserStyleSheetsContainsKey(const StyleSheetKey& injection_key);
void InjectSheet(const StyleSheetKey&,
StyleSheetContents*,
WebCssOrigin = WebCssOrigin::kAuthor);
diff --git a/third_party/blink/renderer/core/exported/web_document.cc b/third_party/blink/renderer/core/exported/web_document.cc
--- a/third_party/blink/renderer/core/exported/web_document.cc
+++ b/third_party/blink/renderer/core/exported/web_document.cc
@@ -274,6 +274,16 @@ WebStyleSheetKey WebDocument::InsertAbpElemhideStylesheet(
Document* document = Unwrap<Document>();
DCHECK(document);
+ const WebStyleSheetKey& injection_key =
+ key && !key->IsNull() ? *key : WebString::FromUTF8("abp");
+ DCHECK(!injection_key.IsEmpty());
+
+ // Check if the css in already present
+ if (document->GetStyleEngine().ActiveUserStyleSheetsContainsKey(injection_key)) {
+ DLOG(WARNING) << "[eyeo] Skip add css as already in collection";
+ return injection_key;
+ }
+
auto* parsed_sheet = MakeGarbageCollected<StyleSheetContents>(
MakeGarbageCollected<CSSParserContext>(*document));
parsed_sheet->ParseString(source_code);
@@ -283,7 +293,7 @@ WebStyleSheetKey WebDocument::InsertAbpElemhideStylesheet(
if (IsValidAbpRule(parsed_sheet->RuleAt(n)))
++n;
else {
- parsed_sheet->SetMutable();
+ parsed_sheet->StartMutation();
parsed_sheet->WrapperDeleteRule(n);
LOG(WARNING) << "[eyeo] Broken rule";
}
@@ -295,9 +305,6 @@ WebStyleSheetKey WebDocument::InsertAbpElemhideStylesheet(
{SchedulingPolicy::DisableBackForwardCache()});
}
- const WebStyleSheetKey& injection_key =
- key && !key->IsNull() ? *key : GenerateStyleSheetKey();
- DCHECK(!injection_key.IsEmpty());
document->GetStyleEngine().InjectSheet(injection_key, parsed_sheet, origin);
return injection_key;
}
diff --git a/tools/typescript/definitions/adblock_private.d.ts b/tools/typescript/definitions/adblock_private.d.ts
--- a/tools/typescript/definitions/adblock_private.d.ts
+++ b/tools/typescript/definitions/adblock_private.d.ts
@@ -33,8 +33,22 @@ declare global {
title: string;
current_version: string;
last_installation_time: string;
+ download_error_count: number;
+ download_success_count: number;
}
+ export function startUpdate(): void;
+
+ export function setPrivilegedFiltersEnabled(
+ enabled: boolean,
+ ): void;
+
+ export function isPrivilegedFiltersEnabled(
+ callback: (
+ result: boolean,
+ ) => void,
+ ): void;
+
export interface SessionStatsEntry {
url: string;
count: number;
--
2.25.1