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

12065 lines
510 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

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);if(n in G)return Se(e,G[n].prototype);if("nodeType"in e)switch(s(xe,e)){case 1:if(!(t in Me))throw new Error("unknown hint "+t);return Me[t](e);case 2:return Me.Attr(e);case 3:return Me.Node(e);case 9:return Me.Document(e)}throw new Error("unknown brand "+n)};var Te=pe?e=>e===window||e===globalThis?G:e:oe(((e,t="Element")=>{if(e===window||e===globalThis)return G;switch(typeof e){case"object":return e&&Ce(e,t);case"string":return new ge(e);case"number":return new me(e);default:throw new Error("unsupported value")}}));let{document:We,getComputedStyle:Le,isExtensionContext:Oe,variables:Pe,Array:De,MutationObserver:Ne,Object:Ae,XPathEvaluator:Ie,XPathExpression:Re,XPathResult:Ve}=Te(window),{querySelectorAll:$e}=We,He=$e&&i($e,We);const{assign:_e,setPrototypeOf:je}=Ae;class Xe extends Re{evaluate(...e){return je(r(super.evaluate,this,e),Ve.prototype)}}class Fe extends Ie{createExpression(...e){return je(r(super.createExpression,this,e),Xe.prototype)}}function qe(e){if(Pe.hidden.has(e))return;!function(e){Oe&&"function"==typeof checkElement&&checkElement(e)}(e),Pe.hidden.add(e);let{style:t}=Te(e),n=Te(t,"CSSStyleDeclaration"),o=Te([]),{debugCSSProperties:r}=S;for(let[e,t]of r||[["display","none"]])n.setProperty(e,t,"important"),o.push([e,n.getPropertyValue(e)]);new Ne((()=>{for(let[e,t]of o){let o=n.getPropertyValue(e),r=n.getPropertyPriority(e);o==t&&"important"==r||n.setProperty(e,t,"important")}})).observe(e,{attributes:!0,attributeFilter:["style"]})}function Be(e){let t=e;if(t.startsWith("xpath(")&&t.endsWith(")")){let e=t.slice(6,-1),n=(new Fe).createExpression(e,null),o=Ve.ORDERED_NODE_SNAPSHOT_TYPE;return e=>{if(!e)return;let t=n.evaluate(We,o,null),{snapshotLength:r}=t;for(let n=0;n<r;n++)e(t.snapshotItem(n))}}return t=>He(e).forEach(t)}function ze(e,t,n,o){let r;null==n&&(n=t);const i=()=>{for(const i of He(n)){const n=Te(i).closest(t);n&&e(i,n)&&(r(),qe(n),"function"==typeof o&&o(n))}};return _e(new Ne(i),{race(e){r=e,this.observe(We,{childList:!0,characterData:!0,subtree:!0}),i()}})}function Ue(e,t,n){let o=Te(t,"CSSStyleDeclaration");if("none"==o.getPropertyValue("display"))return!1;let r=o.getPropertyValue("visibility");if("hidden"==r||"collapse"==r)return!1;if(!n||e==n)return!0;let i=Te(e).parentElement;return!i||Ue(i,Le(i),n)}function Ge(e){let t=Le(e),{cssText:n}=t;if(n)return n;for(let e of t)n+=`${e}: ${t[e]}; `;return Te(n).trim()}let{Math:Je,RegExp:Qe}=Te(window);function Ye(e){let{length:t}=e;if(t>1&&"/"===e[0]){let n="/"===e[t-1];if(n||t>2&&Te(e).endsWith("/i")){let t=[Te(e).slice(1,n?-1:-2)];return n||t.push("i"),new Qe(...t)}}return new Qe(Te(e).replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))}let Ke=!1;function Ze(){return Ke}const{console:et}=Te(window),tt=()=>{};function nt(...e){Ze()&&Te(e).unshift("%c DEBUG","font-weight: bold"),et.log(...e)}function ot(e){return i(Ze()?nt:tt,null,e)}let{Array:rt,Error:it,Map:st,parseInt:at}=Te(window),ct=null,lt=null;function ut(e,t){if(null===ct)return tt;let n=ct,{participants:o}=n;return o.set(r,t),r;function r(){if(n.winners<1)return;if(ot("race")(`${e} won the race`),n===ct)lt.push(r);else if(o.delete(r),--n.winners<1){for(let e of o.values())e();o.clear()}}}const dt={get(e,t){const n=e;for(;!g(e,t);)e=m(e);const{get:o,set:i}=p(e,t);return function(){return arguments.length?r(i,n,arguments):s(o,n)}}};var ht;function ft(e,t,n){var o,r;n?"load"===n?(e("Waiting until window.load"),window.onload=()=>{e("Window.load fired."),t()}):"loading"===n||"interactive"===n||"complete"===n?(e("Waiting document state until :",n),document.onreadystatechange=()=>{e("Document state changed:",document.readyState),document.readyState===n&&t()}):(e("Waiting until ",n," event is triggered on document"),(o=document,r=n,new Promise((e=>{const t=()=>{o.removeEventListener(r,t),e()};o.addEventListener(r,t)}))).then((()=>{e(n," is triggered on document, starting the snippet"),t()})).catch((t=>{e("There was an error while waiting for the event.",t)}))):t()}Te(window),ht=window,new e(ht,dt),Te(/^\d+$/);let{MutationObserver:pt,WeakSet:wt,getComputedStyle:mt}=Te(window);let{clearTimeout:gt,fetch:bt,getComputedStyle:yt,setTimeout:vt,Map:St,MutationObserver:kt,Uint8Array:Et}=Te(window);let xt=new St;function Mt(e,{as:t="arrayBuffer",cleanup:n=6e4}={}){let o=t+":"+e,r=xt.get(o)||{remove:()=>xt.delete(o),result:null,timer:0};return gt(r.timer),r.timer=vt(r.remove,n),r.result||(r.result=bt(e).then((e=>e[t]())).catch(r.remove),xt.set(o,r)),r.result}function Ct(e){return e.reduce(((e,t)=>e+function(e,t=2){let n=Te(e).toString(16);return n.length<t&&(n=Te("0").repeat(t-n.length)+n),n}(t)),"")}const{parseFloat:Tt,Math:Wt,MutationObserver:Lt,WeakSet:Ot}=Te(window),{min:Pt}=Wt,Dt=(e,t)=>{const n=e.length+1,o=t.length+1,r=[[0]];let i=0,s=0;for(;++i<o;)r[0][i]=i;for(i=0;++i<n;){const n=e[s];let a=0,c=0;for(r[i]=[i];++a<o;)r[i][a]=Pt(r[s][a]+1,r[i][c]+1,r[s][c]+(n!=t[c])),++c;++s}return r[n-1][o-1]};let{getComputedStyle:Nt,Map:At,WeakSet:It,parseFloat:Rt}=Te(window);const{ELEMENT_NODE:Vt,TEXT_NODE:$t}=Node;let{MutationObserver:Ht,WeakSet:_t,getComputedStyle:jt}=Te(window);let{getComputedStyle:Xt,MutationObserver:Ft,WeakSet:qt}=Te(window);Te(window);const Bt={mark(){},end(){},toString:()=>"{mark(){},end(){}}"};function zt(e,t=10){return Bt}let{MutationObserver:Ut,WeakSet:Gt}=Te(window);const{ELEMENT_NODE:Jt}=Node;let{MutationObserver:Qt,WeakSet:Yt}=Te(window);const{ELEMENT_NODE:Kt}=Node;let{parseInt:Zt,setTimeout:en,Error:tn,MouseEvent:nn,MutationObserver:on,WeakSet:rn}=Te(window);let{parseInt:sn,setTimeout:an,Error:cn,MouseEvent:ln,MutationObserver:un,WeakSet:dn}=Te(window);const hn=["auxclick","click","dblclick","gotpointercapture","lostpointercapture","mouseenter","mousedown","mouseleave","mousemove","mouseout","mouseover","mouseup","pointerdown","pointerenter","pointermove","pointerover","pointerout","pointerup","pointercancel","pointerleave"];const fn={log:nt,race:function(e,t="1"){switch(e){case"start":ct={winners:at(t,10)||1,participants:new st},lt=new rt;break;case"end":case"finish":case"stop":ct=null;for(let e of lt)e();lt=null;break;default:throw new it(`Invalid action: ${e}`)}},debug:function(){Ke=!0},"hide-if-matches-xpath":function(e,t){const{mark:n,end:o}=zt(),r=ot("hide-if-matches-xpath"),i=t=>{const i=Be(`xpath(${e})`),s=new Gt,a=()=>{n(),i((t=>{if(s.has(t))return!1;s.add(t),l(),Te(t).nodeType===Jt?qe(t):Te(t).textContent="",r("Matched: ",t," for selector: ",e)})),o()},c=new Ut(a),l=ut("hide-if-matches-xpath",(()=>c.disconnect()));c.observe(t,{characterData:!0,childList:!0,subtree:!0}),a()};if(t){let e,n=0;const o=Be(`xpath(${t})`),r=()=>{o((e=>{i(e),n++})),n>0&&e.disconnect()};e=new Ut(r),e.observe(document,{characterData:!0,childList:!0,subtree:!0}),r()}else i(document)},"hide-if-matches-computed-xpath":function(e,t,n,o){const{mark:r,end:i}=zt(),s=ot("hide-if-matches-computed-xpath");if(!t||!e)return void s("No query or searchQuery provided.");const a=t=>{const n=(t=>e.replace("{{}}",t))(t);s("Starting hiding elements that match query: ",n);const o=Be(`xpath(${n})`),a=new Yt,c=()=>{r(),o((t=>{if(a.has(t))return!1;a.add(t),u(),Te(t).nodeType===Kt?qe(t):Te(t).textContent="",s("Matched: ",t," for selector: ",e)})),i()},l=new Qt(c),u=ut("hide-if-matches-computed-xpath",(()=>l.disconnect()));l.observe(document,{characterData:!0,childList:!0,subtree:!0}),c()},c=Ye(n);ft(s,(()=>{if(t){s("Started searching for: ",t);const e=new Yt;let n;const o=Be(`xpath(${t})`),r=()=>{o((t=>{if(e.has(t))return!1;if(e.add(t),s("Found node: ",t),t.innerHTML){s("Searching in: ",t.innerHTML);const e=t.innerHTML.match(c);if(e&&e.length){let t="";t=e[1]?e[1]:e[0],s("Matched search query: ",t),a(t)}}}))};n=new Qt(r),n.observe(document,{characterData:!0,childList:!0,subtree:!0}),r()}}),o)},"hide-if-contains":function(e,t="*",n=null){const o=ot("hide-if-contains");let r=Ye(e);const i=ze((e=>r.test(Te(e).textContent)),t,n,(e=>{o("Matched: ",e," for selector: ",t,n)}));i.race(ut("hide-if-contains",(()=>{i.disconnect()})))},"hide-if-contains-similar-text":function(e,t,n=null,o=0,r=0){const i=new Ot,s=ot("hide-if-contains-similar-text"),a=Te(e),{length:c}=a,l=c+Tt(o)||0,u=Te([...a]).sort(),d=Tt(r)||1/0;null==n&&(n=t),s("Looking for similar text: "+a);const h=()=>{for(const e of He(n)){if(i.has(e))continue;i.add(e);const{innerText:n}=Te(e),r=Pt(d,n.length-l+1);for(let i=0;i<r;i++){const r=Te(n).substr(i,l);if(Dt(u,Te([...r]).sort())-o<=0){const n=Te(e).closest(t);if(s("Found similar text: "+a,n),n){p(),qe(n);break}}}}};let f=new Lt(h),p=ut("hide-if-contains-similar-text",(()=>f.disconnect()));f.observe(document,{childList:!0,characterData:!0,subtree:!0}),h()},"hide-if-contains-visible-text":function(e,t,n=null,...o){let r=Te([]);const i=new At([["-snippet-box-margin","2"],["-disable-bg-color-check","false"],["-check-is-contained","false"]]);for(let e of o){e=Te(e);let t=e.indexOf(":");if(t<0)continue;let n=e.slice(0,t).trim().toString(),o=e.slice(t+1).trim().toString();n&&o&&(i.has(n)?i.set(n,o):r.push([n,o]))}let s=Te([["opacity","0"],["font-size","0px"],["color","rgba(0, 0, 0, 0)"]]),a=new At(s.concat(r));function c(e,t,{bgColorCheck:n=!0}={}){t||(t=Nt(e)),t=Te(t);for(const[e,n]of a){if(Ye(n).test(t.getPropertyValue(e)))return!1}let o=t.getPropertyValue("color");return!n||t.getPropertyValue("background-color")!=o}function l(e,t,{bgColorCheck:n=!0}={}){let o=Nt(e,t);if(!Ue(e,o)||!c(e,o,{bgColorCheck:n}))return"";let{content:r}=Te(o);if(r&&"none"!==r){let t=Te([]);return r=Te(r).trim().replace(/(["'])(?:(?=(\\?))\2.)*?\1/g,(e=>""+(t.push(Te(e).slice(1,-1))-1))),r=r.replace(/\s*attr\(\s*([^\s,)]+)[^)]*?\)\s*/g,((t,n)=>Te(e).getAttribute(n)||"")),r.replace(/\x01(\d+)/g,((e,n)=>t[n]))}return""}function u(e,t,{boxMargin:n=2}={}){const o=Te(e).getBoundingClientRect(),r=Te(t).getBoundingClientRect(),i=r.left-n,s=r.right+n,a=r.top-n,c=r.bottom+n;return i<=o.left&&o.left<=s&&a<=o.top&&o.top<=c&&a<=o.bottom&&o.bottom<=c&&i<=o.right&&o.right<=s}function d(e,t,n,o,r,{boxMargin:i=2,bgColorCheck:s,checkIsContained:a}={}){let h=!n;if(h&&(n=Nt(e)),!Ue(e,n,h&&t))return"";o||"hidden"!==Te(n).getPropertyValue("overflow-x")&&"hidden"!==Te(n).getPropertyValue("overflow-y")||(o=e);let f=l(e,":before",{bgColorCheck:s});for(let t of Te(e).childNodes)switch(Te(t).nodeType){case Vt:f+=d(t,e,Nt(t),o,r,{boxMargin:i,bgColorCheck:s,checkIsContained:a});break;case $t:if(o)u(e,o,{boxMargin:i})&&c(e,n,{bgColorCheck:s})&&(f+=Te(t).nodeValue);else if(c(e,n,{bgColorCheck:s})){if(a&&!u(e,r,{boxMargin:i}))continue;f+=Te(t).nodeValue}}return f+l(e,":after",{bgColorCheck:s})}const h=i.get("-snippet-box-margin"),f=Rt(h)||0,p=!("true"===i.get("-disable-bg-color-check")),w="true"===i.get("-check-is-contained");let m=Ye(e),g=new It;const b=ze(((e,t)=>{if(g.has(e))return!1;g.add(e);let n=d(e,t,null,null,e,{boxMargin:f,bgColorCheck:p,checkIsContained:w}),o=m.test(n);return Ze()&&n.length&&nt(o,m,n),o}),t,n);b.race(ut("hide-if-contains-visible-text",(()=>{b.disconnect()})))},"hide-if-contains-and-matches-style":function(e,t="*",n=null,o=null,r=null,i,s=null,a=null){const c=ot("hide-if-contains-and-matches-style"),l=new wt;null==n&&(n=t);const u=Ye(e),d=o?Ye(o):null,h=r?Ye(r):null,f=()=>{const e=()=>{if(!(s&&window.innerWidth<s||a&&window.innerWidth>a))for(const e of He(n))if(!l.has(e)&&u.test(Te(e).textContent))if(!h||h.test(Ge(e))){const n=Te(e).closest(t);if(!n)continue;!d||d.test(Ge(n))?(r(),qe(n),l.add(e),c("Matched: ",n,"which contains: ",e," for params: ",...arguments)):c("In this element the searchStyle matched but style didn't:\n",n,mt(n),...arguments)}else c("In this element the searchStyle didn't match:\n",e,mt(e),...arguments)},o=new pt(e),r=ut("hide-if-contains-and-matches-style",(()=>o.disconnect()));o.observe(document,{childList:!0,characterData:!0,subtree:!0}),e()};ft(c,f,i)},"hide-if-has-and-matches-style":function(e,t="*",n=null,o=null,r=null,i=null,s=null,a=null){const c=ot("hide-if-has-and-matches-style"),l=new _t;null==n&&(n=t);const u=o?Ye(o):null,d=r?Ye(r):null,h=()=>{const o=()=>{if(!(s&&window.innerWidth<s||a&&window.innerWidth>a))for(const o of He(n))if(!l.has(o))if(!Te(o).querySelector(e)||d&&!d.test(Ge(o)))c("In this element the searchStyle didn't match:\n",o,jt(o),...arguments);else{const e=Te(o).closest(t);!e||u&&!u.test(Ge(e))?c("In this element the searchStyle matched but style didn't:\n",e,jt(e),...arguments):(i(),qe(e),l.add(o),c("Matched: ",e,"which contains: ",o," for params: ",...arguments))}},r=new Ht(o),i=ut("hide-if-has-and-matches-style",(()=>r.disconnect()));r.observe(document,{childList:!0,subtree:!0}),o()};ft(c,h,i)},"hide-if-labelled-by":function(e,t,n=null){let o=null==n,r=Ye(e),i=new qt,s=()=>{for(let e of He(t)){let t=o?e:Te(e).closest(n);if(!t||!Ue(e,Xt(e),t))continue;let s=Te(e).getAttribute("aria-labelledby"),a=()=>{i.has(t)||r.test(Te(e).getAttribute("aria-label")||"")&&(c(),i.add(t),qe(t))};if(s)for(let e of Te(s).split(/\s+/)){let n=Te(document).getElementById(e);n?!i.has(n)&&r.test(n.innerText)&&(c(),i.add(n),qe(t)):a()}else a()}},a=new Ft(s),c=ut("hide-if-labelled-by",(()=>a.disconnect()));a.observe(document,{characterData:!0,childList:!0,subtree:!0}),s()},"hide-if-contains-image":function(e,t,n){null==n&&(n=t);let o=Ye(e);const r=ot("hide-if-contains-image");let i=()=>{for(let e of He(n)){let n=yt(e),i=Te(n["background-image"]).match(/^url\("(.*)"\)$/);i&&Mt(i[1]).then((n=>{if(o.test(Ct(new Et(n)))){let n=Te(e).closest(t);n&&(a(),qe(n),r("Matched: ",n," for:",...arguments))}}))}},s=new kt(i),a=ut("hide-if-contains-image",(()=>s.disconnect()));s.observe(document,{childList:!0,subtree:!0}),i()},"simulate-event-poc":function(e,t,n="0"){if(!e)throw new tn("[simulate-event snippet]: No event type provided.");if(!t)throw new tn("[simulate-event snippet]: No selector provided.");let o=Be(t),r=Zt(n,10),i=new rn;function s(){o((t=>{i.has(t)||(i.add(t),en((()=>{Te(t).dispatchEvent(new nn(e,{bubbles:!0,cancelable:!0}))}),r))}))}new on(s).observe(document,{childList:!0,subtree:!0}),s()},"simulate-mouse-event":function(...e){const t=ot("simulate-mouse-event");if(e.length<1)throw new cn("[simulate-mouse-event snippet]: No selector provided.");e.length>7&&(e=e.slice(0,7));const n=Te([]);function o(){return n.forEach((e=>{if(!e.found){(function(e){let t=e;if(t.startsWith("xpath(")&&t.endsWith(")")){let t=Be(e);return()=>{let e=Te([]);return t((t=>e.push(t))),e}}return()=>De.from(He(e))})(e.selector)().length>0&&(e.found=!0)}})),n.every((e=>e.found))}function r(e,n,o){e&&n&&("click"===n&&e.click?(e.click(),t("Clicked on this node:\n",e,"\nwith a delay of",o,"ms")):(e.dispatchEvent(new ln(n,{bubbles:!0,cancelable:!0})),t("A",n,"event was dispatched with a delay of",o,"ms on this node:\n",e)))}Te(e).forEach((e=>{const o=function(e){if(!e)return null;const n={selector:"",continue:!1,trigger:!1,event:"click",delay:"500",clicked:!1,found:!1},o=e.split("$");let r=[];o.length>=2&&(r=o[1].toLowerCase().split(",")),[n.selector]=o;for(const e of r)if("trigger"===e)n.trigger=!0;else if("continue"===e)n.continue=!0;else if(e.startsWith("event")){const t=e.toLowerCase().split("=");t[1]?n.event=t[1]:n.event="click"}else if(e.startsWith("delay")){const t=e.toLowerCase().split("=");t[1]?n.delay=t[1]:n.delay="500"}return hn.includes(n.event)||t(n.event," might be misspelled, check for typos.\n","These are the supported events:",hn),n}(e);n.push(o)}));let i=!1;const[s]=n.slice(-1);s.trigger=!0;let a=new dn;function c(){if(i||(i=o()),i)for(const e of n){const t=Be(e.selector),n=sn(e.delay,10);e.trigger&&t((t=>{a.has(t)||(a.add(t),e.continue?setInterval((()=>{r(t,e.event,e.delay)}),n):an((()=>{r(t,e.event,e.delay)}),n))}))}}new un(c).observe(document,{childList:!0,subtree:!0}),c()}};let{MutationObserver:pn}=Te(window);const{ELEMENT_NODE:wn}=Node;fn["hide-if-matches-xpath3"]=function(e,t){let{mark:n,end:o}=zt();function r(){return fontoxpath.evaluateXPathToNodes(e,document,null,null,{language:fontoxpath.evaluateXPath.XQUERY_3_1_LANGUAGE})}let i=ot("hide-if-matches-xpath3");const s=t=>{const s=new WeakSet,a=()=>{n();const t=r();for(const n of Te(t)){if(s.has(n))return!1;s.add(n),l(),Te(n).nodeType===wn?qe(n):Te(n).textContent="",i("Matched: ",n," for selector: ",e)}o()},c=new pn(a),l=ut("hide-if-matches-xpath3",(()=>c.disconnect()));c.observe(t,{characterData:!0,childList:!0,subtree:!0}),a()};if(t){let e,t=0;const n=r(),o=()=>{for(const e of Te(n))s(e),t++;t>0&&e.disconnect()};e=new pn(o),e.observe(document,{characterData:!0,childList:!0,subtree:!0}),o()}else s(document)};
+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(Te,s(Me,e),8,-1))(e);if(r in Oe)return Oe[r](e);if(r in G)return Se(e,G[r].prototype);if("nodeType"in e)switch(s(xe,e)){case 1:if(!(t in Oe))throw new Error("unknown hint "+t);return Oe[t](e);case 2:return Oe.Attr(e);case 3:return Oe.Node(e);case 9:return Oe.Document(e)}throw new Error("unknown brand "+r)};var je=we?e=>e===window||e===globalThis?G:e:oe(((e,t="Element")=>{if(e===window||e===globalThis)return G;switch(typeof e){case"object":return e&&Pe(e,t);case"string":return new me(e);case"number":return new ye(e);default:throw new Error("unsupported value")}}));const Ne={get(e,t){const r=e;for(;!m(e,t);)e=y(e);const{get:n,set:i}=w(e,t);return function(){return arguments.length?o(i,r,arguments):s(n,r)}}},Le=t=>new e(t,Ne);let ke=!1;function Ce(){return ke}const{console:Ae}=je(window),We=()=>{};function $e(...e){Ce()&&je(e).unshift("%c DEBUG","font-weight: bold"),Ae.log(...e)}function De(e){return i(Ce()?$e:We,null,e)}let{Math:He,RegExp:ze}=je(window);function Re(e){let{length:t}=e;if(t>1&&"/"===e[0]){let r="/"===e[t-1];if(r||t>2&&je(e).endsWith("/i")){let t=[je(e).slice(1,r?-1:-2)];return r||t.push("i"),new ze(...t)}}return new ze(je(e).replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))}function Fe(){return je(He.floor(2116316160*He.random()+60466176)).toString(36)}let{parseFloat:Ie,variables:Je,Array:Ve,Error:Be,Map:Ue,Object:_e,ReferenceError:Xe,Set:qe,WeakMap:Ge}=je(window),{onerror:Ke}=Le(window),Ye=Node.prototype,Ze=Element.prototype,Qe=null;function et(e,t,r,n=!0){let o=je(t),i=o.indexOf(".");if(-1==i){let o=_e.getOwnPropertyDescriptor(e,t);if(o&&!o.configurable)return;let i=_e.assign({},r,{configurable:n});if(!o&&!i.get&&i.set){let r=e[t];i.get=()=>r}return void _e.defineProperty(e,t,i)}let s=o.slice(0,i).toString();t=o.slice(i+1).toString();let a=e[s];!a||"object"!=typeof a&&"function"!=typeof a||et(a,t,r);let l=_e.getOwnPropertyDescriptor(e,s);if(l&&!l.configurable)return;Qe||(Qe=new Ge),Qe.has(e)||Qe.set(e,new Ue);let c=Qe.get(e);if(c.has(s))return void c.get(s).set(t,r);let u=new Ue([[t,r]]);c.set(s,u),_e.defineProperty(e,s,{get:()=>a,set(e){if(a=e,a&&("object"==typeof a||"function"==typeof a))for(let[e,t]of u)et(a,e,t)},configurable:n})}function tt(e){let t=Ke();Ke(((...r)=>{let n=r.length&&r[0];return!("string"!=typeof n||!je(n).includes(e))||("function"==typeof t?o(t,this,r):void 0)}))}function rt(e,t,r,n=!0){let o=De(e);if(!r)return void o("no property to abort on read");let i=Fe();o(`aborting on ${r} access`),et(t,r,{get:function(){throw o(`${r} access aborted`),new Xe(i)},set(){}},n),tt(i)}function nt(e,t,r,n=!0){let o=De(e);if(!r)return void o("no property to abort on write");let i=Fe();o(`aborting when setting ${r}`),et(t,r,{set:function(){throw o(`setting ${r} aborted`),new Xe(i)}},n),tt(i)}function ot(e,t=!1,r=!1){let n=Je.abortedIframes,i=Je.iframePropertiesToAbort;for(let o of Ve.from(window.frames))if(n.has(o))for(let i of e)t&&n.get(o).read.add(i),r&&n.get(o).write.add(i);for(let n of e)t&&i.read.add(n),r&&i.write.add(n);function a(){for(let e of Ve.from(window.frames)){n.has(e)||n.set(e,{read:new qe(i.read),write:new qe(i.write)});let t=n.get(e).read;if(t.size>0){let r=Ve.from(t);t.clear();for(let t of r)rt("abort-on-iframe-property-read",e,t)}let r=n.get(e).write;if(r.size>0){let t=Ve.from(r);r.clear();for(let r of t)nt("abort-on-iframe-property-write",e,r)}}}a(),n.has(document)||(n.set(document,!0),function(e){let t;function r(e,t){for(let r of t){et(e,r,n(e,r))}}function n(t,r){let n=t[r];return{get:()=>function(...t){let r;return r=o(n,this,t),e&&e(),r}}}function i(t,r){let n=_e.getOwnPropertyDescriptor(t,r),{set:o}=n||{};return{set(t){let r;return r=s(o,this,t),e&&e(),r}}}r(Ye,["appendChild","insertBefore","replaceChild"]),r(Ze,["append","prepend","replaceWith","after","before","insertAdjacentElement","insertAdjacentHTML"]),t=i(Ze,"innerHTML"),et(Ze,"innerHTML",t),t=i(Ze,"outerHTML"),et(Ze,"outerHTML",t)}(a))}let{Object:it}=window;function st(e,t){if(!(e instanceof it))return;let r=e,n=je(t).split(".");if(0===n.length)return;for(let e=0;e<n.length-1;e++){let t=n[e];if(!m(r,t))return;if(r=r[t],!(r instanceof it))return}let o=n[n.length-1];return m(r,o)?[r,o]:void 0}const at=je(/^\d+$/);function lt(e){switch(e){case"false":return!1;case"true":return!0;case"null":return null;case"noopFunc":return()=>{};case"trueFunc":return()=>!0;case"falseFunc":return()=>!1;case"emptyArray":return[];case"emptyObj":return{};case"undefined":return;case"":return e;default:if(at.test(e))return Ie(e);throw new Be(`[override-property-read snippet]: Value "${e}" is not valid.`)}}let{HTMLScriptElement:ct,Object:ut,ReferenceError:ft}=je(window),pt=ut.getPrototypeOf(ct);let{Error:dt}=je(window),{cookie:ht}=Le(document);let{document:wt,getComputedStyle:gt,isExtensionContext:yt,variables:mt,Array:bt,MutationObserver:vt,Object:Et,XPathEvaluator:St,XPathExpression:Mt,XPathResult:Tt}=je(window),{querySelectorAll:xt}=wt,Ot=xt&&i(xt,wt);const{assign:Pt,setPrototypeOf:jt}=Et;class Nt extends Mt{evaluate(...e){return jt(o(super.evaluate,this,e),Tt.prototype)}}class Lt extends St{createExpression(...e){return jt(o(super.createExpression,this,e),Nt.prototype)}}function kt(e){if(mt.hidden.has(e))return;!function(e){yt&&"function"==typeof checkElement&&checkElement(e)}(e),mt.hidden.add(e);let{style:t}=je(e),r=je(t,"CSSStyleDeclaration"),n=je([]),{debugCSSProperties:o}=S;for(let[e,t]of o||[["display","none"]])r.setProperty(e,t,"important"),n.push([e,r.getPropertyValue(e)]);new vt((()=>{for(let[e,t]of n){let n=r.getPropertyValue(e),o=r.getPropertyPriority(e);n==t&&"important"==o||r.setProperty(e,t,"important")}})).observe(e,{attributes:!0,attributeFilter:["style"]})}function Ct(e){let t=e;if(t.startsWith("xpath(")&&t.endsWith(")")){let t=function(e){let t=e;if(t.startsWith("xpath(")&&t.endsWith(")")){let e=t.slice(6,-1),r=(new Lt).createExpression(e,null),n=Tt.ORDERED_NODE_SNAPSHOT_TYPE;return e=>{if(!e)return;let t=r.evaluate(wt,n,null),{snapshotLength:o}=t;for(let r=0;r<o;r++)e(t.snapshotItem(r))}}return t=>Ot(e).forEach(t)}(e);return()=>{let e=je([]);return t((t=>e.push(t))),e}}return()=>bt.from(Ot(e))}let{ELEMENT_NODE:At,TEXT_NODE:Wt,prototype:$t}=Node,{prototype:Dt}=Element,{prototype:Ht}=HTMLElement,{console:zt,variables:Rt,DOMParser:Ft,Error:It,MutationObserver:Jt,Object:Vt,ReferenceError:Bt}=je(window),{getOwnPropertyDescriptor:Ut}=Vt;function _t(e,t){return We}je(window);const{Map:Xt,MutationObserver:qt,Object:Gt,Set:Kt,WeakSet:Yt}=je(window);let Zt=Element.prototype,{attachShadow:Qt}=Zt,er=new Yt,tr=new Xt,rr=null;const{Error:nr,JSON:or,Map:ir,Object:sr}=je(window);let ar=null;let{Error:lr,JSON:cr,Map:ur,Object:fr}=je(window),pr=null;let{Error:dr}=je(window);let{Error:hr,Map:wr,Object:gr,console:yr}=je(window),{toString:mr}=Function.prototype,br=EventTarget.prototype,{addEventListener:vr}=br,Er=null;let Sr,{URL:Mr,fetch:Tr}=je(window),{delete:xr,has:Or}=l(URLSearchParams.prototype);const Pr={"abort-current-inline-script":function(e,t=null){const r=De("abort-current-inline-script"),n=t?Re(t):null,o=Fe(),i=je(document).currentScript;let a=window;const l=je(e).split("."),c=je(l).pop();for(let e of je(l))if(a=a[e],!a||"object"!=typeof a&&"function"!=typeof a)return void r(l," is not found");const{get:u,set:f}=ut.getOwnPropertyDescriptor(a,c)||{};let p=a[c];void 0===p&&r("The property",c,"doesn't exist yet. Check typos.");const d=()=>{const e=je(document).currentScript;if(e instanceof pt&&""==je(e,"HTMLScriptElement").src&&e!=i&&(!n||n.test(je(e).textContent)))throw r(l," is aborted \n",e),new ft(o)};et(a,c,{get(){return d(),u?s(u,this):p},set(e){d(),f?s(f,this,e):p=e}}),tt(o)},"abort-on-iframe-property-read":function(...e){ot(e,!0,!1)},"abort-on-iframe-property-write":function(...e){ot(e,!1,!0)},"abort-on-property-read":function(e,t){rt("abort-on-property-read",window,e,!("false"===t))},"abort-on-property-write":function(e,t){nt("abort-on-property-write",window,e,!("false"===t))},"cookie-remover":function(e){if(!e)throw new dt("[cookie-remover snippet]: No cookie to remove.");let t=De("cookie-remover"),r=Re(e);if(je(/^http|^about/).test(location.protocol)){t("Parsing cookies for matches");for(const e of je(je(ht()).split(";").filter((e=>r.test(je(e).split("=")[0]))))){let r=je(location.hostname),n=je(e).split("=")[0],o="expires=Thu, 01 Jan 1970 00:00:00 GMT",i="path=/",s="domain="+r.slice(r.indexOf(".")+1);ht(`${je(n).trim()}=;${o};${i};${s}`),t(`Set expiration date on ${n}`)}}else t("Snippet only works for http or https and about.")},debug:function(){ke=!0},"freeze-element":function(e,t="",...r){let n,i,a=!1,l=!1,c=je(r).filter((e=>!h(e))),u=je(r).filter((e=>h(e))).map(Re),f=Fe(),p=Ct(e);!function(){let r=je(t).split("+");1===r.length&&""===r[0]&&(r=[]);for(let t of r)switch(t){case"subtree":a=!0;break;case"abort":l=!0;break;default:throw new It("[freeze] Unknown option passed to the snippet. [selector]: "+e+" [option]: "+t)}}();let d={selector:e,shouldAbort:l,rid:f,exceptionSelectors:c,regexExceptions:u,changeId:0};function h(e){return e.length>=2&&"/"==e[0]&&"/"==e[e.length-1]}function w(){i=p(),g(i,!1)}function g(e,t=!0){for(let r of e)Rt.frozen.has(r)||(Rt.frozen.set(r,d),!t&&a&&new Jt((e=>{for(let t of je(e))g(je(t,"MutationRecord").addedNodes)})).observe(r,{childList:!0,subtree:!0}),a&&je(r).nodeType===At&&g(je(r).childNodes))}function y(e,...t){$e(`[freeze][${e}] `,...t)}function m(e,t,r,n){let o=n.selector,i=n.changeId,s="string"==typeof e,a=n.shouldAbort?"aborting":"watching";switch(zt.groupCollapsed(`[freeze][${i}] ${a}: ${o}`),r){case"appendChild":case"append":case"prepend":case"insertBefore":case"replaceChild":case"insertAdjacentElement":case"insertAdjacentHTML":case"insertAdjacentText":case"innerHTML":case"outerHTML":y(i,s?"text: ":"node: ",e),y(i,"added to node: ",t);break;case"replaceWith":case"after":case"before":y(i,s?"text: ":"node: ",e),y(i,"added to node: ",je(t).parentNode);break;case"textContent":case"innerText":case"nodeValue":y(i,"content of node: ",t),y(i,"changed to: ",e)}y(i,`using the function "${r}"`),zt.groupEnd(),n.changeId++}function b(e,t){if(t)for(let r of t)if(r.test(e))return!0;return!1}function v(e){throw new Bt(e)}function E(e,t,r,n){let o=new Ft,{body:i}=je(o.parseFromString(e,"text/html")),s=S(je(i).childNodes,t,r,n);return je(s).map((e=>{switch(je(e).nodeType){case At:return je(e).outerHTML;case Wt:return je(e).textContent;default:return""}})).join("")}function S(e,t,r,n){let o=je([]);for(let i of e)M(i,t,r,n)&&o.push(i);return o}function M(e,t,r,n){let o=n.shouldAbort,i=n.regexExceptions,s=n.exceptionSelectors,a=n.rid;if("string"==typeof e){let s=e;return!!b(s,i)||(Ce()&&m(s,t,r,n),o&&v(a),Ce())}let l=e;switch(je(l).nodeType){case At:return!!function(e,t){if(t){let r=je(e);for(let e of t)if(r.matches(e))return!0}return!1}(l,s)||(o&&(Ce()&&m(l,t,r,n),v(a)),!!Ce()&&(kt(l),m(l,t,r,n),!0));case Wt:return!!b(je(l).textContent,i)||(Ce()&&m(l,t,r,n),o&&v(a),!1);default:return!0}}function T(e,t,r,n){let i=Ut(e,t)||{},a=i.get&&s(i.get,e)||i.value;if(a)return{get:()=>function(...e){if(r(this)){let r=n(this);if(r){let n=e[0];if(!M(n,this,t,r))return n}}return o(a,this,e)}}}function x(e,t,r,n){let i=Ut(e,t)||{},a=i.get&&s(i.get,e)||i.value;if(a)return{get:()=>function(...e){if(!r(this))return o(a,this,e);let i=n(this);if(!i)return o(a,this,e);let s=S(e,this,t,i);return s.length>0?o(a,this,s):void 0}}}function O(e,t,r,n){let i=Ut(e,t)||{},a=i.get&&s(i.get,e)||i.value;if(a)return{get:()=>function(...e){let[i,l]=e,c="afterbegin"===i||"beforeend"===i;if(r(this,c)){let e=n(this,c);if(e){let r,n=c?this:je(this).parentNode;switch(t){case"insertAdjacentElement":if(!M(l,n,t,e))return l;break;case"insertAdjacentHTML":return r=E(l,n,t,e),r?s(a,this,i,r):void 0;case"insertAdjacentText":if(!M(l,n,t,e))return}}}return o(a,this,e)}}}function P(e,t,r,n){let o=Ut(e,t)||{},{set:i}=o;if(i)return{set(e){if(!r(this))return s(i,this,e);let o=n(this);if(!o)return s(i,this,e);let a=E(e,this,t,o);return a?s(i,this,a):void 0}}}function j(e,t,r,n){let o=Ut(e,t)||{},{set:i}=o;if(i)return{set(e){if(!r(this))return s(i,this,e);let o=n(this);return o?M(e,this,t,o)?s(i,this,e):void 0:s(i,this,e)}}}Rt.frozen.has(document)||(Rt.frozen.set(document,!0),function(){let e;function t(e){return e&&Rt.frozen.has(e)}function r(e){try{return e&&(Rt.frozen.has(e)||Rt.frozen.has(je(e).parentNode))}catch(e){return!1}}function n(e,t){try{return e&&(Rt.frozen.has(e)&&t||Rt.frozen.has(je(e).parentNode)&&!t)}catch(e){return!1}}function o(e){return Rt.frozen.get(e)}function i(e){try{if(Rt.frozen.has(e))return Rt.frozen.get(e);let t=je(e).parentNode;return Rt.frozen.get(t)}catch(e){}}function s(e,t){try{if(Rt.frozen.has(e)&&t)return Rt.frozen.get(e);let r=je(e).parentNode;return Rt.frozen.get(r)}catch(e){}}e=T($t,"appendChild",t,o),et($t,"appendChild",e),e=T($t,"insertBefore",t,o),et($t,"insertBefore",e),e=T($t,"replaceChild",t,o),et($t,"replaceChild",e),e=x(Dt,"append",t,o),et(Dt,"append",e),e=x(Dt,"prepend",t,o),et(Dt,"prepend",e),e=x(Dt,"replaceWith",r,i),et(Dt,"replaceWith",e),e=x(Dt,"after",r,i),et(Dt,"after",e),e=x(Dt,"before",r,i),et(Dt,"before",e),e=O(Dt,"insertAdjacentElement",n,s),et(Dt,"insertAdjacentElement",e),e=O(Dt,"insertAdjacentHTML",n,s),et(Dt,"insertAdjacentHTML",e),e=O(Dt,"insertAdjacentText",n,s),et(Dt,"insertAdjacentText",e),e=P(Dt,"innerHTML",t,o),et(Dt,"innerHTML",e),e=P(Dt,"outerHTML",r,i),et(Dt,"outerHTML",e),e=j($t,"textContent",t,o),et($t,"textContent",e),e=j(Ht,"innerText",t,o),et(Ht,"innerText",e),e=j($t,"nodeValue",t,o),et($t,"nodeValue",e)}()),n=new Jt(w),n.observe(document,{childList:!0,subtree:!0}),w()},"hide-if-shadow-contains":function(e,t="*"){let r=`${e}\\${t}`;tr.has(r)||tr.set(r,[Re(e),t,_t()]);const n=De("hide-if-shadow-contain");rr||(rr=new qt((e=>{let t=new Kt;for(let{target:r}of je(e)){let e=je(r).parentNode;for(;e;)[r,e]=[e,je(r).parentNode];if(!er.has(r)&&!t.has(r)){t.add(r);for(let[e,t,o]of tr.values())if(e.test(je(r).textContent)){let e=je(r.host).closest(t);e&&(o(),je(r).appendChild(document.createElement("style")).textContent=":host {display: none !important}",kt(e),er.add(r),n("Hiding: ",e," for params: ",...arguments))}}}})),Gt.defineProperty(Zt,"attachShadow",{value:c(Qt,(function(){let e=o(Qt,this,arguments);return n("attachShadow is called for: ",e),rr.observe(e,{childList:!0,characterData:!0,subtree:!0}),e}))}))},"json-override":function(e,t,r="",n=""){if(!e)throw new nr("[json-override snippet]: Missing paths to override.");if(void 0===t)throw new nr("[json-override snippet]: No value to override with.");if(!ar){let e=De("json-override"),{parse:t}=or;ar=new ir,sr.defineProperty(window.JSON,"parse",{value:c(t,(function(r){let n=o(t,this,arguments);for(let{prune:t,needle:o,filter:i,value:s}of ar.values())if(!i||i.test(r)){if(je(o).some((e=>!st(n,e))))return n;for(let r of t){let t=st(n,r);void 0!==t&&(e(`Found ${r} replaced it with ${s}`),t[0][t[1]]=lt(s))}}return n}))}),e("Wrapped JSON.parse for override")}ar.set(e,{prune:je(e).split(/ +/),needle:r.length?je(r).split(/ +/):[],filter:n?Re(n):null,value:t})},"json-prune":function(e,t=""){if(!e)throw new lr("Missing paths to prune");if(!pr){let e=De("json-prune"),{parse:t}=cr;pr=new ur,fr.defineProperty(window.JSON,"parse",{value:c(t,(function(){let r=o(t,this,arguments);for(let{prune:t,needle:n}of pr.values()){if(je(n).some((e=>!st(r,e))))return r;for(let n of t){let t=st(r,n);void 0!==t&&(e(`Found ${n} and deleted`),delete t[0][t[1]])}}return r}))}),e("Wrapped JSON.parse for prune")}pr.set(e,{prune:je(e).split(/ +/),needle:t.length?je(t).split(/ +/):[]})},"override-property-read":function(e,t){if(!e)throw new dr("[override-property-read snippet]: No property to override.");if(void 0===t)throw new dr("[override-property-read snippet]: No value to override with.");let r=De("override-property-read"),n=lt(t);r(`Overriding ${e}.`),et(window,e,{get:()=>(r(`${e} override done.`),n),set(){}})},"prevent-listener":function(e,t,r){if(!e)throw new hr("[prevent-listener snippet]: No event type.");if(!Er){Er=new wr;let e=De("[prevent]");gr.defineProperty(br,"addEventListener",{value:c(vr,(function(t,r){for(let{evt:n,handlers:o,selectors:i}of Er.values()){if(!n.test(t))continue;let a=this instanceof Element;for(let l=0;l<o.length;l++){let c=o[l],u=i[l],f=()=>c.test(s(mr,"function"==typeof r?r:r.handleEvent));if((!c||f())&&(!u||a&&je(this).matches(u)))return void(Ce()&&(yr.groupCollapsed("DEBUG [prevent] was successful"),e(`type: ${t} matching ${n}`),e("handler:",r),c&&e(`matching ${c}`),u&&e("on element: ",this,` matching ${u}`),e("was prevented from being added"),yr.groupEnd()))}}return o(vr,this,arguments)}))}),e("Wrapped addEventListener")}Er.has(e)||Er.set(e,{evt:Re(e),handlers:[],selectors:[]});let{handlers:n,selectors:i}=Er.get(e);n.push(t?Re(t):null),i.push(r)},"strip-fetch-query-parameter":function(e,t=null){const r=De("strip-fetch-query-parameter");Sr||(Sr=new Map,window.fetch=c(Tr,((...e)=>{let[t]=e;if("string"==typeof t){let n=new Mr(t);for(let[o,i]of Sr)i&&!i.test(t)||Or(n.searchParams,o)&&(r(`${o} has been stripped from url ${t}`),xr(n.searchParams,o),e[0]=n.href)}return o(Tr,self,e)}))),Sr.set(e,t&&Re(t))},trace:function(...e){o($e,null,e)}};
+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);if(n in U)return Se(e,U[n].prototype);if("nodeType"in e)switch(s(Me,e)){case 1:if(!(t in xe))throw new Error("unknown hint "+t);return xe[t](e);case 2:return xe.Attr(e);case 3:return xe.Node(e);case 9:return xe.Document(e)}throw new Error("unknown brand "+n)};var Te=pe?e=>e===window||e===globalThis?U:e:oe(((e,t="Element")=>{if(e===window||e===globalThis)return U;switch(typeof e){case"object":return e&&Ce(e,t);case"string":return new me(e);case"number":return new ge(e);default:throw new Error("unsupported value")}}));let{document:We,getComputedStyle:Oe,isExtensionContext:Pe,variables:Le,Array:De,MutationObserver:Ne,Object:Ie,XPathEvaluator:Ae,XPathExpression:Re,XPathResult:Ve}=Te(window),{querySelectorAll:$e}=We,He=$e&&i($e,We);const{assign:je,setPrototypeOf:_e}=Ie;class Fe extends Re{evaluate(...e){return _e(r(super.evaluate,this,e),Ve.prototype)}}class qe extends Ae{createExpression(...e){return _e(r(super.createExpression,this,e),Fe.prototype)}}function Be(e){if(Le.hidden.has(e))return;!function(e){Pe&&"function"==typeof checkElement&&checkElement(e)}(e),Le.hidden.add(e);let{style:t}=Te(e),n=Te(t,"CSSStyleDeclaration"),o=Te([]),{debugCSSProperties:r}=S;for(let[e,t]of r||[["display","none"]])n.setProperty(e,t,"important"),o.push([e,n.getPropertyValue(e)]);new Ne((()=>{for(let[e,t]of o){let o=n.getPropertyValue(e),r=n.getPropertyPriority(e);o==t&&"important"==r||n.setProperty(e,t,"important")}})).observe(e,{attributes:!0,attributeFilter:["style"]})}function ze(e){let t=e;if(t.startsWith("xpath(")&&t.endsWith(")")){let e=t.slice(6,-1),n=(new qe).createExpression(e,null),o=Ve.ORDERED_NODE_SNAPSHOT_TYPE;return e=>{if(!e)return;let t=n.evaluate(We,o,null),{snapshotLength:r}=t;for(let n=0;n<r;n++)e(t.snapshotItem(n))}}return t=>He(e).forEach(t)}function Xe(e,t,n,o){let r;null==n&&(n=t);const i=()=>{for(const i of He(n)){const n=Te(i).closest(t);n&&e(i,n)&&(r(),Be(n),"function"==typeof o&&o(n))}};return je(new Ne(i),{race(e){r=e,this.observe(We,{childList:!0,characterData:!0,subtree:!0}),i()}})}function Je(e,t,n){let o=Te(t,"CSSStyleDeclaration");if("none"==o.getPropertyValue("display"))return!1;let r=o.getPropertyValue("visibility");if("hidden"==r||"collapse"==r)return!1;if(!n||e==n)return!0;let i=Te(e).parentElement;return!i||Je(i,Oe(i),n)}function Ue(e){let t=Oe(e),{cssText:n}=t;if(n)return n;for(let e of t)n+=`${e}: ${t[e]}; `;return Te(n).trim()}let{Math:Ge,RegExp:Ke}=Te(window);function Qe(e){let{length:t}=e;if(t>1&&"/"===e[0]){let n="/"===e[t-1];if(n||t>2&&Te(e).endsWith("/i")){let t=[Te(e).slice(1,n?-1:-2)];return n||t.push("i"),new Ke(...t)}}return new Ke(Te(e).replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))}let Ye=!1;function Ze(){return Ye}const{console:et}=Te(window),tt=()=>{};function nt(...e){Ze()&&Te(e).unshift("%c DEBUG","font-weight: bold"),et.log(...e)}function ot(e){return i(Ze()?nt:tt,null,e)}let{Array:rt,Error:it,Map:st,parseInt:at}=Te(window),ct=null,lt=null;function ut(e,t){if(null===ct)return tt;let n=ct,{participants:o}=n;return o.set(r,t),r;function r(){if(n.winners<1)return;if(ot("race")(`${e} won the race`),n===ct)lt.push(r);else if(o.delete(r),--n.winners<1){for(let e of o.values())e();o.clear()}}}const dt={get(e,t){const n=e;for(;!m(e,t);)e=g(e);const{get:o,set:i}=p(e,t);return function(){return arguments.length?r(i,n,arguments):s(o,n)}}};var ht;function ft(e,t,n){var o,r;n?"load"===n?(e("Waiting until window.load"),window.onload=()=>{e("Window.load fired."),t()}):"loading"===n||"interactive"===n||"complete"===n?(e("Waiting document state until :",n),document.onreadystatechange=()=>{e("Document state changed:",document.readyState),document.readyState===n&&t()}):(e("Waiting until ",n," event is triggered on document"),(o=document,r=n,new Promise((e=>{const t=()=>{o.removeEventListener(r,t),e()};o.addEventListener(r,t)}))).then((()=>{e(n," is triggered on document, starting the snippet"),t()})).catch((t=>{e("There was an error while waiting for the event.",t)}))):t()}Te(window),ht=window,new e(ht,dt),Te(/^\d+$/);let{MutationObserver:pt,WeakSet:wt,getComputedStyle:gt}=Te(window);let{clearTimeout:mt,fetch:bt,getComputedStyle:yt,setTimeout:vt,Map:St,MutationObserver:kt,Uint8Array:Et}=Te(window);let Mt=new St;function xt(e,{as:t="arrayBuffer",cleanup:n=6e4}={}){let o=t+":"+e,r=Mt.get(o)||{remove:()=>Mt.delete(o),result:null,timer:0};return mt(r.timer),r.timer=vt(r.remove,n),r.result||(r.result=bt(e).then((e=>e[t]())).catch(r.remove),Mt.set(o,r)),r.result}function Ct(e){return e.reduce(((e,t)=>e+function(e,t=2){let n=Te(e).toString(16);return n.length<t&&(n=Te("0").repeat(t-n.length)+n),n}(t)),"")}const{parseFloat:Tt,Math:Wt,MutationObserver:Ot,WeakSet:Pt}=Te(window),{min:Lt}=Wt,Dt=(e,t)=>{const n=e.length+1,o=t.length+1,r=[[0]];let i=0,s=0;for(;++i<o;)r[0][i]=i;for(i=0;++i<n;){const n=e[s];let a=0,c=0;for(r[i]=[i];++a<o;)r[i][a]=Lt(r[s][a]+1,r[i][c]+1,r[s][c]+(n!=t[c])),++c;++s}return r[n-1][o-1]};let{getComputedStyle:Nt,Map:It,WeakSet:At,parseFloat:Rt}=Te(window);const{ELEMENT_NODE:Vt,TEXT_NODE:$t}=Node;let{MutationObserver:Ht,WeakSet:jt,getComputedStyle:_t}=Te(window);let{getComputedStyle:Ft,MutationObserver:qt,WeakSet:Bt}=Te(window);Te(window);const zt={mark(){},end(){},toString:()=>"{mark(){},end(){}}"};function Xt(e,t=10){return zt}let{MutationObserver:Jt,WeakSet:Ut}=Te(window);const{ELEMENT_NODE:Gt}=Node;let{MutationObserver:Kt,WeakSet:Qt}=Te(window);const{ELEMENT_NODE:Yt}=Node;let{parseInt:Zt,setTimeout:en,Error:tn,MouseEvent:nn,MutationObserver:on,WeakSet:rn}=Te(window);let{parseInt:sn,setTimeout:an,Error:cn,MouseEvent:ln,MutationObserver:un,WeakSet:dn}=Te(window);const hn=["auxclick","click","dblclick","gotpointercapture","lostpointercapture","mouseenter","mousedown","mouseleave","mousemove","mouseout","mouseover","mouseup","pointerdown","pointerenter","pointermove","pointerover","pointerout","pointerup","pointercancel","pointerleave"];const fn={log:nt,race:function(e,t="1"){switch(e){case"start":ct={winners:at(t,10)||1,participants:new st},lt=new rt;break;case"end":case"finish":case"stop":ct=null;for(let e of lt)e();lt=null;break;default:throw new it(`Invalid action: ${e}`)}},debug:function(){Ye=!0},"hide-if-matches-xpath":function(e,t){const{mark:n,end:o}=Xt(),r=ot("hide-if-matches-xpath"),i=t=>{const i=ze(`xpath(${e})`),s=new Ut,a=()=>{n(),i((t=>{if(s.has(t))return!1;s.add(t),l(),Te(t).nodeType===Gt?Be(t):Te(t).textContent="",r("Matched: ",t," for selector: ",e)})),o()},c=new Jt(a),l=ut("hide-if-matches-xpath",(()=>c.disconnect()));c.observe(t,{characterData:!0,childList:!0,subtree:!0}),a()};if(t){let e,n=0;const o=ze(`xpath(${t})`),r=()=>{o((e=>{i(e),n++})),n>0&&e.disconnect()};e=new Jt(r),e.observe(document,{characterData:!0,childList:!0,subtree:!0}),r()}else i(document)},"hide-if-matches-computed-xpath":function(e,t,n,o){const{mark:r,end:i}=Xt(),s=ot("hide-if-matches-computed-xpath");if(!t||!e)return void s("No query or searchQuery provided.");const a=t=>{const n=(t=>e.replace("{{}}",t))(t);s("Starting hiding elements that match query: ",n);const o=ze(`xpath(${n})`),a=new Qt,c=()=>{r(),o((t=>{if(a.has(t))return!1;a.add(t),u(),Te(t).nodeType===Yt?Be(t):Te(t).textContent="",s("Matched: ",t," for selector: ",e)})),i()},l=new Kt(c),u=ut("hide-if-matches-computed-xpath",(()=>l.disconnect()));l.observe(document,{characterData:!0,childList:!0,subtree:!0}),c()},c=Qe(n);ft(s,(()=>{if(t){s("Started searching for: ",t);const e=new Qt;let n;const o=ze(`xpath(${t})`),r=()=>{o((t=>{if(e.has(t))return!1;if(e.add(t),s("Found node: ",t),t.innerHTML){s("Searching in: ",t.innerHTML);const e=t.innerHTML.match(c);if(e&&e.length){let t="";t=e[1]?e[1]:e[0],s("Matched search query: ",t),a(t)}}}))};n=new Kt(r),n.observe(document,{characterData:!0,childList:!0,subtree:!0}),r()}}),o)},"hide-if-contains":function(e,t="*",n=null){const o=ot("hide-if-contains");let r=Qe(e);const i=Xe((e=>r.test(Te(e).textContent)),t,n,(e=>{o("Matched: ",e," for selector: ",t,n)}));i.race(ut("hide-if-contains",(()=>{i.disconnect()})))},"hide-if-contains-similar-text":function(e,t,n=null,o=0,r=0){const i=new Pt,s=ot("hide-if-contains-similar-text"),a=Te(e),{length:c}=a,l=c+Tt(o)||0,u=Te([...a]).sort(),d=Tt(r)||1/0;null==n&&(n=t),s("Looking for similar text: "+a);const h=()=>{for(const e of He(n)){if(i.has(e))continue;i.add(e);const{innerText:n}=Te(e),r=Lt(d,n.length-l+1);for(let i=0;i<r;i++){const r=Te(n).substr(i,l);if(Dt(u,Te([...r]).sort())-o<=0){const n=Te(e).closest(t);if(s("Found similar text: "+a,n),n){p(),Be(n);break}}}}};let f=new Ot(h),p=ut("hide-if-contains-similar-text",(()=>f.disconnect()));f.observe(document,{childList:!0,characterData:!0,subtree:!0}),h()},"hide-if-contains-visible-text":function(e,t,n=null,...o){let r=Te([]);const i=new It([["-snippet-box-margin","2"],["-disable-bg-color-check","false"],["-check-is-contained","false"]]);for(let e of o){e=Te(e);let t=e.indexOf(":");if(t<0)continue;let n=e.slice(0,t).trim().toString(),o=e.slice(t+1).trim().toString();n&&o&&(i.has(n)?i.set(n,o):r.push([n,o]))}let s=Te([["opacity","0"],["font-size","0px"],["color","rgba(0, 0, 0, 0)"]]),a=new It(s.concat(r));function c(e,t,{bgColorCheck:n=!0}={}){t||(t=Nt(e)),t=Te(t);for(const[e,n]of a){if(Qe(n).test(t.getPropertyValue(e)))return!1}let o=t.getPropertyValue("color");return!n||t.getPropertyValue("background-color")!=o}function l(e,t,{bgColorCheck:n=!0}={}){let o=Nt(e,t);if(!Je(e,o)||!c(e,o,{bgColorCheck:n}))return"";let{content:r}=Te(o);if(r&&"none"!==r){let t=Te([]);return r=Te(r).trim().replace(/(["'])(?:(?=(\\?))\2.)*?\1/g,(e=>""+(t.push(Te(e).slice(1,-1))-1))),r=r.replace(/\s*attr\(\s*([^\s,)]+)[^)]*?\)\s*/g,((t,n)=>Te(e).getAttribute(n)||"")),r.replace(/\x01(\d+)/g,((e,n)=>t[n]))}return""}function u(e,t,{boxMargin:n=2}={}){const o=Te(e).getBoundingClientRect(),r=Te(t).getBoundingClientRect(),i=r.left-n,s=r.right+n,a=r.top-n,c=r.bottom+n;return i<=o.left&&o.left<=s&&a<=o.top&&o.top<=c&&a<=o.bottom&&o.bottom<=c&&i<=o.right&&o.right<=s}function d(e,t,n,o,r,{boxMargin:i=2,bgColorCheck:s,checkIsContained:a}={}){let h=!n;if(h&&(n=Nt(e)),!Je(e,n,h&&t))return"";o||"hidden"!==Te(n).getPropertyValue("overflow-x")&&"hidden"!==Te(n).getPropertyValue("overflow-y")||(o=e);let f=l(e,":before",{bgColorCheck:s});for(let t of Te(e).childNodes)switch(Te(t).nodeType){case Vt:f+=d(t,e,Nt(t),o,r,{boxMargin:i,bgColorCheck:s,checkIsContained:a});break;case $t:if(o)u(e,o,{boxMargin:i})&&c(e,n,{bgColorCheck:s})&&(f+=Te(t).nodeValue);else if(c(e,n,{bgColorCheck:s})){if(a&&!u(e,r,{boxMargin:i}))continue;f+=Te(t).nodeValue}}return f+l(e,":after",{bgColorCheck:s})}const h=i.get("-snippet-box-margin"),f=Rt(h)||0,p=!("true"===i.get("-disable-bg-color-check")),w="true"===i.get("-check-is-contained");let g=Qe(e),m=new At;const b=Xe(((e,t)=>{if(m.has(e))return!1;m.add(e);let n=d(e,t,null,null,e,{boxMargin:f,bgColorCheck:p,checkIsContained:w}),o=g.test(n);return Ze()&&n.length&&nt(o,g,n),o}),t,n);b.race(ut("hide-if-contains-visible-text",(()=>{b.disconnect()})))},"hide-if-contains-and-matches-style":function(e,t="*",n=null,o=null,r=null,i,s=null,a=null){const c=ot("hide-if-contains-and-matches-style"),l=new wt;null==n&&(n=t);const u=Qe(e),d=o?Qe(o):null,h=r?Qe(r):null,f=()=>{const e=()=>{if(!(s&&window.innerWidth<s||a&&window.innerWidth>a))for(const e of He(n))if(!l.has(e)&&u.test(Te(e).textContent))if(!h||h.test(Ue(e))){const n=Te(e).closest(t);if(!n)continue;!d||d.test(Ue(n))?(r(),Be(n),l.add(e),c("Matched: ",n,"which contains: ",e," for params: ",...arguments)):c("In this element the searchStyle matched but style didn't:\n",n,gt(n),...arguments)}else c("In this element the searchStyle didn't match:\n",e,gt(e),...arguments)},o=new pt(e),r=ut("hide-if-contains-and-matches-style",(()=>o.disconnect()));o.observe(document,{childList:!0,characterData:!0,subtree:!0}),e()};ft(c,f,i)},"hide-if-has-and-matches-style":function(e,t="*",n=null,o=null,r=null,i=null,s=null,a=null){const c=ot("hide-if-has-and-matches-style"),l=new jt;null==n&&(n=t);const u=o?Qe(o):null,d=r?Qe(r):null,h=()=>{const o=()=>{if(!(s&&window.innerWidth<s||a&&window.innerWidth>a))for(const o of He(n))if(!l.has(o))if(!Te(o).querySelector(e)||d&&!d.test(Ue(o)))c("In this element the searchStyle didn't match:\n",o,_t(o),...arguments);else{const e=Te(o).closest(t);!e||u&&!u.test(Ue(e))?c("In this element the searchStyle matched but style didn't:\n",e,_t(e),...arguments):(i(),Be(e),l.add(o),c("Matched: ",e,"which contains: ",o," for params: ",...arguments))}},r=new Ht(o),i=ut("hide-if-has-and-matches-style",(()=>r.disconnect()));r.observe(document,{childList:!0,subtree:!0}),o()};ft(c,h,i)},"hide-if-labelled-by":function(e,t,n=null){let o=null==n,r=Qe(e),i=new Bt,s=()=>{for(let e of He(t)){let t=o?e:Te(e).closest(n);if(!t||!Je(e,Ft(e),t))continue;let s=Te(e).getAttribute("aria-labelledby"),a=()=>{i.has(t)||r.test(Te(e).getAttribute("aria-label")||"")&&(c(),i.add(t),Be(t))};if(s)for(let e of Te(s).split(/\s+/)){let n=Te(document).getElementById(e);n?!i.has(n)&&r.test(n.innerText)&&(c(),i.add(n),Be(t)):a()}else a()}},a=new qt(s),c=ut("hide-if-labelled-by",(()=>a.disconnect()));a.observe(document,{characterData:!0,childList:!0,subtree:!0}),s()},"hide-if-contains-image":function(e,t,n){null==n&&(n=t);let o=Qe(e);const r=ot("hide-if-contains-image");let i=()=>{for(let e of He(n)){let n=yt(e),i=Te(n["background-image"]).match(/^url\("(.*)"\)$/);i&&xt(i[1]).then((n=>{if(o.test(Ct(new Et(n)))){let n=Te(e).closest(t);n&&(a(),Be(n),r("Matched: ",n," for:",...arguments))}}))}},s=new kt(i),a=ut("hide-if-contains-image",(()=>s.disconnect()));s.observe(document,{childList:!0,subtree:!0}),i()},"simulate-event-poc":function(e,t,n="0"){if(!e)throw new tn("[simulate-event snippet]: No event type provided.");if(!t)throw new tn("[simulate-event snippet]: No selector provided.");let o=ze(t),r=Zt(n,10),i=new rn;function s(){o((t=>{i.has(t)||(i.add(t),en((()=>{Te(t).dispatchEvent(new nn(e,{bubbles:!0,cancelable:!0}))}),r))}))}new on(s).observe(document,{childList:!0,subtree:!0}),s()},"simulate-mouse-event":function(...e){const t=ot("simulate-mouse-event");if(e.length<1)throw new cn("[simulate-mouse-event snippet]: No selector provided.");e.length>7&&(e=e.slice(0,7));const n=Te([]);function o(){return n.forEach((e=>{if(!e.found){(function(e){let t=e;if(t.startsWith("xpath(")&&t.endsWith(")")){let t=ze(e);return()=>{let e=Te([]);return t((t=>e.push(t))),e}}return()=>De.from(He(e))})(e.selector)().length>0&&(e.found=!0)}})),n.every((e=>e.found))}function r(e,n,o){e&&n&&("click"===n&&e.click?(e.click(),t("Clicked on this node:\n",e,"\nwith a delay of",o,"ms")):(e.dispatchEvent(new ln(n,{bubbles:!0,cancelable:!0})),t("A",n,"event was dispatched with a delay of",o,"ms on this node:\n",e)))}Te(e).forEach((e=>{const o=function(e){if(!e)return null;const n={selector:"",continue:!1,trigger:!1,event:"click",delay:"500",clicked:!1,found:!1},o=e.split("$");let r=[];o.length>=2&&(r=o[1].toLowerCase().split(",")),[n.selector]=o;for(const e of r)if("trigger"===e)n.trigger=!0;else if("continue"===e)n.continue=!0;else if(e.startsWith("event")){const t=e.toLowerCase().split("=");t[1]?n.event=t[1]:n.event="click"}else if(e.startsWith("delay")){const t=e.toLowerCase().split("=");t[1]?n.delay=t[1]:n.delay="500"}return hn.includes(n.event)||t(n.event," might be misspelled, check for typos.\n","These are the supported events:",hn),n}(e);n.push(o)}));let i=!1;const[s]=n.slice(-1);s.trigger=!0;let a=new dn;function c(){if(i||(i=o()),i)for(const e of n){const t=ze(e.selector),n=sn(e.delay,10);e.trigger&&t((t=>{a.has(t)||(a.add(t),e.continue?setInterval((()=>{r(t,e.event,e.delay)}),n):an((()=>{r(t,e.event,e.delay)}),n))}))}}new un(c).observe(document,{childList:!0,subtree:!0}),c()}};
+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(Te,s(Me,e),8,-1))(e);if(r in Oe)return Oe[r](e);if(r in G)return Se(e,G[r].prototype);if("nodeType"in e)switch(s(xe,e)){case 1:if(!(t in Oe))throw new Error("unknown hint "+t);return Oe[t](e);case 2:return Oe.Attr(e);case 3:return Oe.Node(e);case 9:return Oe.Document(e)}throw new Error("unknown brand "+r)};var je=we?e=>e===window||e===globalThis?G:e:oe(((e,t="Element")=>{if(e===window||e===globalThis)return G;switch(typeof e){case"object":return e&&Pe(e,t);case"string":return new me(e);case"number":return new ye(e);default:throw new Error("unsupported value")}}));const Ne={get(e,t){const r=e;for(;!m(e,t);)e=y(e);const{get:n,set:i}=w(e,t);return function(){return arguments.length?o(i,r,arguments):s(n,r)}}},Le=t=>new e(t,Ne);let ke=!1;function Ce(){return ke}const{console:Ae}=je(window),We=()=>{};function $e(...e){Ce()&&je(e).unshift("%c DEBUG","font-weight: bold"),Ae.log(...e)}function De(e){return i(Ce()?$e:We,null,e)}let{Math:He,RegExp:ze}=je(window);function Re(e){let{length:t}=e;if(t>1&&"/"===e[0]){let r="/"===e[t-1];if(r||t>2&&je(e).endsWith("/i")){let t=[je(e).slice(1,r?-1:-2)];return r||t.push("i"),new ze(...t)}}return new ze(je(e).replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))}function Fe(){return je(He.floor(2116316160*He.random()+60466176)).toString(36)}let{parseFloat:Ie,variables:Je,Array:Ve,Error:Be,Map:Ue,Object:_e,ReferenceError:Xe,Set:qe,WeakMap:Ge}=je(window),{onerror:Ke}=Le(window),Ye=Node.prototype,Ze=Element.prototype,Qe=null;function et(e,t,r,n=!0){let o=je(t),i=o.indexOf(".");if(-1==i){let o=_e.getOwnPropertyDescriptor(e,t);if(o&&!o.configurable)return;let i=_e.assign({},r,{configurable:n});if(!o&&!i.get&&i.set){let r=e[t];i.get=()=>r}return void _e.defineProperty(e,t,i)}let s=o.slice(0,i).toString();t=o.slice(i+1).toString();let a=e[s];!a||"object"!=typeof a&&"function"!=typeof a||et(a,t,r);let l=_e.getOwnPropertyDescriptor(e,s);if(l&&!l.configurable)return;Qe||(Qe=new Ge),Qe.has(e)||Qe.set(e,new Ue);let c=Qe.get(e);if(c.has(s))return void c.get(s).set(t,r);let u=new Ue([[t,r]]);c.set(s,u),_e.defineProperty(e,s,{get:()=>a,set(e){if(a=e,a&&("object"==typeof a||"function"==typeof a))for(let[e,t]of u)et(a,e,t)},configurable:n})}function tt(e){let t=Ke();Ke(((...r)=>{let n=r.length&&r[0];return!("string"!=typeof n||!je(n).includes(e))||("function"==typeof t?o(t,this,r):void 0)}))}function rt(e,t,r,n=!0){let o=De(e);if(!r)return void o("no property to abort on read");let i=Fe();o(`aborting on ${r} access`),et(t,r,{get:function(){throw o(`${r} access aborted`),new Xe(i)},set(){}},n),tt(i)}function nt(e,t,r,n=!0){let o=De(e);if(!r)return void o("no property to abort on write");let i=Fe();o(`aborting when setting ${r}`),et(t,r,{set:function(){throw o(`setting ${r} aborted`),new Xe(i)}},n),tt(i)}function ot(e,t=!1,r=!1){let n=Je.abortedIframes,i=Je.iframePropertiesToAbort;for(let o of Ve.from(window.frames))if(n.has(o))for(let i of e)t&&n.get(o).read.add(i),r&&n.get(o).write.add(i);for(let n of e)t&&i.read.add(n),r&&i.write.add(n);function a(){for(let e of Ve.from(window.frames)){n.has(e)||n.set(e,{read:new qe(i.read),write:new qe(i.write)});let t=n.get(e).read;if(t.size>0){let r=Ve.from(t);t.clear();for(let t of r)rt("abort-on-iframe-property-read",e,t)}let r=n.get(e).write;if(r.size>0){let t=Ve.from(r);r.clear();for(let r of t)nt("abort-on-iframe-property-write",e,r)}}}a(),n.has(document)||(n.set(document,!0),function(e){let t;function r(e,t){for(let r of t){et(e,r,n(e,r))}}function n(t,r){let n=t[r];return{get:()=>function(...t){let r;return r=o(n,this,t),e&&e(),r}}}function i(t,r){let n=_e.getOwnPropertyDescriptor(t,r),{set:o}=n||{};return{set(t){let r;return r=s(o,this,t),e&&e(),r}}}r(Ye,["appendChild","insertBefore","replaceChild"]),r(Ze,["append","prepend","replaceWith","after","before","insertAdjacentElement","insertAdjacentHTML"]),t=i(Ze,"innerHTML"),et(Ze,"innerHTML",t),t=i(Ze,"outerHTML"),et(Ze,"outerHTML",t)}(a))}let{Object:it}=window;function st(e,t){if(!(e instanceof it))return;let r=e,n=je(t).split(".");if(0===n.length)return;for(let e=0;e<n.length-1;e++){let t=n[e];if(!m(r,t))return;if(r=r[t],!(r instanceof it))return}let o=n[n.length-1];return m(r,o)?[r,o]:void 0}const at=je(/^\d+$/);function lt(e){switch(e){case"false":return!1;case"true":return!0;case"null":return null;case"noopFunc":return()=>{};case"trueFunc":return()=>!0;case"falseFunc":return()=>!1;case"emptyArray":return[];case"emptyObj":return{};case"undefined":return;case"":return e;default:if(at.test(e))return Ie(e);throw new Be(`[override-property-read snippet]: Value "${e}" is not valid.`)}}let{HTMLScriptElement:ct,Object:ut,ReferenceError:ft}=je(window),pt=ut.getPrototypeOf(ct);let{Error:dt}=je(window),{cookie:ht}=Le(document);let{document:wt,getComputedStyle:gt,isExtensionContext:yt,variables:mt,Array:bt,MutationObserver:vt,Object:Et,XPathEvaluator:St,XPathExpression:Mt,XPathResult:Tt}=je(window),{querySelectorAll:xt}=wt,Ot=xt&&i(xt,wt);const{assign:Pt,setPrototypeOf:jt}=Et;class Nt extends Mt{evaluate(...e){return jt(o(super.evaluate,this,e),Tt.prototype)}}class Lt extends St{createExpression(...e){return jt(o(super.createExpression,this,e),Nt.prototype)}}function kt(e){if(mt.hidden.has(e))return;!function(e){yt&&"function"==typeof checkElement&&checkElement(e)}(e),mt.hidden.add(e);let{style:t}=je(e),r=je(t,"CSSStyleDeclaration"),n=je([]),{debugCSSProperties:o}=S;for(let[e,t]of o||[["display","none"]])r.setProperty(e,t,"important"),n.push([e,r.getPropertyValue(e)]);new vt((()=>{for(let[e,t]of n){let n=r.getPropertyValue(e),o=r.getPropertyPriority(e);n==t&&"important"==o||r.setProperty(e,t,"important")}})).observe(e,{attributes:!0,attributeFilter:["style"]})}function Ct(e){let t=e;if(t.startsWith("xpath(")&&t.endsWith(")")){let t=function(e){let t=e;if(t.startsWith("xpath(")&&t.endsWith(")")){let e=t.slice(6,-1),r=(new Lt).createExpression(e,null),n=Tt.ORDERED_NODE_SNAPSHOT_TYPE;return e=>{if(!e)return;let t=r.evaluate(wt,n,null),{snapshotLength:o}=t;for(let r=0;r<o;r++)e(t.snapshotItem(r))}}return t=>Ot(e).forEach(t)}(e);return()=>{let e=je([]);return t((t=>e.push(t))),e}}return()=>bt.from(Ot(e))}let{ELEMENT_NODE:At,TEXT_NODE:Wt,prototype:$t}=Node,{prototype:Dt}=Element,{prototype:Ht}=HTMLElement,{console:zt,variables:Rt,DOMParser:Ft,Error:It,MutationObserver:Jt,Object:Vt,ReferenceError:Bt}=je(window),{getOwnPropertyDescriptor:Ut}=Vt;function _t(e,t){return We}je(window);const{Map:Xt,MutationObserver:qt,Object:Gt,Set:Kt,WeakSet:Yt}=je(window);let Zt=Element.prototype,{attachShadow:Qt}=Zt,er=new Yt,tr=new Xt,rr=null;const{Error:nr,JSON:or,Map:ir,Object:sr}=je(window);let ar=null;let{Error:lr,JSON:cr,Map:ur,Object:fr}=je(window),pr=null;let{Error:dr}=je(window);let{Error:hr,Map:wr,Object:gr,console:yr}=je(window),{toString:mr}=Function.prototype,br=EventTarget.prototype,{addEventListener:vr}=br,Er=null;let Sr,{URL:Mr,fetch:Tr}=je(window),{delete:xr,has:Or}=l(URLSearchParams.prototype);const Pr={"abort-current-inline-script":function(e,t=null){const r=De("abort-current-inline-script"),n=t?Re(t):null,o=Fe(),i=je(document).currentScript;let a=window;const l=je(e).split("."),c=je(l).pop();for(let e of je(l))if(a=a[e],!a||"object"!=typeof a&&"function"!=typeof a)return void r(l," is not found");const{get:u,set:f}=ut.getOwnPropertyDescriptor(a,c)||{};let p=a[c];void 0===p&&r("The property",c,"doesn't exist yet. Check typos.");const d=()=>{const e=je(document).currentScript;if(e instanceof pt&&""==je(e,"HTMLScriptElement").src&&e!=i&&(!n||n.test(je(e).textContent)))throw r(l," is aborted \n",e),new ft(o)};et(a,c,{get(){return d(),u?s(u,this):p},set(e){d(),f?s(f,this,e):p=e}}),tt(o)},"abort-on-iframe-property-read":function(...e){ot(e,!0,!1)},"abort-on-iframe-property-write":function(...e){ot(e,!1,!0)},"abort-on-property-read":function(e,t){rt("abort-on-property-read",window,e,!("false"===t))},"abort-on-property-write":function(e,t){nt("abort-on-property-write",window,e,!("false"===t))},"cookie-remover":function(e){if(!e)throw new dt("[cookie-remover snippet]: No cookie to remove.");let t=De("cookie-remover"),r=Re(e);if(je(/^http|^about/).test(location.protocol)){t("Parsing cookies for matches");for(const e of je(je(ht()).split(";").filter((e=>r.test(je(e).split("=")[0]))))){let r=je(location.hostname),n=je(e).split("=")[0],o="expires=Thu, 01 Jan 1970 00:00:00 GMT",i="path=/",s="domain="+r.slice(r.indexOf(".")+1);ht(`${je(n).trim()}=;${o};${i};${s}`),t(`Set expiration date on ${n}`)}}else t("Snippet only works for http or https and about.")},debug:function(){ke=!0},"freeze-element":function(e,t="",...r){let n,i,a=!1,l=!1,c=je(r).filter((e=>!h(e))),u=je(r).filter((e=>h(e))).map(Re),f=Fe(),p=Ct(e);!function(){let r=je(t).split("+");1===r.length&&""===r[0]&&(r=[]);for(let t of r)switch(t){case"subtree":a=!0;break;case"abort":l=!0;break;default:throw new It("[freeze] Unknown option passed to the snippet. [selector]: "+e+" [option]: "+t)}}();let d={selector:e,shouldAbort:l,rid:f,exceptionSelectors:c,regexExceptions:u,changeId:0};function h(e){return e.length>=2&&"/"==e[0]&&"/"==e[e.length-1]}function w(){i=p(),g(i,!1)}function g(e,t=!0){for(let r of e)Rt.frozen.has(r)||(Rt.frozen.set(r,d),!t&&a&&new Jt((e=>{for(let t of je(e))g(je(t,"MutationRecord").addedNodes)})).observe(r,{childList:!0,subtree:!0}),a&&je(r).nodeType===At&&g(je(r).childNodes))}function y(e,...t){$e(`[freeze][${e}] `,...t)}function m(e,t,r,n){let o=n.selector,i=n.changeId,s="string"==typeof e,a=n.shouldAbort?"aborting":"watching";switch(zt.groupCollapsed(`[freeze][${i}] ${a}: ${o}`),r){case"appendChild":case"append":case"prepend":case"insertBefore":case"replaceChild":case"insertAdjacentElement":case"insertAdjacentHTML":case"insertAdjacentText":case"innerHTML":case"outerHTML":y(i,s?"text: ":"node: ",e),y(i,"added to node: ",t);break;case"replaceWith":case"after":case"before":y(i,s?"text: ":"node: ",e),y(i,"added to node: ",je(t).parentNode);break;case"textContent":case"innerText":case"nodeValue":y(i,"content of node: ",t),y(i,"changed to: ",e)}y(i,`using the function "${r}"`),zt.groupEnd(),n.changeId++}function b(e,t){if(t)for(let r of t)if(r.test(e))return!0;return!1}function v(e){throw new Bt(e)}function E(e,t,r,n){let o=new Ft,{body:i}=je(o.parseFromString(e,"text/html")),s=S(je(i).childNodes,t,r,n);return je(s).map((e=>{switch(je(e).nodeType){case At:return je(e).outerHTML;case Wt:return je(e).textContent;default:return""}})).join("")}function S(e,t,r,n){let o=je([]);for(let i of e)M(i,t,r,n)&&o.push(i);return o}function M(e,t,r,n){let o=n.shouldAbort,i=n.regexExceptions,s=n.exceptionSelectors,a=n.rid;if("string"==typeof e){let s=e;return!!b(s,i)||(Ce()&&m(s,t,r,n),o&&v(a),Ce())}let l=e;switch(je(l).nodeType){case At:return!!function(e,t){if(t){let r=je(e);for(let e of t)if(r.matches(e))return!0}return!1}(l,s)||(o&&(Ce()&&m(l,t,r,n),v(a)),!!Ce()&&(kt(l),m(l,t,r,n),!0));case Wt:return!!b(je(l).textContent,i)||(Ce()&&m(l,t,r,n),o&&v(a),!1);default:return!0}}function T(e,t,r,n){let i=Ut(e,t)||{},a=i.get&&s(i.get,e)||i.value;if(a)return{get:()=>function(...e){if(r(this)){let r=n(this);if(r){let n=e[0];if(!M(n,this,t,r))return n}}return o(a,this,e)}}}function x(e,t,r,n){let i=Ut(e,t)||{},a=i.get&&s(i.get,e)||i.value;if(a)return{get:()=>function(...e){if(!r(this))return o(a,this,e);let i=n(this);if(!i)return o(a,this,e);let s=S(e,this,t,i);return s.length>0?o(a,this,s):void 0}}}function O(e,t,r,n){let i=Ut(e,t)||{},a=i.get&&s(i.get,e)||i.value;if(a)return{get:()=>function(...e){let[i,l]=e,c="afterbegin"===i||"beforeend"===i;if(r(this,c)){let e=n(this,c);if(e){let r,n=c?this:je(this).parentNode;switch(t){case"insertAdjacentElement":if(!M(l,n,t,e))return l;break;case"insertAdjacentHTML":return r=E(l,n,t,e),r?s(a,this,i,r):void 0;case"insertAdjacentText":if(!M(l,n,t,e))return}}}return o(a,this,e)}}}function P(e,t,r,n){let o=Ut(e,t)||{},{set:i}=o;if(i)return{set(e){if(!r(this))return s(i,this,e);let o=n(this);if(!o)return s(i,this,e);let a=E(e,this,t,o);return a?s(i,this,a):void 0}}}function j(e,t,r,n){let o=Ut(e,t)||{},{set:i}=o;if(i)return{set(e){if(!r(this))return s(i,this,e);let o=n(this);return o?M(e,this,t,o)?s(i,this,e):void 0:s(i,this,e)}}}Rt.frozen.has(document)||(Rt.frozen.set(document,!0),function(){let e;function t(e){return e&&Rt.frozen.has(e)}function r(e){try{return e&&(Rt.frozen.has(e)||Rt.frozen.has(je(e).parentNode))}catch(e){return!1}}function n(e,t){try{return e&&(Rt.frozen.has(e)&&t||Rt.frozen.has(je(e).parentNode)&&!t)}catch(e){return!1}}function o(e){return Rt.frozen.get(e)}function i(e){try{if(Rt.frozen.has(e))return Rt.frozen.get(e);let t=je(e).parentNode;return Rt.frozen.get(t)}catch(e){}}function s(e,t){try{if(Rt.frozen.has(e)&&t)return Rt.frozen.get(e);let r=je(e).parentNode;return Rt.frozen.get(r)}catch(e){}}e=T($t,"appendChild",t,o),et($t,"appendChild",e),e=T($t,"insertBefore",t,o),et($t,"insertBefore",e),e=T($t,"replaceChild",t,o),et($t,"replaceChild",e),e=x(Dt,"append",t,o),et(Dt,"append",e),e=x(Dt,"prepend",t,o),et(Dt,"prepend",e),e=x(Dt,"replaceWith",r,i),et(Dt,"replaceWith",e),e=x(Dt,"after",r,i),et(Dt,"after",e),e=x(Dt,"before",r,i),et(Dt,"before",e),e=O(Dt,"insertAdjacentElement",n,s),et(Dt,"insertAdjacentElement",e),e=O(Dt,"insertAdjacentHTML",n,s),et(Dt,"insertAdjacentHTML",e),e=O(Dt,"insertAdjacentText",n,s),et(Dt,"insertAdjacentText",e),e=P(Dt,"innerHTML",t,o),et(Dt,"innerHTML",e),e=P(Dt,"outerHTML",r,i),et(Dt,"outerHTML",e),e=j($t,"textContent",t,o),et($t,"textContent",e),e=j(Ht,"innerText",t,o),et(Ht,"innerText",e),e=j($t,"nodeValue",t,o),et($t,"nodeValue",e)}()),n=new Jt(w),n.observe(document,{childList:!0,subtree:!0}),w()},"hide-if-shadow-contains":function(e,t="*"){let r=`${e}\\${t}`;tr.has(r)||tr.set(r,[Re(e),t,_t()]);const n=De("hide-if-shadow-contain");rr||(rr=new qt((e=>{let t=new Kt;for(let{target:r}of je(e)){let e=je(r).parentNode;for(;e;)[r,e]=[e,je(r).parentNode];if(!er.has(r)&&!t.has(r)){t.add(r);for(let[e,t,o]of tr.values())if(e.test(je(r).textContent)){let e=je(r.host).closest(t);e&&(o(),je(r).appendChild(document.createElement("style")).textContent=":host {display: none !important}",kt(e),er.add(r),n("Hiding: ",e," for params: ",...arguments))}}}})),Gt.defineProperty(Zt,"attachShadow",{value:c(Qt,(function(){let e=o(Qt,this,arguments);return n("attachShadow is called for: ",e),rr.observe(e,{childList:!0,characterData:!0,subtree:!0}),e}))}))},"json-override":function(e,t,r="",n=""){if(!e)throw new nr("[json-override snippet]: Missing paths to override.");if(void 0===t)throw new nr("[json-override snippet]: No value to override with.");if(!ar){let e=De("json-override"),{parse:t}=or;ar=new ir,sr.defineProperty(window.JSON,"parse",{value:c(t,(function(r){let n=o(t,this,arguments);for(let{prune:t,needle:o,filter:i,value:s}of ar.values())if(!i||i.test(r)){if(je(o).some((e=>!st(n,e))))return n;for(let r of t){let t=st(n,r);void 0!==t&&(e(`Found ${r} replaced it with ${s}`),t[0][t[1]]=lt(s))}}return n}))}),e("Wrapped JSON.parse for override")}ar.set(e,{prune:je(e).split(/ +/),needle:r.length?je(r).split(/ +/):[],filter:n?Re(n):null,value:t})},"json-prune":function(e,t=""){if(!e)throw new lr("Missing paths to prune");if(!pr){let e=De("json-prune"),{parse:t}=cr;pr=new ur,fr.defineProperty(window.JSON,"parse",{value:c(t,(function(){let r=o(t,this,arguments);for(let{prune:t,needle:n}of pr.values()){if(je(n).some((e=>!st(r,e))))return r;for(let n of t){let t=st(r,n);void 0!==t&&(e(`Found ${n} and deleted`),delete t[0][t[1]])}}return r}))}),e("Wrapped JSON.parse for prune")}pr.set(e,{prune:je(e).split(/ +/),needle:t.length?je(t).split(/ +/):[]})},"override-property-read":function(e,t){if(!e)throw new dr("[override-property-read snippet]: No property to override.");if(void 0===t)throw new dr("[override-property-read snippet]: No value to override with.");let r=De("override-property-read"),n=lt(t);r(`Overriding ${e}.`),et(window,e,{get:()=>(r(`${e} override done.`),n),set(){}})},"prevent-listener":function(e,t,r){if(!e)throw new hr("[prevent-listener snippet]: No event type.");if(!Er){Er=new wr;let e=De("[prevent]");gr.defineProperty(br,"addEventListener",{value:c(vr,(function(t,r){for(let{evt:n,handlers:o,selectors:i}of Er.values()){if(!n.test(t))continue;let a=this instanceof Element;for(let l=0;l<o.length;l++){let c=o[l],u=i[l],f=()=>c.test(s(mr,"function"==typeof r?r:r.handleEvent));if((!c||f())&&(!u||a&&je(this).matches(u)))return void(Ce()&&(yr.groupCollapsed("DEBUG [prevent] was successful"),e(`type: ${t} matching ${n}`),e("handler:",r),c&&e(`matching ${c}`),u&&e("on element: ",this,` matching ${u}`),e("was prevented from being added"),yr.groupEnd()))}}return o(vr,this,arguments)}))}),e("Wrapped addEventListener")}Er.has(e)||Er.set(e,{evt:Re(e),handlers:[],selectors:[]});let{handlers:n,selectors:i}=Er.get(e);n.push(t?Re(t):null),i.push(r)},"strip-fetch-query-parameter":function(e,t=null){const r=De("strip-fetch-query-parameter");Sr||(Sr=new Map,window.fetch=c(Tr,((...e)=>{let[t]=e;if("string"==typeof t){let n=new Mr(t);for(let[o,i]of Sr)i&&!i.test(t)||Or(n.searchParams,o)&&(r(`${o} has been stripped from url ${t}`),xr(n.searchParams,o),e[0]=n.href)}return o(Tr,self,e)}))),Sr.set(e,t&&Re(t))},trace:function(...e){o($e,null,e)}};
+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