12065 lines
510 KiB
Diff
12065 lines
510 KiB
Diff
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
|