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)
|
||
|
+const snippets=fn;
|
||
|
+let context;
|
||
|
+for (const [name, ...args] of filters) {
|
||
|
+if (snippets.hasOwnProperty(name)) {
|
||
|
+try { context = snippets[name].apply(context, args); }
|
||
|
+catch (error) { console.error(error); }
|
||
|
+}
|
||
|
+}
|
||
|
+context = void 0;
|
||
|
+})(e, ...t);
|
||
|
+
|
||
|
+const callback = (environment, ...filters) => {
|
||
|
+const e=Proxy,{apply:t,bind:r,call:n}=Function,o=n.bind(t),i=n.bind(r),s=n.bind(n),a={get:(e,t)=>i(n,e[t])},l=t=>new e(t,a),c=(t,r)=>new e(t,{apply:(e,t,n)=>o(r,t,n)}),u={get:(e,t)=>i(e[t],e)},f=t=>new e(t,u),{assign:p,defineProperties:d,freeze:h,getOwnPropertyDescriptor:w,getOwnPropertyDescriptors:g,getPrototypeOf:y}=f(Object),{hasOwnProperty:m}=l({}),{species:b}=Symbol,v={get(e,t){const r=e[t];class n extends r{}const o=g(r.prototype);delete o.constructor,h(d(n.prototype,o));const i=g(r);return delete i.length,delete i.prototype,i[b]={value:n},h(d(n,i))}},E=t=>new e(t,v),S="undefined"!=typeof environment?environment:{};"undefined"==typeof globalThis&&(window.globalThis=window);const{apply:M,ownKeys:T}=f(Reflect),x="world"in S,O=x&&"ISOLATED"===S.world,P=x&&"MAIN"===S.world,j="object"==typeof chrome&&!!chrome.runtime,N="object"==typeof browser&&!!browser.runtime,L=!P&&(O||j||N),k=e=>L?e:C(e,H(e)),{create:C,defineProperties:A,defineProperty:W,freeze:$,getOwnPropertyDescriptor:D,getOwnPropertyDescriptors:H}=f(Object),z=f(globalThis),R=L?globalThis:E(globalThis),{Map:F,RegExp:I,Set:J,WeakMap:V,WeakSet:B}=R,U=(e,t,r=null)=>{const n=T(t);for(const o of T(e)){if(n.includes(o))continue;const i=D(e,o);if(r&&"value"in i){const{value:e}=i;"function"==typeof e&&(i.value=r(e))}W(t,o,i)}},_=e=>{const t=R[e];class r extends t{}const{toString:n,valueOf:o}=t.prototype;A(r.prototype,{toString:{value:n},valueOf:{value:o}});const i=e.toLowerCase(),s=e=>function(){const t=M(e,this,arguments);return typeof t===i?new r(t):t};return U(t,r,s),U(t.prototype,r.prototype,s),r},X=$({frozen:new V,hidden:new B,iframePropertiesToAbort:{read:new J,write:new J},abortedIframes:new V}),q=new I("^[A-Z]");var G=new Proxy(new F([["chrome",L&&(j&&chrome||N&&browser)||void 0],["isExtensionContext",L],["variables",X],["console",k(console)],["document",globalThis.document],["performance",k(performance)],["JSON",k(JSON)],["Map",F],["Math",k(Math)],["Number",L?Number:_("Number")],["RegExp",I],["Set",J],["String",L?String:_("String")],["WeakMap",V],["WeakSet",B],["MouseEvent",MouseEvent]]),{get(e,t){if(e.has(t))return e.get(t);let r=globalThis[t];return"function"==typeof r&&(r=(q.test(t)?R:z)[t]),e.set(t,r),r},has:(e,t)=>e.has(t)});const K={WeakSet:WeakSet,WeakMap:WeakMap,WeakValue:class{has(){return!1}set(){}}},{apply:Y}=Reflect;const{Map:Z,WeakMap:Q,WeakSet:ee,setTimeout:te}=G;let re=!0,ne=e=>{e.clear(),re=!re};var oe=function(e){const{WeakSet:t,WeakMap:r,WeakValue:n}=this||K,o=new t,i=new r,s=new n;return function(t){if(o.has(t))return t;if(i.has(t))return i.get(t);if(s.has(t))return s.get(t);const r=Y(e,this,arguments);return o.add(r),r!==t&&("object"==typeof t&&t?i:s).set(t,r),r}}.bind({WeakMap:Q,WeakSet:ee,WeakValue:class extends Z{set(e,t){return re&&(re=!re,te(ne,0,this)),super.set(e,t)}}});const{concat:ie,includes:se,join:ae,reduce:le,unshift:ce}=l([]),ue=E(globalThis),{Map:fe,WeakMap:pe}=ue,de=new fe,he=t=>{const r=(e=>{const t=[];let r=e;for(;r;){if(de.has(r))ce(t,de.get(r));else{const e=g(r);de.set(r,e),ce(t,e)}r=y(r)}return ce(t,{}),o(p,null,t)})("function"==typeof t?t.prototype:t),n={get(e,t){if(t in r){const{value:n,get:o}=r[t];if(o)return s(o,e);if("function"==typeof n)return i(n,e)}return e[t]},set(e,t,n){if(t in r){const{set:o}=r[t];if(o)return s(o,e,n),!0}return e[t]=n,!0}};return t=>new e(t,n)},{isExtensionContext:we,Array:ge,Number:ye,String:me,Object:be}=G,{isArray:ve}=ge,{getOwnPropertyDescriptor:Ee,setPrototypeOf:Se}=be,{toString:Me}=be.prototype,{slice:Te}=me.prototype,{get:xe}=Ee(Node.prototype,"nodeType"),Oe=we?{}:{Attr:he(Attr),CanvasRenderingContext2D:he(CanvasRenderingContext2D),CSSStyleDeclaration:he(CSSStyleDeclaration),Document:he(Document),Element:he(Element),HTMLCanvasElement:he(HTMLCanvasElement),HTMLElement:he(HTMLElement),HTMLImageElement:he(HTMLImageElement),HTMLScriptElement:he(HTMLScriptElement),MutationRecord:he(MutationRecord),Node:he(Node),ShadowRoot:he(ShadowRoot),get CSS2Properties(){return Oe.CSSStyleDeclaration}},Pe=(e,t)=>{if("Element"!==t&&t in Oe)return Oe[t](e);if(ve(e))return Se(e,ge.prototype);const r=(e=>s(T
|
||
|
+const snippets=Pr;
|
||
|
+let context;
|
||
|
+for (const [name, ...args] of filters) {
|
||
|
+if (snippets.hasOwnProperty(name)) {
|
||
|
+try { context = snippets[name].apply(context, args); }
|
||
|
+catch (error) { console.error(error); }
|
||
|
+}
|
||
|
+}
|
||
|
+context = void 0;
|
||
|
+};
|
||
|
+const graph = new Map([["abort-current-inline-script",null],["abort-on-iframe-property-read",null],["abort-on-iframe-property-write",null],["abort-on-property-read",null],["abort-on-property-write",null],["cookie-remover",null],["debug",null],["freeze-element",null],["hide-if-shadow-contains",null],["json-override",null],["json-prune",null],["override-property-read",null],["prevent-listener",null],["strip-fetch-query-parameter",null],["trace",null]]);
|
||
|
+callback.get = snippet => graph.get(snippet);
|
||
|
+callback.has = snippet => graph.has(snippet);
|
||
|
+
|
||
|
+ if (t.every(([name]) => !callback.has(name))) return;
|
||
|
+ const append = () => {
|
||
|
+ URL.revokeObjectURL(
|
||
|
+ Object.assign(
|
||
|
+ document.documentElement.appendChild(document.createElement("script")),
|
||
|
+ {async: false, src: URL.createObjectURL(new Blob([
|
||
|
+ "(" + callback + ")(..." + JSON.stringify([e, ...t]) + ")"
|
||
|
+ ]))}
|
||
|
+ ).src
|
||
|
+ );
|
||
|
+ };
|
||
|
+ try { append(); }
|
||
|
+ catch (_) {
|
||
|
+ document.addEventListener("readystatechange", append, {once:true});
|
||
|
+ }
|
||
|
+}
|
||
|
\ No newline at end of file
|
||
|
diff --git a/components/resources/adblocking/snippets/dist/isolated-first-xpath3.source.jst b/components/resources/adblocking/snippets/dist/isolated-first-xpath3.source.jst
|
||
|
new file mode 100644
|
||
|
--- /dev/null
|
||
|
+++ b/components/resources/adblocking/snippets/dist/isolated-first-xpath3.source.jst
|
||
|
@@ -0,0 +1,3696 @@
|
||
|
+(e, ...t) => {
|
||
|
+/*!
|
||
|
+ * snippets v0.8.1 (https://gitlab.com/eyeo/anti-cv/snippets/-/tree/5f54b58f039a604088d65991f2829a9a081832f6/dist)
|
||
|
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
|
||
|
+ * Copyright (C) 2006-present eyeo GmbH
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is free software: you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License version 3 as
|
||
|
+ * published by the Free Software Foundation.
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License
|
||
|
+ * along with @eyeo/snippets. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+ */
|
||
|
+ ((environment, ...filters) => {
|
||
|
+ /*!
|
||
|
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
|
||
|
+ * Copyright (C) 2006-present eyeo GmbH
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is free software: you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License version 3 as
|
||
|
+ * published by the Free Software Foundation.
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License
|
||
|
+ * along with @eyeo/snippets. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+ */
|
||
|
+ const $$1 = Proxy;
|
||
|
+
|
||
|
+ const {apply: a, bind: b, call: c} = Function;
|
||
|
+ const apply$2 = c.bind(a);
|
||
|
+ const bind = c.bind(b);
|
||
|
+ const call = c.bind(c);
|
||
|
+
|
||
|
+ const callerHandler = {
|
||
|
+ get(target, name) {
|
||
|
+ return bind(c, target[name]);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const caller = target => new $$1(target, callerHandler);
|
||
|
+
|
||
|
+ const handler$2 = {
|
||
|
+ get(target, name) {
|
||
|
+ return bind(target[name], target);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const bound = target => new $$1(target, handler$2);
|
||
|
+
|
||
|
+ const {
|
||
|
+ assign: assign$1,
|
||
|
+ defineProperties: defineProperties$1,
|
||
|
+ freeze: freeze$1,
|
||
|
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$2,
|
||
|
+ getOwnPropertyDescriptors: getOwnPropertyDescriptors$1,
|
||
|
+ getPrototypeOf
|
||
|
+ } = bound(Object);
|
||
|
+
|
||
|
+ const {hasOwnProperty} = caller({});
|
||
|
+
|
||
|
+ const {species} = Symbol;
|
||
|
+
|
||
|
+ const handler$1 = {
|
||
|
+ get(target, name) {
|
||
|
+ const Native = target[name];
|
||
|
+ class Secure extends Native {}
|
||
|
+
|
||
|
+ const proto = getOwnPropertyDescriptors$1(Native.prototype);
|
||
|
+ delete proto.constructor;
|
||
|
+ freeze$1(defineProperties$1(Secure.prototype, proto));
|
||
|
+
|
||
|
+ const statics = getOwnPropertyDescriptors$1(Native);
|
||
|
+ delete statics.length;
|
||
|
+ delete statics.prototype;
|
||
|
+ statics[species] = {value: Secure};
|
||
|
+ return freeze$1(defineProperties$1(Secure, statics));
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const secure = target => new $$1(target, handler$1);
|
||
|
+
|
||
|
+ const libEnvironment = typeof environment !== "undefined" ? environment :
|
||
|
+ {};
|
||
|
+
|
||
|
+ if (typeof globalThis === "undefined")
|
||
|
+ window.globalThis = window;
|
||
|
+
|
||
|
+ const {apply: apply$1, ownKeys} = bound(Reflect);
|
||
|
+
|
||
|
+ const worldEnvDefined = "world" in libEnvironment;
|
||
|
+ const isIsolatedWorld = worldEnvDefined && libEnvironment.world === "ISOLATED";
|
||
|
+ const isMainWorld = worldEnvDefined && libEnvironment.world === "MAIN";
|
||
|
+ const isChrome = typeof chrome === "object" && !!chrome.runtime;
|
||
|
+ const isOtherThanChrome = typeof browser === "object" && !!browser.runtime;
|
||
|
+ const isExtensionContext$2 = !isMainWorld &&
|
||
|
+ (isIsolatedWorld || isChrome || isOtherThanChrome);
|
||
|
+ const copyIfExtension = value => isExtensionContext$2 ?
|
||
|
+ value :
|
||
|
+ create(value, getOwnPropertyDescriptors(value));
|
||
|
+
|
||
|
+ const {
|
||
|
+ create,
|
||
|
+ defineProperties,
|
||
|
+ defineProperty,
|
||
|
+ freeze,
|
||
|
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$1,
|
||
|
+ getOwnPropertyDescriptors
|
||
|
+ } = bound(Object);
|
||
|
+
|
||
|
+ const invokes = bound(globalThis);
|
||
|
+ const classes = isExtensionContext$2 ? globalThis : secure(globalThis);
|
||
|
+ const {Map: Map$5, RegExp: RegExp$1, Set, WeakMap: WeakMap$3, WeakSet: WeakSet$b} = classes;
|
||
|
+
|
||
|
+ const augment = (source, target, method = null) => {
|
||
|
+ const known = ownKeys(target);
|
||
|
+ for (const key of ownKeys(source)) {
|
||
|
+ if (known.includes(key))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ const descriptor = getOwnPropertyDescriptor$1(source, key);
|
||
|
+ if (method && "value" in descriptor) {
|
||
|
+ const {value} = descriptor;
|
||
|
+ if (typeof value === "function")
|
||
|
+ descriptor.value = method(value);
|
||
|
+ }
|
||
|
+ defineProperty(target, key, descriptor);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const primitive = name => {
|
||
|
+ const Super = classes[name];
|
||
|
+ class Class extends Super {}
|
||
|
+ const {toString, valueOf} = Super.prototype;
|
||
|
+ defineProperties(Class.prototype, {
|
||
|
+ toString: {value: toString},
|
||
|
+ valueOf: {value: valueOf}
|
||
|
+ });
|
||
|
+ const type = name.toLowerCase();
|
||
|
+ const method = callback => function() {
|
||
|
+ const result = apply$1(callback, this, arguments);
|
||
|
+ return typeof result === type ? new Class(result) : result;
|
||
|
+ };
|
||
|
+ augment(Super, Class, method);
|
||
|
+ augment(Super.prototype, Class.prototype, method);
|
||
|
+ return Class;
|
||
|
+ };
|
||
|
+
|
||
|
+ const variables$1 = freeze({
|
||
|
+ frozen: new WeakMap$3(),
|
||
|
+ hidden: new WeakSet$b(),
|
||
|
+ iframePropertiesToAbort: {
|
||
|
+ read: new Set(),
|
||
|
+ write: new Set()
|
||
|
+ },
|
||
|
+ abortedIframes: new WeakMap$3()
|
||
|
+ });
|
||
|
+
|
||
|
+ const startsCapitalized = new RegExp$1("^[A-Z]");
|
||
|
+
|
||
|
+ var env = new Proxy(new Map$5([
|
||
|
+
|
||
|
+ ["chrome", (
|
||
|
+ isExtensionContext$2 && (
|
||
|
+ (isChrome && chrome) ||
|
||
|
+ (isOtherThanChrome && browser)
|
||
|
+ )
|
||
|
+ ) || void 0],
|
||
|
+ ["isExtensionContext", isExtensionContext$2],
|
||
|
+ ["variables", variables$1],
|
||
|
+
|
||
|
+ ["console", copyIfExtension(console)],
|
||
|
+ ["document", globalThis.document],
|
||
|
+ ["performance", copyIfExtension(performance)],
|
||
|
+ ["JSON", copyIfExtension(JSON)],
|
||
|
+ ["Map", Map$5],
|
||
|
+ ["Math", copyIfExtension(Math)],
|
||
|
+ ["Number", isExtensionContext$2 ? Number : primitive("Number")],
|
||
|
+ ["RegExp", RegExp$1],
|
||
|
+ ["Set", Set],
|
||
|
+ ["String", isExtensionContext$2 ? String : primitive("String")],
|
||
|
+ ["WeakMap", WeakMap$3],
|
||
|
+ ["WeakSet", WeakSet$b],
|
||
|
+
|
||
|
+ ["MouseEvent", MouseEvent]
|
||
|
+ ]), {
|
||
|
+ get(map, key) {
|
||
|
+ if (map.has(key))
|
||
|
+ return map.get(key);
|
||
|
+
|
||
|
+ let value = globalThis[key];
|
||
|
+ if (typeof value === "function")
|
||
|
+ value = (startsCapitalized.test(key) ? classes : invokes)[key];
|
||
|
+
|
||
|
+ map.set(key, value);
|
||
|
+ return value;
|
||
|
+ },
|
||
|
+ has(map, key) {
|
||
|
+ return map.has(key);
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ class WeakValue {
|
||
|
+ has() { return false; }
|
||
|
+ set() {}
|
||
|
+ }
|
||
|
+
|
||
|
+ const helpers = {WeakSet, WeakMap, WeakValue};
|
||
|
+ const {apply} = Reflect;
|
||
|
+
|
||
|
+ function transformOnce (callback) { const {WeakSet, WeakMap, WeakValue} = (this || helpers);
|
||
|
+ const ws = new WeakSet;
|
||
|
+ const wm = new WeakMap;
|
||
|
+ const wv = new WeakValue;
|
||
|
+ return function (any) {
|
||
|
+ if (ws.has(any))
|
||
|
+ return any;
|
||
|
+
|
||
|
+ if (wm.has(any))
|
||
|
+ return wm.get(any);
|
||
|
+
|
||
|
+ if (wv.has(any))
|
||
|
+ return wv.get(any);
|
||
|
+
|
||
|
+ const value = apply(callback, this, arguments);
|
||
|
+ ws.add(value);
|
||
|
+ if (value !== any)
|
||
|
+ (typeof any === 'object' && any ? wm : wv).set(any, value);
|
||
|
+ return value;
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ const {Map: Map$4, WeakMap: WeakMap$2, WeakSet: WeakSet$a, setTimeout: setTimeout$3} = env;
|
||
|
+
|
||
|
+ let cleanup = true;
|
||
|
+ let cleanUpCallback = map => {
|
||
|
+ map.clear();
|
||
|
+ cleanup = !cleanup;
|
||
|
+ };
|
||
|
+
|
||
|
+ var transformer = transformOnce.bind({
|
||
|
+ WeakMap: WeakMap$2,
|
||
|
+ WeakSet: WeakSet$a,
|
||
|
+
|
||
|
+ WeakValue: class extends Map$4 {
|
||
|
+ set(key, value) {
|
||
|
+ if (cleanup) {
|
||
|
+ cleanup = !cleanup;
|
||
|
+ setTimeout$3(cleanUpCallback, 0, this);
|
||
|
+ }
|
||
|
+ return super.set(key, value);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ const {concat, includes, join, reduce, unshift} = caller([]);
|
||
|
+
|
||
|
+ const globals = secure(globalThis);
|
||
|
+
|
||
|
+ const {
|
||
|
+ Map: Map$3,
|
||
|
+ WeakMap: WeakMap$1
|
||
|
+ } = globals;
|
||
|
+
|
||
|
+ const map = new Map$3;
|
||
|
+ const descriptors = target => {
|
||
|
+ const chain = [];
|
||
|
+ let current = target;
|
||
|
+ while (current) {
|
||
|
+ if (map.has(current))
|
||
|
+ unshift(chain, map.get(current));
|
||
|
+ else {
|
||
|
+ const descriptors = getOwnPropertyDescriptors$1(current);
|
||
|
+ map.set(current, descriptors);
|
||
|
+ unshift(chain, descriptors);
|
||
|
+ }
|
||
|
+ current = getPrototypeOf(current);
|
||
|
+ }
|
||
|
+ unshift(chain, {});
|
||
|
+ return apply$2(assign$1, null, chain);
|
||
|
+ };
|
||
|
+
|
||
|
+ const chain = source => {
|
||
|
+ const target = typeof source === 'function' ? source.prototype : source;
|
||
|
+ const chained = descriptors(target);
|
||
|
+ const handler = {
|
||
|
+ get(target, key) {
|
||
|
+ if (key in chained) {
|
||
|
+ const {value, get} = chained[key];
|
||
|
+ if (get)
|
||
|
+ return call(get, target);
|
||
|
+ if (typeof value === 'function')
|
||
|
+ return bind(value, target);
|
||
|
+ }
|
||
|
+ return target[key];
|
||
|
+ },
|
||
|
+ set(target, key, value) {
|
||
|
+ if (key in chained) {
|
||
|
+ const {set} = chained[key];
|
||
|
+ if (set) {
|
||
|
+ call(set, target, value);
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ target[key] = value;
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ };
|
||
|
+ return target => new $$1(target, handler);
|
||
|
+ };
|
||
|
+
|
||
|
+ const {
|
||
|
+ isExtensionContext: isExtensionContext$1,
|
||
|
+ Array: Array$2,
|
||
|
+ Number: Number$1,
|
||
|
+ String: String$1,
|
||
|
+ Object: Object$2
|
||
|
+ } = env;
|
||
|
+
|
||
|
+ const {isArray} = Array$2;
|
||
|
+ const {getOwnPropertyDescriptor, setPrototypeOf: setPrototypeOf$1} = Object$2;
|
||
|
+
|
||
|
+ const {toString} = Object$2.prototype;
|
||
|
+ const {slice} = String$1.prototype;
|
||
|
+ const getBrand = value => call(slice, call(toString, value), 8, -1);
|
||
|
+
|
||
|
+ const {get: nodeType} = getOwnPropertyDescriptor(Node.prototype, "nodeType");
|
||
|
+
|
||
|
+ const chained = isExtensionContext$1 ? {} : {
|
||
|
+ Attr: chain(Attr),
|
||
|
+ CanvasRenderingContext2D: chain(CanvasRenderingContext2D),
|
||
|
+ CSSStyleDeclaration: chain(CSSStyleDeclaration),
|
||
|
+ Document: chain(Document),
|
||
|
+ Element: chain(Element),
|
||
|
+ HTMLCanvasElement: chain(HTMLCanvasElement),
|
||
|
+ HTMLElement: chain(HTMLElement),
|
||
|
+ HTMLImageElement: chain(HTMLImageElement),
|
||
|
+ HTMLScriptElement: chain(HTMLScriptElement),
|
||
|
+ MutationRecord: chain(MutationRecord),
|
||
|
+ Node: chain(Node),
|
||
|
+ ShadowRoot: chain(ShadowRoot),
|
||
|
+
|
||
|
+ get CSS2Properties() {
|
||
|
+ return chained.CSSStyleDeclaration;
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const upgrade = (value, hint) => {
|
||
|
+ if (hint !== "Element" && hint in chained)
|
||
|
+ return chained[hint](value);
|
||
|
+
|
||
|
+ if (isArray(value))
|
||
|
+ return setPrototypeOf$1(value, Array$2.prototype);
|
||
|
+
|
||
|
+ const brand = getBrand(value);
|
||
|
+ if (brand in chained)
|
||
|
+ return chained[brand](value);
|
||
|
+
|
||
|
+ if (brand in env)
|
||
|
+ return setPrototypeOf$1(value, env[brand].prototype);
|
||
|
+
|
||
|
+ if ("nodeType" in value) {
|
||
|
+ switch (call(nodeType, value)) {
|
||
|
+ case 1:
|
||
|
+ if (!(hint in chained))
|
||
|
+ throw new Error("unknown hint " + hint);
|
||
|
+ return chained[hint](value);
|
||
|
+ case 2:
|
||
|
+ return chained.Attr(value);
|
||
|
+ case 3:
|
||
|
+ return chained.Node(value);
|
||
|
+ case 9:
|
||
|
+ return chained.Document(value);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ throw new Error("unknown brand " + brand);
|
||
|
+ };
|
||
|
+
|
||
|
+ var $ = isExtensionContext$1 ?
|
||
|
+ value => (value === window || value === globalThis ? env : value) :
|
||
|
+ transformer((value, hint = "Element") => {
|
||
|
+ if (value === window || value === globalThis)
|
||
|
+ return env;
|
||
|
+
|
||
|
+ switch (typeof value) {
|
||
|
+ case "object":
|
||
|
+ return value && upgrade(value, hint);
|
||
|
+
|
||
|
+ case "string":
|
||
|
+ return new String$1(value);
|
||
|
+
|
||
|
+ case "number":
|
||
|
+ return new Number$1(value);
|
||
|
+
|
||
|
+ default:
|
||
|
+ throw new Error("unsupported value");
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ let {
|
||
|
+ document: document$1,
|
||
|
+ getComputedStyle: getComputedStyle$5,
|
||
|
+ isExtensionContext,
|
||
|
+ variables,
|
||
|
+ Array: Array$1,
|
||
|
+ MutationObserver: MutationObserver$a,
|
||
|
+ Object: Object$1,
|
||
|
+ XPathEvaluator,
|
||
|
+ XPathExpression,
|
||
|
+ XPathResult
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ let {querySelectorAll} = document$1;
|
||
|
+ let $$ = querySelectorAll && bind(querySelectorAll, document$1);
|
||
|
+
|
||
|
+ const {assign, setPrototypeOf} = Object$1;
|
||
|
+
|
||
|
+ class $XPathExpression extends XPathExpression {
|
||
|
+ evaluate(...args) {
|
||
|
+ return setPrototypeOf(
|
||
|
+ apply$2(super.evaluate, this, args),
|
||
|
+ XPathResult.prototype
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ class $XPathEvaluator extends XPathEvaluator {
|
||
|
+ createExpression(...args) {
|
||
|
+ return setPrototypeOf(
|
||
|
+ apply$2(super.createExpression, this, args),
|
||
|
+ $XPathExpression.prototype
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function hideElement(element) {
|
||
|
+ if (variables.hidden.has(element))
|
||
|
+ return;
|
||
|
+
|
||
|
+ notifyElementHidden(element);
|
||
|
+
|
||
|
+ variables.hidden.add(element);
|
||
|
+
|
||
|
+ let {style} = $(element);
|
||
|
+ let $style = $(style, "CSSStyleDeclaration");
|
||
|
+ let properties = $([]);
|
||
|
+ let {debugCSSProperties} = libEnvironment;
|
||
|
+
|
||
|
+ for (let [key, value] of (debugCSSProperties || [["display", "none"]])) {
|
||
|
+ $style.setProperty(key, value, "important");
|
||
|
+ properties.push([key, $style.getPropertyValue(key)]);
|
||
|
+ }
|
||
|
+
|
||
|
+ new MutationObserver$a(() => {
|
||
|
+ for (let [key, value] of properties) {
|
||
|
+ let propertyValue = $style.getPropertyValue(key);
|
||
|
+ let propertyPriority = $style.getPropertyPriority(key);
|
||
|
+ if (propertyValue != value || propertyPriority != "important")
|
||
|
+ $style.setProperty(key, value, "important");
|
||
|
+ }
|
||
|
+ }).observe(element, {attributes: true,
|
||
|
+ attributeFilter: ["style"]});
|
||
|
+ }
|
||
|
+
|
||
|
+ function notifyElementHidden(element) {
|
||
|
+ if (isExtensionContext && typeof checkElement === "function")
|
||
|
+ checkElement(element);
|
||
|
+ }
|
||
|
+
|
||
|
+ function initQueryAndApply(selector) {
|
||
|
+ let $selector = selector;
|
||
|
+ if ($selector.startsWith("xpath(") &&
|
||
|
+ $selector.endsWith(")")) {
|
||
|
+ let xpathQuery = $selector.slice(6, -1);
|
||
|
+ let evaluator = new $XPathEvaluator();
|
||
|
+ let expression = evaluator.createExpression(xpathQuery, null);
|
||
|
+
|
||
|
+ let flag = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
|
||
|
+
|
||
|
+ return cb => {
|
||
|
+ if (!cb)
|
||
|
+ return;
|
||
|
+ let result = expression.evaluate(document$1, flag, null);
|
||
|
+ let {snapshotLength} = result;
|
||
|
+ for (let i = 0; i < snapshotLength; i++)
|
||
|
+ cb(result.snapshotItem(i));
|
||
|
+ };
|
||
|
+ }
|
||
|
+ return cb => $$(selector).forEach(cb);
|
||
|
+ }
|
||
|
+
|
||
|
+ function initQueryAll(selector) {
|
||
|
+ let $selector = selector;
|
||
|
+ if ($selector.startsWith("xpath(") &&
|
||
|
+ $selector.endsWith(")")) {
|
||
|
+ let queryAndApply = initQueryAndApply(selector);
|
||
|
+ return () => {
|
||
|
+ let elements = $([]);
|
||
|
+ queryAndApply(e => elements.push(e));
|
||
|
+ return elements;
|
||
|
+ };
|
||
|
+ }
|
||
|
+ return () => Array$1.from($$(selector));
|
||
|
+ }
|
||
|
+
|
||
|
+ function hideIfMatches(match, selector, searchSelector, onHideCallback) {
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ let won;
|
||
|
+ const callback = () => {
|
||
|
+ for (const element of $$(searchSelector)) {
|
||
|
+ const closest = $(element).closest(selector);
|
||
|
+ if (closest && match(element, closest)) {
|
||
|
+ won();
|
||
|
+ hideElement(closest);
|
||
|
+ if (typeof onHideCallback === "function")
|
||
|
+ onHideCallback(closest);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+ return assign(
|
||
|
+ new MutationObserver$a(callback),
|
||
|
+ {
|
||
|
+ race(win) {
|
||
|
+ won = win;
|
||
|
+ this.observe(document$1, {childList: true,
|
||
|
+ characterData: true,
|
||
|
+ subtree: true});
|
||
|
+ callback();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ );
|
||
|
+ }
|
||
|
+
|
||
|
+ function isVisible(element, style, closest) {
|
||
|
+ let $style = $(style, "CSSStyleDeclaration");
|
||
|
+ if ($style.getPropertyValue("display") == "none")
|
||
|
+ return false;
|
||
|
+
|
||
|
+ let visibility = $style.getPropertyValue("visibility");
|
||
|
+ if (visibility == "hidden" || visibility == "collapse")
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if (!closest || element == closest)
|
||
|
+ return true;
|
||
|
+
|
||
|
+ let parent = $(element).parentElement;
|
||
|
+ if (!parent)
|
||
|
+ return true;
|
||
|
+
|
||
|
+ return isVisible(parent, getComputedStyle$5(parent), closest);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getComputedCSSText(element) {
|
||
|
+ let style = getComputedStyle$5(element);
|
||
|
+ let {cssText} = style;
|
||
|
+
|
||
|
+ if (cssText)
|
||
|
+ return cssText;
|
||
|
+
|
||
|
+ for (let property of style)
|
||
|
+ cssText += `${property}: ${style[property]}; `;
|
||
|
+
|
||
|
+ return $(cssText).trim();
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Math: Math$2, RegExp} = $(window);
|
||
|
+
|
||
|
+ function regexEscape(string) {
|
||
|
+ return $(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||
|
+ }
|
||
|
+
|
||
|
+ function toRegExp(pattern) {
|
||
|
+ let {length} = pattern;
|
||
|
+
|
||
|
+ if (length > 1 && pattern[0] === "/") {
|
||
|
+ let isCaseSensitive = pattern[length - 1] === "/";
|
||
|
+
|
||
|
+ if (isCaseSensitive || (length > 2 && $(pattern).endsWith("/i"))) {
|
||
|
+ let args = [$(pattern).slice(1, isCaseSensitive ? -1 : -2)];
|
||
|
+ if (!isCaseSensitive)
|
||
|
+ args.push("i");
|
||
|
+
|
||
|
+ return new RegExp(...args);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return new RegExp(regexEscape(pattern));
|
||
|
+ }
|
||
|
+
|
||
|
+ let debugging = false;
|
||
|
+
|
||
|
+ function debug() {
|
||
|
+ return debugging;
|
||
|
+ }
|
||
|
+
|
||
|
+ function setDebug() {
|
||
|
+ debugging = true;
|
||
|
+ }
|
||
|
+
|
||
|
+ const {console: console$1} = $(window);
|
||
|
+
|
||
|
+ const noop = () => {};
|
||
|
+
|
||
|
+ function log(...args) {
|
||
|
+ if (debug())
|
||
|
+ $(args).unshift("%c DEBUG", "font-weight: bold");
|
||
|
+
|
||
|
+ console$1.log(...args);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getDebugger(name) {
|
||
|
+ return bind(debug() ? log : noop, null, name);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Array, Error: Error$3, Map: Map$2, parseInt: parseInt$2} = $(window);
|
||
|
+
|
||
|
+ let stack = null;
|
||
|
+ let won = null;
|
||
|
+
|
||
|
+ function race(action, winners = "1") {
|
||
|
+ switch (action) {
|
||
|
+ case "start":
|
||
|
+ stack = {
|
||
|
+ winners: parseInt$2(winners, 10) || 1,
|
||
|
+ participants: new Map$2()
|
||
|
+ };
|
||
|
+ won = new Array();
|
||
|
+ break;
|
||
|
+ case "end":
|
||
|
+ case "finish":
|
||
|
+ case "stop":
|
||
|
+ stack = null;
|
||
|
+ for (let win of won)
|
||
|
+ win();
|
||
|
+ won = null;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ throw new Error$3(`Invalid action: ${action}`);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function raceWinner(name, lose) {
|
||
|
+
|
||
|
+ if (stack === null)
|
||
|
+ return noop;
|
||
|
+
|
||
|
+ let current = stack;
|
||
|
+ let {participants} = current;
|
||
|
+ participants.set(win, lose);
|
||
|
+
|
||
|
+ return win;
|
||
|
+
|
||
|
+ function win() {
|
||
|
+
|
||
|
+ if (current.winners < 1)
|
||
|
+ return;
|
||
|
+
|
||
|
+ let debugLog = getDebugger("race");
|
||
|
+ debugLog(`${name} won the race`);
|
||
|
+
|
||
|
+ if (current === stack) {
|
||
|
+ won.push(win);
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ participants.delete(win);
|
||
|
+ if (--current.winners < 1) {
|
||
|
+ for (let looser of participants.values())
|
||
|
+ looser();
|
||
|
+
|
||
|
+ participants.clear();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function hideIfContains(search, selector = "*", searchSelector = null) {
|
||
|
+ const debugLog = getDebugger("hide-if-contains");
|
||
|
+ const onHideCallback = node => {
|
||
|
+ debugLog("Matched: ", node, " for selector: ", selector, searchSelector);
|
||
|
+ };
|
||
|
+ let re = toRegExp(search);
|
||
|
+
|
||
|
+ const mo = hideIfMatches(element => re.test($(element).textContent),
|
||
|
+ selector,
|
||
|
+ searchSelector,
|
||
|
+ onHideCallback);
|
||
|
+ mo.race(raceWinner(
|
||
|
+ "hide-if-contains",
|
||
|
+ () => {
|
||
|
+ mo.disconnect();
|
||
|
+ }
|
||
|
+ ));
|
||
|
+ }
|
||
|
+
|
||
|
+ const handler = {
|
||
|
+ get(target, name) {
|
||
|
+ const context = target;
|
||
|
+ while (!hasOwnProperty(target, name))
|
||
|
+ target = getPrototypeOf(target);
|
||
|
+ const {get, set} = getOwnPropertyDescriptor$2(target, name);
|
||
|
+ return function () {
|
||
|
+ return arguments.length ?
|
||
|
+ apply$2(set, context, arguments) :
|
||
|
+ call(get, context);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const accessor = target => new $$1(target, handler);
|
||
|
+
|
||
|
+ $(window);
|
||
|
+
|
||
|
+ accessor(window);
|
||
|
+
|
||
|
+ $(/^\d+$/);
|
||
|
+
|
||
|
+ function getPromiseFromEvent(item, event) {
|
||
|
+ return new Promise(
|
||
|
+ resolve => {
|
||
|
+ const listener = () => {
|
||
|
+ item.removeEventListener(event, listener);
|
||
|
+ resolve();
|
||
|
+ };
|
||
|
+ item.addEventListener(event, listener);
|
||
|
+ }
|
||
|
+ );
|
||
|
+ }
|
||
|
+
|
||
|
+ function waitUntilEvent(
|
||
|
+ debugLog,
|
||
|
+ mainLogic,
|
||
|
+ waitUntil) {
|
||
|
+ if (waitUntil) {
|
||
|
+
|
||
|
+ if (waitUntil === "load") {
|
||
|
+ debugLog("Waiting until window.load");
|
||
|
+
|
||
|
+ window.onload = () => {
|
||
|
+ debugLog("Window.load fired.");
|
||
|
+ mainLogic();
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ else if (waitUntil === "loading" ||
|
||
|
+ waitUntil === "interactive" ||
|
||
|
+ waitUntil === "complete") {
|
||
|
+ debugLog("Waiting document state until :", waitUntil);
|
||
|
+
|
||
|
+ document.onreadystatechange = () => {
|
||
|
+ debugLog("Document state changed:", document.readyState);
|
||
|
+ if (document.readyState === waitUntil)
|
||
|
+ mainLogic();
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ else {
|
||
|
+ debugLog("Waiting until ", waitUntil, " event is triggered on document");
|
||
|
+ getPromiseFromEvent(document, waitUntil).then(() => {
|
||
|
+ debugLog(waitUntil, " is triggered on document, starting the snippet");
|
||
|
+ mainLogic();
|
||
|
+ }).catch(err => {
|
||
|
+ debugLog("There was an error while waiting for the event.", err);
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+
|
||
|
+ mainLogic();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {MutationObserver: MutationObserver$9, WeakSet: WeakSet$9, getComputedStyle: getComputedStyle$4} = $(window);
|
||
|
+
|
||
|
+ function hideIfContainsAndMatchesStyle(search,
|
||
|
+ selector = "*",
|
||
|
+ searchSelector = null,
|
||
|
+ style = null,
|
||
|
+ searchStyle = null,
|
||
|
+ waitUntil,
|
||
|
+ windowWidthMin = null,
|
||
|
+ windowWidthMax = null
|
||
|
+ ) {
|
||
|
+ const debugLog = getDebugger("hide-if-contains-and-matches-style");
|
||
|
+ const hiddenMap = new WeakSet$9();
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ const searchRegExp = toRegExp(search);
|
||
|
+
|
||
|
+ const styleRegExp = style ? toRegExp(style) : null;
|
||
|
+ const searchStyleRegExp = searchStyle ? toRegExp(searchStyle) : null;
|
||
|
+ const mainLogic = () => {
|
||
|
+ const callback = () => {
|
||
|
+ if ((windowWidthMin && window.innerWidth < windowWidthMin) ||
|
||
|
+ (windowWidthMax && window.innerWidth > windowWidthMax)
|
||
|
+ )
|
||
|
+ return;
|
||
|
+ for (const element of $$(searchSelector)) {
|
||
|
+ if (hiddenMap.has(element))
|
||
|
+ continue;
|
||
|
+ if (searchRegExp.test($(element).textContent)) {
|
||
|
+ if (!searchStyleRegExp ||
|
||
|
+ searchStyleRegExp.test(getComputedCSSText(element))) {
|
||
|
+ const closest = $(element).closest(selector);
|
||
|
+ if (!closest)
|
||
|
+ continue;
|
||
|
+ if (!styleRegExp || styleRegExp.test(getComputedCSSText(closest))) {
|
||
|
+ win();
|
||
|
+ hideElement(closest);
|
||
|
+ hiddenMap.add(element);
|
||
|
+ debugLog("Matched: ",
|
||
|
+ closest,
|
||
|
+ "which contains: ",
|
||
|
+ element,
|
||
|
+ " for params: ",
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ debugLog(
|
||
|
+ "In this element the searchStyle matched but style didn't:\n",
|
||
|
+ closest,
|
||
|
+ getComputedStyle$4(closest),
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ debugLog(
|
||
|
+ "In this element the searchStyle didn't match:\n",
|
||
|
+ element,
|
||
|
+ getComputedStyle$4(element),
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const mo = new MutationObserver$9(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-contains-and-matches-style",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {childList: true, characterData: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ clearTimeout,
|
||
|
+ fetch,
|
||
|
+ getComputedStyle: getComputedStyle$3,
|
||
|
+ setTimeout: setTimeout$2,
|
||
|
+ Map: Map$1,
|
||
|
+ MutationObserver: MutationObserver$8,
|
||
|
+ Uint8Array
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ function hideIfContainsImage(search, selector, searchSelector) {
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ let searchRegExp = toRegExp(search);
|
||
|
+
|
||
|
+ const debugLog = getDebugger("hide-if-contains-image");
|
||
|
+
|
||
|
+ let callback = () => {
|
||
|
+ for (let element of $$(searchSelector)) {
|
||
|
+ let style = getComputedStyle$3(element);
|
||
|
+ let match = $(style["background-image"]).match(/^url\("(.*)"\)$/);
|
||
|
+ if (match) {
|
||
|
+ fetchContent(match[1]).then(content => {
|
||
|
+ if (searchRegExp.test(uint8ArrayToHex(new Uint8Array(content)))) {
|
||
|
+ let closest = $(element).closest(selector);
|
||
|
+ if (closest) {
|
||
|
+ win();
|
||
|
+ hideElement(closest);
|
||
|
+ debugLog("Matched: ", closest, " for:", ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ let mo = new MutationObserver$8(callback);
|
||
|
+ let win = raceWinner(
|
||
|
+ "hide-if-contains-image",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ }
|
||
|
+
|
||
|
+ let fetchContentMap = new Map$1();
|
||
|
+
|
||
|
+ function fetchContent(url, {as = "arrayBuffer", cleanup = 60000} = {}) {
|
||
|
+
|
||
|
+ let uid = as + ":" + url;
|
||
|
+ let details = fetchContentMap.get(uid) || {
|
||
|
+ remove: () => fetchContentMap.delete(uid),
|
||
|
+ result: null,
|
||
|
+ timer: 0
|
||
|
+ };
|
||
|
+ clearTimeout(details.timer);
|
||
|
+ details.timer = setTimeout$2(details.remove, cleanup);
|
||
|
+ if (!details.result) {
|
||
|
+ details.result = fetch(url).then(res => res[as]()).catch(details.remove);
|
||
|
+ fetchContentMap.set(uid, details);
|
||
|
+ }
|
||
|
+ return details.result;
|
||
|
+ }
|
||
|
+
|
||
|
+ function toHex(number, length = 2) {
|
||
|
+ let hex = $(number).toString(16);
|
||
|
+
|
||
|
+ if (hex.length < length)
|
||
|
+ hex = $("0").repeat(length - hex.length) + hex;
|
||
|
+
|
||
|
+ return hex;
|
||
|
+ }
|
||
|
+
|
||
|
+ function uint8ArrayToHex(uint8Array) {
|
||
|
+ return uint8Array.reduce((hex, byte) => hex + toHex(byte), "");
|
||
|
+ }
|
||
|
+
|
||
|
+ const {parseFloat: parseFloat$1, Math: Math$1, MutationObserver: MutationObserver$7, WeakSet: WeakSet$8} = $(window);
|
||
|
+ const {min} = Math$1;
|
||
|
+
|
||
|
+ const ld = (a, b) => {
|
||
|
+ const len1 = a.length + 1;
|
||
|
+ const len2 = b.length + 1;
|
||
|
+ const d = [[0]];
|
||
|
+ let i = 0;
|
||
|
+ let I = 0;
|
||
|
+
|
||
|
+ while (++i < len2)
|
||
|
+ d[0][i] = i;
|
||
|
+
|
||
|
+ i = 0;
|
||
|
+ while (++i < len1) {
|
||
|
+ const c = a[I];
|
||
|
+ let j = 0;
|
||
|
+ let J = 0;
|
||
|
+ d[i] = [i];
|
||
|
+ while (++j < len2) {
|
||
|
+ d[i][j] = min(d[I][j] + 1, d[i][J] + 1, d[I][J] + (c != b[J]));
|
||
|
+ ++J;
|
||
|
+ }
|
||
|
+ ++I;
|
||
|
+ }
|
||
|
+ return d[len1 - 1][len2 - 1];
|
||
|
+ };
|
||
|
+
|
||
|
+ function hideIfContainsSimilarText(
|
||
|
+ search, selector,
|
||
|
+ searchSelector = null,
|
||
|
+ ignoreChars = 0,
|
||
|
+ maxSearches = 0
|
||
|
+ ) {
|
||
|
+ const visitedNodes = new WeakSet$8();
|
||
|
+ const debugLog = getDebugger("hide-if-contains-similar-text");
|
||
|
+ const $search = $(search);
|
||
|
+ const {length} = $search;
|
||
|
+ const chars = length + parseFloat$1(ignoreChars) || 0;
|
||
|
+ const find = $([...$search]).sort();
|
||
|
+ const guard = parseFloat$1(maxSearches) || Infinity;
|
||
|
+
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ debugLog("Looking for similar text: " + $search);
|
||
|
+
|
||
|
+ const callback = () => {
|
||
|
+ for (const element of $$(searchSelector)) {
|
||
|
+ if (visitedNodes.has(element))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ visitedNodes.add(element);
|
||
|
+ const {innerText} = $(element);
|
||
|
+ const loop = min(guard, innerText.length - chars + 1);
|
||
|
+ for (let i = 0; i < loop; i++) {
|
||
|
+ const str = $(innerText).substr(i, chars);
|
||
|
+ const distance = ld(find, $([...str]).sort()) - ignoreChars;
|
||
|
+ if (distance <= 0) {
|
||
|
+ const closest = $(element).closest(selector);
|
||
|
+ debugLog("Found similar text: " + $search, closest);
|
||
|
+ if (closest) {
|
||
|
+ win();
|
||
|
+ hideElement(closest);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ let mo = new MutationObserver$7(callback);
|
||
|
+ let win = raceWinner(
|
||
|
+ "hide-if-contains-similar-text",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {childList: true, characterData: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ }
|
||
|
+
|
||
|
+ let {getComputedStyle: getComputedStyle$2, Map, WeakSet: WeakSet$7, parseFloat} = $(window);
|
||
|
+
|
||
|
+ const {ELEMENT_NODE: ELEMENT_NODE$3, TEXT_NODE} = Node;
|
||
|
+
|
||
|
+ function hideIfContainsVisibleText(search, selector,
|
||
|
+ searchSelector = null,
|
||
|
+ ...attributes) {
|
||
|
+ let entries = $([]);
|
||
|
+ const optionalParameters = new Map([
|
||
|
+ ["-snippet-box-margin", "2"],
|
||
|
+ ["-disable-bg-color-check", "false"],
|
||
|
+ ["-check-is-contained", "false"]
|
||
|
+ ]);
|
||
|
+
|
||
|
+ for (let attr of attributes) {
|
||
|
+ attr = $(attr);
|
||
|
+ let markerIndex = attr.indexOf(":");
|
||
|
+ if (markerIndex < 0)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ let key = attr.slice(0, markerIndex).trim().toString();
|
||
|
+ let value = attr.slice(markerIndex + 1).trim().toString();
|
||
|
+
|
||
|
+ if (key && value) {
|
||
|
+ if (optionalParameters.has(key))
|
||
|
+ optionalParameters.set(key, value);
|
||
|
+ else
|
||
|
+ entries.push([key, value]);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let defaultEntries = $([
|
||
|
+ ["opacity", "0"],
|
||
|
+ ["font-size", "0px"],
|
||
|
+
|
||
|
+ ["color", "rgba(0, 0, 0, 0)"]
|
||
|
+ ]);
|
||
|
+
|
||
|
+ let attributesMap = new Map(defaultEntries.concat(entries));
|
||
|
+
|
||
|
+ function isTextVisible(element, style, {bgColorCheck = true} = {}) {
|
||
|
+ if (!style)
|
||
|
+ style = getComputedStyle$2(element);
|
||
|
+
|
||
|
+ style = $(style);
|
||
|
+
|
||
|
+ for (const [key, value] of attributesMap) {
|
||
|
+ let valueAsRegex = toRegExp(value);
|
||
|
+ if (valueAsRegex.test(style.getPropertyValue(key)))
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ let color = style.getPropertyValue("color");
|
||
|
+ if (bgColorCheck && style.getPropertyValue("background-color") == color)
|
||
|
+ return false;
|
||
|
+
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ function getPseudoContent(element, pseudo, {bgColorCheck = true} = {}) {
|
||
|
+ let style = getComputedStyle$2(element, pseudo);
|
||
|
+ if (!isVisible(element, style) ||
|
||
|
+ !isTextVisible(element, style, {bgColorCheck}))
|
||
|
+ return "";
|
||
|
+
|
||
|
+ let {content} = $(style);
|
||
|
+ if (content && content !== "none") {
|
||
|
+ let strings = $([]);
|
||
|
+
|
||
|
+ content = $(content).trim().replace(
|
||
|
+ /(["'])(?:(?=(\\?))\2.)*?\1/g,
|
||
|
+ value => `\x01${strings.push($(value).slice(1, -1)) - 1}`
|
||
|
+ );
|
||
|
+
|
||
|
+ content = content.replace(
|
||
|
+ /\s*attr\(\s*([^\s,)]+)[^)]*?\)\s*/g,
|
||
|
+ (_, name) => $(element).getAttribute(name) || ""
|
||
|
+ );
|
||
|
+
|
||
|
+ return content.replace(
|
||
|
+ /\x01(\d+)/g,
|
||
|
+ (_, index) => strings[index]);
|
||
|
+ }
|
||
|
+ return "";
|
||
|
+ }
|
||
|
+
|
||
|
+ function isContained(childNode, parentNode, {boxMargin = 2} = {}) {
|
||
|
+ const child = $(childNode).getBoundingClientRect();
|
||
|
+ const parent = $(parentNode).getBoundingClientRect();
|
||
|
+ const stretchedParent = {
|
||
|
+ left: parent.left - boxMargin,
|
||
|
+ right: parent.right + boxMargin,
|
||
|
+ top: parent.top - boxMargin,
|
||
|
+ bottom: parent.bottom + boxMargin
|
||
|
+ };
|
||
|
+ return (
|
||
|
+ (stretchedParent.left <= child.left &&
|
||
|
+ child.left <= stretchedParent.right &&
|
||
|
+ stretchedParent.top <= child.top &&
|
||
|
+ child.top <= stretchedParent.bottom) &&
|
||
|
+ (stretchedParent.top <= child.bottom &&
|
||
|
+ child.bottom <= stretchedParent.bottom &&
|
||
|
+ stretchedParent.left <= child.right &&
|
||
|
+ child.right <= stretchedParent.right)
|
||
|
+ );
|
||
|
+ }
|
||
|
+
|
||
|
+ function getVisibleContent(element,
|
||
|
+ closest,
|
||
|
+ style,
|
||
|
+ parentOverflowNode,
|
||
|
+ originalElement,
|
||
|
+ {
|
||
|
+ boxMargin = 2,
|
||
|
+ bgColorCheck,
|
||
|
+ checkIsContained
|
||
|
+ } = {}) {
|
||
|
+ let checkClosest = !style;
|
||
|
+ if (checkClosest)
|
||
|
+ style = getComputedStyle$2(element);
|
||
|
+
|
||
|
+ if (!isVisible(element, style, checkClosest && closest))
|
||
|
+ return "";
|
||
|
+
|
||
|
+ if (!parentOverflowNode &&
|
||
|
+ (
|
||
|
+ $(style).getPropertyValue("overflow-x") === "hidden" ||
|
||
|
+ $(style).getPropertyValue("overflow-y") === "hidden"
|
||
|
+ )
|
||
|
+ )
|
||
|
+ parentOverflowNode = element;
|
||
|
+
|
||
|
+ let text = getPseudoContent(element, ":before", {bgColorCheck});
|
||
|
+ for (let node of $(element).childNodes) {
|
||
|
+ switch ($(node).nodeType) {
|
||
|
+ case ELEMENT_NODE$3:
|
||
|
+ text += getVisibleContent(node,
|
||
|
+ element,
|
||
|
+ getComputedStyle$2(node),
|
||
|
+ parentOverflowNode,
|
||
|
+ originalElement,
|
||
|
+ {
|
||
|
+ boxMargin,
|
||
|
+ bgColorCheck,
|
||
|
+ checkIsContained
|
||
|
+ }
|
||
|
+ );
|
||
|
+ break;
|
||
|
+ case TEXT_NODE:
|
||
|
+
|
||
|
+ if (parentOverflowNode) {
|
||
|
+ if (isContained(element, parentOverflowNode, {boxMargin}) &&
|
||
|
+ isTextVisible(element, style, {bgColorCheck}))
|
||
|
+ text += $(node).nodeValue;
|
||
|
+ }
|
||
|
+ else if (isTextVisible(element, style, {bgColorCheck})) {
|
||
|
+ if (checkIsContained &&
|
||
|
+ !isContained(element, originalElement, {boxMargin}))
|
||
|
+ continue;
|
||
|
+ text += $(node).nodeValue;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return text + getPseudoContent(element, ":after", {bgColorCheck});
|
||
|
+ }
|
||
|
+ const boxMarginStr = optionalParameters.get("-snippet-box-margin");
|
||
|
+ const boxMargin = parseFloat(boxMarginStr) || 0;
|
||
|
+
|
||
|
+ const bgColorCheckStr = optionalParameters.get("-disable-bg-color-check");
|
||
|
+ const bgColorCheck = !(bgColorCheckStr === "true");
|
||
|
+
|
||
|
+ const checkIsContainedStr = optionalParameters.get("-check-is-contained");
|
||
|
+ const checkIsContained = (checkIsContainedStr === "true");
|
||
|
+
|
||
|
+ let re = toRegExp(search);
|
||
|
+ let seen = new WeakSet$7();
|
||
|
+
|
||
|
+ const mo = hideIfMatches(
|
||
|
+ (element, closest) => {
|
||
|
+ if (seen.has(element))
|
||
|
+ return false;
|
||
|
+
|
||
|
+ seen.add(element);
|
||
|
+ let text = getVisibleContent(element, closest, null, null, element, {
|
||
|
+ boxMargin,
|
||
|
+ bgColorCheck,
|
||
|
+ checkIsContained
|
||
|
+ }
|
||
|
+ );
|
||
|
+ let result = re.test(text);
|
||
|
+ if (debug() && text.length)
|
||
|
+ log(result, re, text);
|
||
|
+ return result;
|
||
|
+ },
|
||
|
+ selector,
|
||
|
+ searchSelector
|
||
|
+ );
|
||
|
+ mo.race(raceWinner(
|
||
|
+ "hide-if-contains-visible-text",
|
||
|
+ () => {
|
||
|
+ mo.disconnect();
|
||
|
+ }
|
||
|
+ ));
|
||
|
+ }
|
||
|
+
|
||
|
+ let {MutationObserver: MutationObserver$6, WeakSet: WeakSet$6, getComputedStyle: getComputedStyle$1} = $(window);
|
||
|
+
|
||
|
+ function hideIfHasAndMatchesStyle(search,
|
||
|
+ selector = "*",
|
||
|
+ searchSelector = null,
|
||
|
+ style = null,
|
||
|
+ searchStyle = null,
|
||
|
+ waitUntil = null,
|
||
|
+ windowWidthMin = null,
|
||
|
+ windowWidthMax = null
|
||
|
+ ) {
|
||
|
+ const debugLog = getDebugger("hide-if-has-and-matches-style");
|
||
|
+ const hiddenMap = new WeakSet$6();
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ const styleRegExp = style ? toRegExp(style) : null;
|
||
|
+ const searchStyleRegExp = searchStyle ? toRegExp(searchStyle) : null;
|
||
|
+ const mainLogic = () => {
|
||
|
+ const callback = () => {
|
||
|
+ if ((windowWidthMin && window.innerWidth < windowWidthMin) ||
|
||
|
+ (windowWidthMax && window.innerWidth > windowWidthMax)
|
||
|
+ )
|
||
|
+ return;
|
||
|
+ for (const element of $$(searchSelector)) {
|
||
|
+ if (hiddenMap.has(element))
|
||
|
+ continue;
|
||
|
+ if ($(element).querySelector(search) &&
|
||
|
+ (!searchStyleRegExp ||
|
||
|
+ searchStyleRegExp.test(getComputedCSSText(element)))) {
|
||
|
+ const closest = $(element).closest(selector);
|
||
|
+ if (closest && (!styleRegExp ||
|
||
|
+ styleRegExp.test(getComputedCSSText(closest)))) {
|
||
|
+ win();
|
||
|
+ hideElement(closest);
|
||
|
+ hiddenMap.add(element);
|
||
|
+ debugLog("Matched: ",
|
||
|
+ closest,
|
||
|
+ "which contains: ",
|
||
|
+ element,
|
||
|
+ " for params: ",
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ debugLog(
|
||
|
+ "In this element the searchStyle matched but style didn't:\n",
|
||
|
+ closest,
|
||
|
+ getComputedStyle$1(closest),
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ debugLog(
|
||
|
+ "In this element the searchStyle didn't match:\n",
|
||
|
+ element,
|
||
|
+ getComputedStyle$1(element),
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const mo = new MutationObserver$6(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-has-and-matches-style",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {getComputedStyle, MutationObserver: MutationObserver$5, WeakSet: WeakSet$5} = $(window);
|
||
|
+
|
||
|
+ function hideIfLabelledBy(search, selector, searchSelector = null) {
|
||
|
+ let sameSelector = searchSelector == null;
|
||
|
+
|
||
|
+ let searchRegExp = toRegExp(search);
|
||
|
+
|
||
|
+ let matched = new WeakSet$5();
|
||
|
+
|
||
|
+ let callback = () => {
|
||
|
+ for (let node of $$(selector)) {
|
||
|
+ let closest = sameSelector ?
|
||
|
+ node :
|
||
|
+ $(node).closest(searchSelector);
|
||
|
+ if (!closest || !isVisible(node, getComputedStyle(node), closest))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ let attr = $(node).getAttribute("aria-labelledby");
|
||
|
+ let fallback = () => {
|
||
|
+ if (matched.has(closest))
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (searchRegExp.test(
|
||
|
+ $(node).getAttribute("aria-label") || ""
|
||
|
+ )) {
|
||
|
+ win();
|
||
|
+ matched.add(closest);
|
||
|
+ hideElement(closest);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ if (attr) {
|
||
|
+ for (let label of $(attr).split(/\s+/)) {
|
||
|
+ let target = $(document).getElementById(label);
|
||
|
+ if (target) {
|
||
|
+ if (!matched.has(target) && searchRegExp.test(target.innerText)) {
|
||
|
+ win();
|
||
|
+ matched.add(target);
|
||
|
+ hideElement(closest);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ fallback();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ fallback();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ let mo = new MutationObserver$5(callback);
|
||
|
+ let win = raceWinner(
|
||
|
+ "hide-if-labelled-by",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {characterData: true, childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ }
|
||
|
+
|
||
|
+ $(window);
|
||
|
+
|
||
|
+ const noopProfile = {
|
||
|
+ mark() {},
|
||
|
+ end() {},
|
||
|
+ toString() {
|
||
|
+ return "{mark(){},end(){}}";
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ function profile(id, rate = 10) {
|
||
|
+ return noopProfile;
|
||
|
+ }
|
||
|
+
|
||
|
+ let {MutationObserver: MutationObserver$4, WeakSet: WeakSet$4} = $(window);
|
||
|
+
|
||
|
+ const {ELEMENT_NODE: ELEMENT_NODE$2} = Node;
|
||
|
+
|
||
|
+ function hideIfMatchesXPath(query, scopeQuery) {
|
||
|
+ const {mark, end} = profile();
|
||
|
+ const debugLog = getDebugger("hide-if-matches-xpath");
|
||
|
+
|
||
|
+ const startHidingMutationObserver = scopeNode => {
|
||
|
+ const queryAndApply = initQueryAndApply(`xpath(${query})`);
|
||
|
+ const seenMap = new WeakSet$4();
|
||
|
+ const callback = () => {
|
||
|
+ mark();
|
||
|
+ queryAndApply(node => {
|
||
|
+ if (seenMap.has(node))
|
||
|
+ return false;
|
||
|
+ seenMap.add(node);
|
||
|
+ win();
|
||
|
+ if ($(node).nodeType === ELEMENT_NODE$2)
|
||
|
+ hideElement(node);
|
||
|
+ else
|
||
|
+ $(node).textContent = "";
|
||
|
+ debugLog("Matched: ", node, " for selector: ", query);
|
||
|
+ });
|
||
|
+ end();
|
||
|
+ };
|
||
|
+ const mo = new MutationObserver$4(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-matches-xpath",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(
|
||
|
+ scopeNode, {characterData: true, childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+
|
||
|
+ if (scopeQuery) {
|
||
|
+
|
||
|
+ let count = 0;
|
||
|
+ let scopeMutationObserver;
|
||
|
+ const scopeQueryAndApply = initQueryAndApply(`xpath(${scopeQuery})`);
|
||
|
+ const findMutationScopeNodes = () => {
|
||
|
+ scopeQueryAndApply(scopeNode => {
|
||
|
+
|
||
|
+ startHidingMutationObserver(scopeNode);
|
||
|
+ count++;
|
||
|
+ });
|
||
|
+ if (count > 0)
|
||
|
+ scopeMutationObserver.disconnect();
|
||
|
+ };
|
||
|
+ scopeMutationObserver = new MutationObserver$4(findMutationScopeNodes);
|
||
|
+ scopeMutationObserver.observe(
|
||
|
+ document, {characterData: true, childList: true, subtree: true}
|
||
|
+ );
|
||
|
+ findMutationScopeNodes();
|
||
|
+ }
|
||
|
+ else {
|
||
|
+
|
||
|
+ startHidingMutationObserver(document);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {MutationObserver: MutationObserver$3, WeakSet: WeakSet$3} = $(window);
|
||
|
+
|
||
|
+ const {ELEMENT_NODE: ELEMENT_NODE$1} = Node;
|
||
|
+
|
||
|
+ function hideIfMatchesComputedXPath(query, searchQuery, searchRegex,
|
||
|
+ waitUntil) {
|
||
|
+ const {mark, end} = profile();
|
||
|
+ const debugLog = getDebugger("hide-if-matches-computed-xpath");
|
||
|
+
|
||
|
+ if (!searchQuery || !query) {
|
||
|
+ debugLog("No query or searchQuery provided.");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ const computeQuery = foundText => query.replace("{{}}", foundText);
|
||
|
+
|
||
|
+ const startHidingMutationObserver = foundText => {
|
||
|
+ const computedQuery = computeQuery(foundText);
|
||
|
+ debugLog("Starting hiding elements that match query: ", computedQuery);
|
||
|
+ const queryAndApply = initQueryAndApply(`xpath(${computedQuery})`);
|
||
|
+ const seenMap = new WeakSet$3();
|
||
|
+ const callback = () => {
|
||
|
+ mark();
|
||
|
+ queryAndApply(node => {
|
||
|
+ if (seenMap.has(node))
|
||
|
+ return false;
|
||
|
+ seenMap.add(node);
|
||
|
+ win();
|
||
|
+ if ($(node).nodeType === ELEMENT_NODE$1)
|
||
|
+ hideElement(node);
|
||
|
+ else
|
||
|
+ $(node).textContent = "";
|
||
|
+ debugLog("Matched: ", node, " for selector: ", query);
|
||
|
+ });
|
||
|
+ end();
|
||
|
+ };
|
||
|
+ const mo = new MutationObserver$3(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-matches-computed-xpath",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(
|
||
|
+ document, {characterData: true, childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+
|
||
|
+ const re = toRegExp(searchRegex);
|
||
|
+
|
||
|
+ const mainLogic = () => {
|
||
|
+ if (searchQuery) {
|
||
|
+ debugLog("Started searching for: ", searchQuery);
|
||
|
+ const seenMap = new WeakSet$3();
|
||
|
+ let searchMO;
|
||
|
+ const searchQueryAndApply = initQueryAndApply(`xpath(${searchQuery})`);
|
||
|
+ const findMutationSearchNodes = () => {
|
||
|
+ searchQueryAndApply(searchNode => {
|
||
|
+ if (seenMap.has(searchNode))
|
||
|
+ return false;
|
||
|
+ seenMap.add(searchNode);
|
||
|
+ debugLog("Found node: ", searchNode);
|
||
|
+ if (searchNode.innerHTML) {
|
||
|
+ debugLog("Searching in: ", searchNode.innerHTML);
|
||
|
+ const foundTextArr = searchNode.innerHTML.match(re);
|
||
|
+ if (foundTextArr && foundTextArr.length) {
|
||
|
+ let foundText = "";
|
||
|
+
|
||
|
+ foundTextArr[1] ? foundText = foundTextArr[1] :
|
||
|
+ foundText = foundTextArr[0];
|
||
|
+ debugLog("Matched search query: ", foundText);
|
||
|
+ startHidingMutationObserver(foundText);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+ };
|
||
|
+
|
||
|
+ searchMO = new MutationObserver$3(findMutationSearchNodes);
|
||
|
+ searchMO.observe(
|
||
|
+ document, {characterData: true, childList: true, subtree: true}
|
||
|
+ );
|
||
|
+ findMutationSearchNodes();
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ parseInt: parseInt$1,
|
||
|
+ setTimeout: setTimeout$1,
|
||
|
+ Error: Error$2,
|
||
|
+ MouseEvent: MouseEvent$2,
|
||
|
+ MutationObserver: MutationObserver$2,
|
||
|
+ WeakSet: WeakSet$2
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ function simulateEvent(event, selector, delay = "0") {
|
||
|
+ if (!event)
|
||
|
+ throw new Error$2("[simulate-event snippet]: No event type provided.");
|
||
|
+ if (!selector)
|
||
|
+ throw new Error$2("[simulate-event snippet]: No selector provided.");
|
||
|
+
|
||
|
+ let queryAndApply = initQueryAndApply(selector);
|
||
|
+ let delayInMiliseconds = parseInt$1(delay, 10);
|
||
|
+ let dispatchedNodes = new WeakSet$2();
|
||
|
+
|
||
|
+ let observer = new MutationObserver$2(findNodesAndDispatchEvents);
|
||
|
+ observer.observe(document, {childList: true, subtree: true});
|
||
|
+ findNodesAndDispatchEvents();
|
||
|
+
|
||
|
+ function findNodesAndDispatchEvents() {
|
||
|
+ queryAndApply(node => {
|
||
|
+ if (!dispatchedNodes.has(node)) {
|
||
|
+ dispatchedNodes.add(node);
|
||
|
+ setTimeout$1(() => {
|
||
|
+ $(node).dispatchEvent(
|
||
|
+ new MouseEvent$2(event, {bubbles: true, cancelable: true})
|
||
|
+ );
|
||
|
+ }, delayInMiliseconds);
|
||
|
+ }
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ parseInt,
|
||
|
+ setTimeout,
|
||
|
+ Error: Error$1,
|
||
|
+ MouseEvent: MouseEvent$1,
|
||
|
+ MutationObserver: MutationObserver$1,
|
||
|
+ WeakSet: WeakSet$1
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ const VALID_TYPES = ["auxclick", "click", "dblclick", "gotpointercapture",
|
||
|
+ "lostpointercapture", "mouseenter", "mousedown",
|
||
|
+ "mouseleave", "mousemove", "mouseout", "mouseover",
|
||
|
+ "mouseup", "pointerdown", "pointerenter",
|
||
|
+ "pointermove", "pointerover", "pointerout",
|
||
|
+ "pointerup", "pointercancel", "pointerleave"];
|
||
|
+
|
||
|
+ function simulateMouseEvent(...selectors) {
|
||
|
+ const debugLog = getDebugger("simulate-mouse-event");
|
||
|
+ const MAX_ARGS = 7;
|
||
|
+ if (selectors.length < 1)
|
||
|
+ throw new Error$1("[simulate-mouse-event snippet]: No selector provided.");
|
||
|
+ if (selectors.length > MAX_ARGS) {
|
||
|
+
|
||
|
+ selectors = selectors.slice(0, MAX_ARGS);
|
||
|
+ }
|
||
|
+ function parseArg(theRule) {
|
||
|
+ if (!theRule)
|
||
|
+ return null;
|
||
|
+
|
||
|
+ const result = {
|
||
|
+ selector: "",
|
||
|
+ continue: false,
|
||
|
+ trigger: false,
|
||
|
+ event: "click",
|
||
|
+ delay: "500",
|
||
|
+ clicked: false,
|
||
|
+ found: false
|
||
|
+ };
|
||
|
+ const textArr = theRule.split("$");
|
||
|
+ let options = [];
|
||
|
+ if (textArr.length >= 2)
|
||
|
+ options = textArr[1].toLowerCase().split(",");
|
||
|
+
|
||
|
+ [result.selector] = textArr;
|
||
|
+
|
||
|
+ for (const option of options) {
|
||
|
+ if (option === "trigger") {
|
||
|
+ result.trigger = true;
|
||
|
+ }
|
||
|
+ else if (option === "continue") {
|
||
|
+ result.continue = true;
|
||
|
+ }
|
||
|
+ else if (option.startsWith("event")) {
|
||
|
+ const event = option.toLowerCase().split("=");
|
||
|
+ event[1] ? result.event = event[1] : result.event = "click";
|
||
|
+ }
|
||
|
+ else if (option.startsWith("delay")) {
|
||
|
+ const delay = option.toLowerCase().split("=");
|
||
|
+ delay[1] ? result.delay = delay[1] : result.delay = "500";
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (!VALID_TYPES.includes(result.event)) {
|
||
|
+ debugLog(result.event,
|
||
|
+ " might be misspelled, check for typos.\n",
|
||
|
+ "These are the supported events:",
|
||
|
+ VALID_TYPES);
|
||
|
+ }
|
||
|
+ return result;
|
||
|
+ }
|
||
|
+
|
||
|
+ const parsedArgs = $([]);
|
||
|
+
|
||
|
+ $(selectors).forEach(rule => {
|
||
|
+ const parsedRule = parseArg(rule);
|
||
|
+ parsedArgs.push(parsedRule);
|
||
|
+ });
|
||
|
+
|
||
|
+ function checkIfAllSelectorsFound() {
|
||
|
+ parsedArgs.forEach(arg => {
|
||
|
+ if (!arg.found) {
|
||
|
+ const queryAll = initQueryAll(arg.selector);
|
||
|
+ const elems = queryAll();
|
||
|
+ if (elems.length > 0)
|
||
|
+ arg.found = true;
|
||
|
+ }
|
||
|
+ });
|
||
|
+ return parsedArgs.every(arg => arg.found);
|
||
|
+ }
|
||
|
+
|
||
|
+ function triggerEvent(node, event, delay) {
|
||
|
+
|
||
|
+ if (!node || !event)
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (event === "click" && node.click) {
|
||
|
+ node.click();
|
||
|
+ debugLog(
|
||
|
+ "Clicked on this node:\n", node, "\nwith a delay of", delay, "ms"
|
||
|
+ );
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ node.dispatchEvent(
|
||
|
+ new MouseEvent$1(event, {bubbles: true, cancelable: true})
|
||
|
+ );
|
||
|
+ debugLog(
|
||
|
+ "A",
|
||
|
+ event,
|
||
|
+ "event was dispatched with a delay of",
|
||
|
+ delay,
|
||
|
+ "ms on this node:\n",
|
||
|
+ node
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+ let allFound = false;
|
||
|
+
|
||
|
+ const [last] = parsedArgs.slice(-1);
|
||
|
+ last.trigger = true;
|
||
|
+
|
||
|
+ let dispatchedNodes = new WeakSet$1();
|
||
|
+
|
||
|
+ let observer = new MutationObserver$1(findNodesAndDispatchEvents);
|
||
|
+ observer.observe(document, {childList: true, subtree: true});
|
||
|
+ findNodesAndDispatchEvents();
|
||
|
+
|
||
|
+ function findNodesAndDispatchEvents() {
|
||
|
+
|
||
|
+ if (!allFound)
|
||
|
+ allFound = checkIfAllSelectorsFound();
|
||
|
+ if (allFound) {
|
||
|
+ for (const parsedRule of parsedArgs) {
|
||
|
+ const queryAndApply = initQueryAndApply(parsedRule.selector);
|
||
|
+ const delayInMiliseconds = parseInt(parsedRule.delay, 10);
|
||
|
+ if (parsedRule.trigger) {
|
||
|
+ queryAndApply(node => {
|
||
|
+ if (!dispatchedNodes.has(node)) {
|
||
|
+ dispatchedNodes.add(node);
|
||
|
+ if (parsedRule.continue) {
|
||
|
+ setInterval(() => {
|
||
|
+ triggerEvent(node, parsedRule.event, parsedRule.delay);
|
||
|
+ }, delayInMiliseconds);
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ setTimeout(() => {
|
||
|
+ triggerEvent(node, parsedRule.event, parsedRule.delay);
|
||
|
+ }, delayInMiliseconds);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ const snippets = {
|
||
|
+ log,
|
||
|
+ race,
|
||
|
+ "debug": setDebug,
|
||
|
+ "hide-if-matches-xpath": hideIfMatchesXPath,
|
||
|
+ "hide-if-matches-computed-xpath": hideIfMatchesComputedXPath,
|
||
|
+ "hide-if-contains": hideIfContains,
|
||
|
+ "hide-if-contains-similar-text": hideIfContainsSimilarText,
|
||
|
+ "hide-if-contains-visible-text": hideIfContainsVisibleText,
|
||
|
+ "hide-if-contains-and-matches-style": hideIfContainsAndMatchesStyle,
|
||
|
+ "hide-if-has-and-matches-style": hideIfHasAndMatchesStyle,
|
||
|
+ "hide-if-labelled-by": hideIfLabelledBy,
|
||
|
+ "hide-if-contains-image": hideIfContainsImage,
|
||
|
+ "simulate-event-poc": simulateEvent,
|
||
|
+ "simulate-mouse-event": simulateMouseEvent
|
||
|
+ };
|
||
|
+
|
||
|
+ let {MutationObserver} = $(window);
|
||
|
+
|
||
|
+ const {ELEMENT_NODE} = Node;
|
||
|
+
|
||
|
+ function hideIfMatchesXPath3(query, scopeQuery) {
|
||
|
+ let {mark, end} = profile();
|
||
|
+
|
||
|
+ function queryNodes() {
|
||
|
+ return fontoxpath.evaluateXPathToNodes(query, document, null, null, {
|
||
|
+ language: fontoxpath.evaluateXPath.XQUERY_3_1_LANGUAGE
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ let debugLog = getDebugger("hide-if-matches-xpath3");
|
||
|
+
|
||
|
+ const startHidingMutationObserver = scopeNode => {
|
||
|
+ const seenMap = new WeakSet();
|
||
|
+ const callback = () => {
|
||
|
+ mark();
|
||
|
+
|
||
|
+ const nodes = queryNodes();
|
||
|
+ for (const node of $(nodes)) {
|
||
|
+ if (seenMap.has(node))
|
||
|
+ return false;
|
||
|
+ seenMap.add(node);
|
||
|
+ win();
|
||
|
+ if ($(node).nodeType === ELEMENT_NODE)
|
||
|
+ hideElement(node);
|
||
|
+ else
|
||
|
+ $(node).textContent = "";
|
||
|
+ debugLog("Matched: ", node, " for selector: ", query);
|
||
|
+ }
|
||
|
+ end();
|
||
|
+ };
|
||
|
+
|
||
|
+ const mo = new MutationObserver(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-matches-xpath3",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(
|
||
|
+ scopeNode, {characterData: true, childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+
|
||
|
+ if (scopeQuery) {
|
||
|
+
|
||
|
+ let count = 0;
|
||
|
+ let scopeMutationObserver;
|
||
|
+ const scopeNodes = queryNodes();
|
||
|
+ const findMutationScopeNodes = () => {
|
||
|
+ for (const scopeNode of $(scopeNodes)) {
|
||
|
+
|
||
|
+ startHidingMutationObserver(scopeNode);
|
||
|
+ count++;
|
||
|
+ }
|
||
|
+ if (count > 0)
|
||
|
+ scopeMutationObserver.disconnect();
|
||
|
+ };
|
||
|
+
|
||
|
+ scopeMutationObserver = new MutationObserver(findMutationScopeNodes);
|
||
|
+ scopeMutationObserver.observe(
|
||
|
+ document, {characterData: true, childList: true, subtree: true}
|
||
|
+ );
|
||
|
+ findMutationScopeNodes();
|
||
|
+ }
|
||
|
+ else {
|
||
|
+
|
||
|
+ startHidingMutationObserver(document);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ snippets["hide-if-matches-xpath3"] = hideIfMatchesXPath3;
|
||
|
+ let context;
|
||
|
+ for (const [name, ...args] of filters) {
|
||
|
+ if (snippets.hasOwnProperty(name)) {
|
||
|
+ try { context = snippets[name].apply(context, args); }
|
||
|
+ catch (error) { console.error(error); }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ context = void 0;
|
||
|
+})(e, ...t);
|
||
|
+
|
||
|
+const callback = (environment, ...filters) => {
|
||
|
+ /*!
|
||
|
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
|
||
|
+ * Copyright (C) 2006-present eyeo GmbH
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is free software: you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License version 3 as
|
||
|
+ * published by the Free Software Foundation.
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License
|
||
|
+ * along with @eyeo/snippets. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+ */
|
||
|
+ const $$1 = Proxy;
|
||
|
+
|
||
|
+ const {apply: a, bind: b, call: c} = Function;
|
||
|
+ const apply$2 = c.bind(a);
|
||
|
+ const bind = c.bind(b);
|
||
|
+ const call = c.bind(c);
|
||
|
+
|
||
|
+ const callerHandler = {
|
||
|
+ get(target, name) {
|
||
|
+ return bind(c, target[name]);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const caller = target => new $$1(target, callerHandler);
|
||
|
+
|
||
|
+ const proxy = (source, target) => new $$1(source, {
|
||
|
+ apply: (_, self, args) => apply$2(target, self, args)
|
||
|
+ });
|
||
|
+
|
||
|
+ const handler$2 = {
|
||
|
+ get(target, name) {
|
||
|
+ return bind(target[name], target);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const bound = target => new $$1(target, handler$2);
|
||
|
+
|
||
|
+ const {
|
||
|
+ assign: assign$1,
|
||
|
+ defineProperties: defineProperties$1,
|
||
|
+ freeze: freeze$1,
|
||
|
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$3,
|
||
|
+ getOwnPropertyDescriptors: getOwnPropertyDescriptors$1,
|
||
|
+ getPrototypeOf
|
||
|
+ } = bound(Object);
|
||
|
+
|
||
|
+ const {hasOwnProperty} = caller({});
|
||
|
+
|
||
|
+ const {species} = Symbol;
|
||
|
+
|
||
|
+ const handler$1 = {
|
||
|
+ get(target, name) {
|
||
|
+ const Native = target[name];
|
||
|
+ class Secure extends Native {}
|
||
|
+
|
||
|
+ const proto = getOwnPropertyDescriptors$1(Native.prototype);
|
||
|
+ delete proto.constructor;
|
||
|
+ freeze$1(defineProperties$1(Secure.prototype, proto));
|
||
|
+
|
||
|
+ const statics = getOwnPropertyDescriptors$1(Native);
|
||
|
+ delete statics.length;
|
||
|
+ delete statics.prototype;
|
||
|
+ statics[species] = {value: Secure};
|
||
|
+ return freeze$1(defineProperties$1(Secure, statics));
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const secure = target => new $$1(target, handler$1);
|
||
|
+
|
||
|
+ const libEnvironment = typeof environment !== "undefined" ? environment :
|
||
|
+ {};
|
||
|
+
|
||
|
+ if (typeof globalThis === "undefined")
|
||
|
+ window.globalThis = window;
|
||
|
+
|
||
|
+ const {apply: apply$1, ownKeys} = bound(Reflect);
|
||
|
+
|
||
|
+ const worldEnvDefined = "world" in libEnvironment;
|
||
|
+ const isIsolatedWorld = worldEnvDefined && libEnvironment.world === "ISOLATED";
|
||
|
+ const isMainWorld = worldEnvDefined && libEnvironment.world === "MAIN";
|
||
|
+ const isChrome = typeof chrome === "object" && !!chrome.runtime;
|
||
|
+ const isOtherThanChrome = typeof browser === "object" && !!browser.runtime;
|
||
|
+ const isExtensionContext$2 = !isMainWorld &&
|
||
|
+ (isIsolatedWorld || isChrome || isOtherThanChrome);
|
||
|
+ const copyIfExtension = value => isExtensionContext$2 ?
|
||
|
+ value :
|
||
|
+ create(value, getOwnPropertyDescriptors(value));
|
||
|
+
|
||
|
+ const {
|
||
|
+ create,
|
||
|
+ defineProperties,
|
||
|
+ defineProperty,
|
||
|
+ freeze,
|
||
|
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$2,
|
||
|
+ getOwnPropertyDescriptors
|
||
|
+ } = bound(Object);
|
||
|
+
|
||
|
+ const invokes = bound(globalThis);
|
||
|
+ const classes = isExtensionContext$2 ? globalThis : secure(globalThis);
|
||
|
+ const {Map: Map$8, RegExp: RegExp$1, Set: Set$2, WeakMap: WeakMap$4, WeakSet: WeakSet$3} = classes;
|
||
|
+
|
||
|
+ const augment = (source, target, method = null) => {
|
||
|
+ const known = ownKeys(target);
|
||
|
+ for (const key of ownKeys(source)) {
|
||
|
+ if (known.includes(key))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ const descriptor = getOwnPropertyDescriptor$2(source, key);
|
||
|
+ if (method && "value" in descriptor) {
|
||
|
+ const {value} = descriptor;
|
||
|
+ if (typeof value === "function")
|
||
|
+ descriptor.value = method(value);
|
||
|
+ }
|
||
|
+ defineProperty(target, key, descriptor);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const primitive = name => {
|
||
|
+ const Super = classes[name];
|
||
|
+ class Class extends Super {}
|
||
|
+ const {toString, valueOf} = Super.prototype;
|
||
|
+ defineProperties(Class.prototype, {
|
||
|
+ toString: {value: toString},
|
||
|
+ valueOf: {value: valueOf}
|
||
|
+ });
|
||
|
+ const type = name.toLowerCase();
|
||
|
+ const method = callback => function() {
|
||
|
+ const result = apply$1(callback, this, arguments);
|
||
|
+ return typeof result === type ? new Class(result) : result;
|
||
|
+ };
|
||
|
+ augment(Super, Class, method);
|
||
|
+ augment(Super.prototype, Class.prototype, method);
|
||
|
+ return Class;
|
||
|
+ };
|
||
|
+
|
||
|
+ const variables$3 = freeze({
|
||
|
+ frozen: new WeakMap$4(),
|
||
|
+ hidden: new WeakSet$3(),
|
||
|
+ iframePropertiesToAbort: {
|
||
|
+ read: new Set$2(),
|
||
|
+ write: new Set$2()
|
||
|
+ },
|
||
|
+ abortedIframes: new WeakMap$4()
|
||
|
+ });
|
||
|
+
|
||
|
+ const startsCapitalized = new RegExp$1("^[A-Z]");
|
||
|
+
|
||
|
+ var env = new Proxy(new Map$8([
|
||
|
+
|
||
|
+ ["chrome", (
|
||
|
+ isExtensionContext$2 && (
|
||
|
+ (isChrome && chrome) ||
|
||
|
+ (isOtherThanChrome && browser)
|
||
|
+ )
|
||
|
+ ) || void 0],
|
||
|
+ ["isExtensionContext", isExtensionContext$2],
|
||
|
+ ["variables", variables$3],
|
||
|
+
|
||
|
+ ["console", copyIfExtension(console)],
|
||
|
+ ["document", globalThis.document],
|
||
|
+ ["performance", copyIfExtension(performance)],
|
||
|
+ ["JSON", copyIfExtension(JSON)],
|
||
|
+ ["Map", Map$8],
|
||
|
+ ["Math", copyIfExtension(Math)],
|
||
|
+ ["Number", isExtensionContext$2 ? Number : primitive("Number")],
|
||
|
+ ["RegExp", RegExp$1],
|
||
|
+ ["Set", Set$2],
|
||
|
+ ["String", isExtensionContext$2 ? String : primitive("String")],
|
||
|
+ ["WeakMap", WeakMap$4],
|
||
|
+ ["WeakSet", WeakSet$3],
|
||
|
+
|
||
|
+ ["MouseEvent", MouseEvent]
|
||
|
+ ]), {
|
||
|
+ get(map, key) {
|
||
|
+ if (map.has(key))
|
||
|
+ return map.get(key);
|
||
|
+
|
||
|
+ let value = globalThis[key];
|
||
|
+ if (typeof value === "function")
|
||
|
+ value = (startsCapitalized.test(key) ? classes : invokes)[key];
|
||
|
+
|
||
|
+ map.set(key, value);
|
||
|
+ return value;
|
||
|
+ },
|
||
|
+ has(map, key) {
|
||
|
+ return map.has(key);
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ class WeakValue {
|
||
|
+ has() { return false; }
|
||
|
+ set() {}
|
||
|
+ }
|
||
|
+
|
||
|
+ const helpers = {WeakSet, WeakMap, WeakValue};
|
||
|
+ const {apply} = Reflect;
|
||
|
+
|
||
|
+ function transformOnce (callback) { const {WeakSet, WeakMap, WeakValue} = (this || helpers);
|
||
|
+ const ws = new WeakSet;
|
||
|
+ const wm = new WeakMap;
|
||
|
+ const wv = new WeakValue;
|
||
|
+ return function (any) {
|
||
|
+ if (ws.has(any))
|
||
|
+ return any;
|
||
|
+
|
||
|
+ if (wm.has(any))
|
||
|
+ return wm.get(any);
|
||
|
+
|
||
|
+ if (wv.has(any))
|
||
|
+ return wv.get(any);
|
||
|
+
|
||
|
+ const value = apply(callback, this, arguments);
|
||
|
+ ws.add(value);
|
||
|
+ if (value !== any)
|
||
|
+ (typeof any === 'object' && any ? wm : wv).set(any, value);
|
||
|
+ return value;
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ const {Map: Map$7, WeakMap: WeakMap$3, WeakSet: WeakSet$2, setTimeout} = env;
|
||
|
+
|
||
|
+ let cleanup = true;
|
||
|
+ let cleanUpCallback = map => {
|
||
|
+ map.clear();
|
||
|
+ cleanup = !cleanup;
|
||
|
+ };
|
||
|
+
|
||
|
+ var transformer = transformOnce.bind({
|
||
|
+ WeakMap: WeakMap$3,
|
||
|
+ WeakSet: WeakSet$2,
|
||
|
+
|
||
|
+ WeakValue: class extends Map$7 {
|
||
|
+ set(key, value) {
|
||
|
+ if (cleanup) {
|
||
|
+ cleanup = !cleanup;
|
||
|
+ setTimeout(cleanUpCallback, 0, this);
|
||
|
+ }
|
||
|
+ return super.set(key, value);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ const {concat, includes, join, reduce, unshift} = caller([]);
|
||
|
+
|
||
|
+ const globals = secure(globalThis);
|
||
|
+
|
||
|
+ const {
|
||
|
+ Map: Map$6,
|
||
|
+ WeakMap: WeakMap$2
|
||
|
+ } = globals;
|
||
|
+
|
||
|
+ const map = new Map$6;
|
||
|
+ const descriptors = target => {
|
||
|
+ const chain = [];
|
||
|
+ let current = target;
|
||
|
+ while (current) {
|
||
|
+ if (map.has(current))
|
||
|
+ unshift(chain, map.get(current));
|
||
|
+ else {
|
||
|
+ const descriptors = getOwnPropertyDescriptors$1(current);
|
||
|
+ map.set(current, descriptors);
|
||
|
+ unshift(chain, descriptors);
|
||
|
+ }
|
||
|
+ current = getPrototypeOf(current);
|
||
|
+ }
|
||
|
+ unshift(chain, {});
|
||
|
+ return apply$2(assign$1, null, chain);
|
||
|
+ };
|
||
|
+
|
||
|
+ const chain = source => {
|
||
|
+ const target = typeof source === 'function' ? source.prototype : source;
|
||
|
+ const chained = descriptors(target);
|
||
|
+ const handler = {
|
||
|
+ get(target, key) {
|
||
|
+ if (key in chained) {
|
||
|
+ const {value, get} = chained[key];
|
||
|
+ if (get)
|
||
|
+ return call(get, target);
|
||
|
+ if (typeof value === 'function')
|
||
|
+ return bind(value, target);
|
||
|
+ }
|
||
|
+ return target[key];
|
||
|
+ },
|
||
|
+ set(target, key, value) {
|
||
|
+ if (key in chained) {
|
||
|
+ const {set} = chained[key];
|
||
|
+ if (set) {
|
||
|
+ call(set, target, value);
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ target[key] = value;
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ };
|
||
|
+ return target => new $$1(target, handler);
|
||
|
+ };
|
||
|
+
|
||
|
+ const {
|
||
|
+ isExtensionContext: isExtensionContext$1,
|
||
|
+ Array: Array$2,
|
||
|
+ Number: Number$1,
|
||
|
+ String: String$1,
|
||
|
+ Object: Object$9
|
||
|
+ } = env;
|
||
|
+
|
||
|
+ const {isArray} = Array$2;
|
||
|
+ const {getOwnPropertyDescriptor: getOwnPropertyDescriptor$1, setPrototypeOf: setPrototypeOf$1} = Object$9;
|
||
|
+
|
||
|
+ const {toString: toString$1} = Object$9.prototype;
|
||
|
+ const {slice} = String$1.prototype;
|
||
|
+ const getBrand = value => call(slice, call(toString$1, value), 8, -1);
|
||
|
+
|
||
|
+ const {get: nodeType} = getOwnPropertyDescriptor$1(Node.prototype, "nodeType");
|
||
|
+
|
||
|
+ const chained = isExtensionContext$1 ? {} : {
|
||
|
+ Attr: chain(Attr),
|
||
|
+ CanvasRenderingContext2D: chain(CanvasRenderingContext2D),
|
||
|
+ CSSStyleDeclaration: chain(CSSStyleDeclaration),
|
||
|
+ Document: chain(Document),
|
||
|
+ Element: chain(Element),
|
||
|
+ HTMLCanvasElement: chain(HTMLCanvasElement),
|
||
|
+ HTMLElement: chain(HTMLElement),
|
||
|
+ HTMLImageElement: chain(HTMLImageElement),
|
||
|
+ HTMLScriptElement: chain(HTMLScriptElement),
|
||
|
+ MutationRecord: chain(MutationRecord),
|
||
|
+ Node: chain(Node),
|
||
|
+ ShadowRoot: chain(ShadowRoot),
|
||
|
+
|
||
|
+ get CSS2Properties() {
|
||
|
+ return chained.CSSStyleDeclaration;
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const upgrade = (value, hint) => {
|
||
|
+ if (hint !== "Element" && hint in chained)
|
||
|
+ return chained[hint](value);
|
||
|
+
|
||
|
+ if (isArray(value))
|
||
|
+ return setPrototypeOf$1(value, Array$2.prototype);
|
||
|
+
|
||
|
+ const brand = getBrand(value);
|
||
|
+ if (brand in chained)
|
||
|
+ return chained[brand](value);
|
||
|
+
|
||
|
+ if (brand in env)
|
||
|
+ return setPrototypeOf$1(value, env[brand].prototype);
|
||
|
+
|
||
|
+ if ("nodeType" in value) {
|
||
|
+ switch (call(nodeType, value)) {
|
||
|
+ case 1:
|
||
|
+ if (!(hint in chained))
|
||
|
+ throw new Error("unknown hint " + hint);
|
||
|
+ return chained[hint](value);
|
||
|
+ case 2:
|
||
|
+ return chained.Attr(value);
|
||
|
+ case 3:
|
||
|
+ return chained.Node(value);
|
||
|
+ case 9:
|
||
|
+ return chained.Document(value);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ throw new Error("unknown brand " + brand);
|
||
|
+ };
|
||
|
+
|
||
|
+ var $ = isExtensionContext$1 ?
|
||
|
+ value => (value === window || value === globalThis ? env : value) :
|
||
|
+ transformer((value, hint = "Element") => {
|
||
|
+ if (value === window || value === globalThis)
|
||
|
+ return env;
|
||
|
+
|
||
|
+ switch (typeof value) {
|
||
|
+ case "object":
|
||
|
+ return value && upgrade(value, hint);
|
||
|
+
|
||
|
+ case "string":
|
||
|
+ return new String$1(value);
|
||
|
+
|
||
|
+ case "number":
|
||
|
+ return new Number$1(value);
|
||
|
+
|
||
|
+ default:
|
||
|
+ throw new Error("unsupported value");
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ const handler = {
|
||
|
+ get(target, name) {
|
||
|
+ const context = target;
|
||
|
+ while (!hasOwnProperty(target, name))
|
||
|
+ target = getPrototypeOf(target);
|
||
|
+ const {get, set} = getOwnPropertyDescriptor$3(target, name);
|
||
|
+ return function () {
|
||
|
+ return arguments.length ?
|
||
|
+ apply$2(set, context, arguments) :
|
||
|
+ call(get, context);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const accessor = target => new $$1(target, handler);
|
||
|
+
|
||
|
+ let debugging = false;
|
||
|
+
|
||
|
+ function debug() {
|
||
|
+ return debugging;
|
||
|
+ }
|
||
|
+
|
||
|
+ function setDebug() {
|
||
|
+ debugging = true;
|
||
|
+ }
|
||
|
+
|
||
|
+ const {console: console$3} = $(window);
|
||
|
+
|
||
|
+ const noop = () => {};
|
||
|
+
|
||
|
+ function log(...args) {
|
||
|
+ if (debug())
|
||
|
+ $(args).unshift("%c DEBUG", "font-weight: bold");
|
||
|
+
|
||
|
+ console$3.log(...args);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getDebugger(name) {
|
||
|
+ return bind(debug() ? log : noop, null, name);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Math: Math$1, RegExp} = $(window);
|
||
|
+
|
||
|
+ function regexEscape(string) {
|
||
|
+ return $(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||
|
+ }
|
||
|
+
|
||
|
+ function toRegExp(pattern) {
|
||
|
+ let {length} = pattern;
|
||
|
+
|
||
|
+ if (length > 1 && pattern[0] === "/") {
|
||
|
+ let isCaseSensitive = pattern[length - 1] === "/";
|
||
|
+
|
||
|
+ if (isCaseSensitive || (length > 2 && $(pattern).endsWith("/i"))) {
|
||
|
+ let args = [$(pattern).slice(1, isCaseSensitive ? -1 : -2)];
|
||
|
+ if (!isCaseSensitive)
|
||
|
+ args.push("i");
|
||
|
+
|
||
|
+ return new RegExp(...args);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return new RegExp(regexEscape(pattern));
|
||
|
+ }
|
||
|
+
|
||
|
+ function randomId() {
|
||
|
+
|
||
|
+ return $(Math$1.floor(Math$1.random() * 2116316160 + 60466176)).toString(36);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ parseFloat,
|
||
|
+ variables: variables$2,
|
||
|
+ Array: Array$1,
|
||
|
+ Error: Error$7,
|
||
|
+ Map: Map$5,
|
||
|
+ Object: Object$8,
|
||
|
+ ReferenceError: ReferenceError$2,
|
||
|
+ Set: Set$1,
|
||
|
+ WeakMap: WeakMap$1
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ let {onerror} = accessor(window);
|
||
|
+
|
||
|
+ let NodeProto$1 = Node.prototype;
|
||
|
+ let ElementProto$2 = Element.prototype;
|
||
|
+
|
||
|
+ let propertyAccessors = null;
|
||
|
+
|
||
|
+ function wrapPropertyAccess(object, property, descriptor,
|
||
|
+ setConfigurable = true) {
|
||
|
+ let $property = $(property);
|
||
|
+ let dotIndex = $property.indexOf(".");
|
||
|
+ if (dotIndex == -1) {
|
||
|
+
|
||
|
+ let currentDescriptor = Object$8.getOwnPropertyDescriptor(object, property);
|
||
|
+ if (currentDescriptor && !currentDescriptor.configurable)
|
||
|
+ return;
|
||
|
+
|
||
|
+ let newDescriptor = Object$8.assign({}, descriptor, {
|
||
|
+ configurable: setConfigurable
|
||
|
+ });
|
||
|
+
|
||
|
+ if (!currentDescriptor && !newDescriptor.get && newDescriptor.set) {
|
||
|
+ let propertyValue = object[property];
|
||
|
+ newDescriptor.get = () => propertyValue;
|
||
|
+ }
|
||
|
+
|
||
|
+ Object$8.defineProperty(object, property, newDescriptor);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let name = $property.slice(0, dotIndex).toString();
|
||
|
+ property = $property.slice(dotIndex + 1).toString();
|
||
|
+ let value = object[name];
|
||
|
+ if (value && (typeof value == "object" || typeof value == "function"))
|
||
|
+ wrapPropertyAccess(value, property, descriptor);
|
||
|
+
|
||
|
+ let currentDescriptor = Object$8.getOwnPropertyDescriptor(object, name);
|
||
|
+ if (currentDescriptor && !currentDescriptor.configurable)
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (!propertyAccessors)
|
||
|
+ propertyAccessors = new WeakMap$1();
|
||
|
+
|
||
|
+ if (!propertyAccessors.has(object))
|
||
|
+ propertyAccessors.set(object, new Map$5());
|
||
|
+
|
||
|
+ let properties = propertyAccessors.get(object);
|
||
|
+ if (properties.has(name)) {
|
||
|
+ properties.get(name).set(property, descriptor);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let toBeWrapped = new Map$5([[property, descriptor]]);
|
||
|
+ properties.set(name, toBeWrapped);
|
||
|
+ Object$8.defineProperty(object, name, {
|
||
|
+ get: () => value,
|
||
|
+ set(newValue) {
|
||
|
+ value = newValue;
|
||
|
+ if (value && (typeof value == "object" || typeof value == "function")) {
|
||
|
+
|
||
|
+ for (let [prop, desc] of toBeWrapped)
|
||
|
+ wrapPropertyAccess(value, prop, desc);
|
||
|
+ }
|
||
|
+ },
|
||
|
+ configurable: setConfigurable
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ function overrideOnError(magic) {
|
||
|
+ let prev = onerror();
|
||
|
+ onerror((...args) => {
|
||
|
+ let message = args.length && args[0];
|
||
|
+ if (typeof message == "string" && $(message).includes(magic))
|
||
|
+ return true;
|
||
|
+ if (typeof prev == "function")
|
||
|
+ return apply$2(prev, this, args);
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnRead(loggingPrefix, context, property,
|
||
|
+ setConfigurable = true) {
|
||
|
+ let debugLog = getDebugger(loggingPrefix);
|
||
|
+
|
||
|
+ if (!property) {
|
||
|
+ debugLog("no property to abort on read");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let rid = randomId();
|
||
|
+
|
||
|
+ function abort() {
|
||
|
+ debugLog(`${property} access aborted`);
|
||
|
+ throw new ReferenceError$2(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ debugLog(`aborting on ${property} access`);
|
||
|
+
|
||
|
+ wrapPropertyAccess(context,
|
||
|
+ property,
|
||
|
+ {get: abort, set() {}},
|
||
|
+ setConfigurable);
|
||
|
+ overrideOnError(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnWrite(loggingPrefix, context, property,
|
||
|
+ setConfigurable = true) {
|
||
|
+ let debugLog = getDebugger(loggingPrefix);
|
||
|
+
|
||
|
+ if (!property) {
|
||
|
+ debugLog("no property to abort on write");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let rid = randomId();
|
||
|
+
|
||
|
+ function abort() {
|
||
|
+ debugLog(`setting ${property} aborted`);
|
||
|
+ throw new ReferenceError$2(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ debugLog(`aborting when setting ${property}`);
|
||
|
+
|
||
|
+ wrapPropertyAccess(context, property, {set: abort}, setConfigurable);
|
||
|
+ overrideOnError(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnIframe(
|
||
|
+ properties,
|
||
|
+ abortRead = false,
|
||
|
+ abortWrite = false
|
||
|
+ ) {
|
||
|
+ let abortedIframes = variables$2.abortedIframes;
|
||
|
+ let iframePropertiesToAbort = variables$2.iframePropertiesToAbort;
|
||
|
+
|
||
|
+ for (let frame of Array$1.from(window.frames)) {
|
||
|
+ if (abortedIframes.has(frame)) {
|
||
|
+ for (let property of properties) {
|
||
|
+ if (abortRead)
|
||
|
+ abortedIframes.get(frame).read.add(property);
|
||
|
+ if (abortWrite)
|
||
|
+ abortedIframes.get(frame).write.add(property);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ for (let property of properties) {
|
||
|
+ if (abortRead)
|
||
|
+ iframePropertiesToAbort.read.add(property);
|
||
|
+ if (abortWrite)
|
||
|
+ iframePropertiesToAbort.write.add(property);
|
||
|
+ }
|
||
|
+
|
||
|
+ queryAndProxyIframe();
|
||
|
+ if (!abortedIframes.has(document)) {
|
||
|
+ abortedIframes.set(document, true);
|
||
|
+ addHooksOnDomAdditions(queryAndProxyIframe);
|
||
|
+ }
|
||
|
+
|
||
|
+ function queryAndProxyIframe() {
|
||
|
+ for (let frame of Array$1.from(window.frames)) {
|
||
|
+
|
||
|
+ if (!abortedIframes.has(frame)) {
|
||
|
+ abortedIframes.set(frame, {
|
||
|
+ read: new Set$1(iframePropertiesToAbort.read),
|
||
|
+ write: new Set$1(iframePropertiesToAbort.write)
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ let readProps = abortedIframes.get(frame).read;
|
||
|
+ if (readProps.size > 0) {
|
||
|
+ let props = Array$1.from(readProps);
|
||
|
+ readProps.clear();
|
||
|
+ for (let property of props)
|
||
|
+ abortOnRead("abort-on-iframe-property-read", frame, property);
|
||
|
+ }
|
||
|
+
|
||
|
+ let writeProps = abortedIframes.get(frame).write;
|
||
|
+ if (writeProps.size > 0) {
|
||
|
+ let props = Array$1.from(writeProps);
|
||
|
+ writeProps.clear();
|
||
|
+ for (let property of props)
|
||
|
+ abortOnWrite("abort-on-iframe-property-write", frame, property);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function addHooksOnDomAdditions(endCallback) {
|
||
|
+ let descriptor;
|
||
|
+
|
||
|
+ wrapAccess(NodeProto$1, ["appendChild", "insertBefore", "replaceChild"]);
|
||
|
+ wrapAccess(ElementProto$2, ["append", "prepend", "replaceWith", "after",
|
||
|
+ "before", "insertAdjacentElement",
|
||
|
+ "insertAdjacentHTML"]);
|
||
|
+
|
||
|
+ descriptor = getInnerHTMLDescriptor(ElementProto$2, "innerHTML");
|
||
|
+ wrapPropertyAccess(ElementProto$2, "innerHTML", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInnerHTMLDescriptor(ElementProto$2, "outerHTML");
|
||
|
+ wrapPropertyAccess(ElementProto$2, "outerHTML", descriptor);
|
||
|
+
|
||
|
+ function wrapAccess(prototype, names) {
|
||
|
+ for (let name of names) {
|
||
|
+ let desc = getAppendChildDescriptor(prototype, name);
|
||
|
+ wrapPropertyAccess(prototype, name, desc);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function getAppendChildDescriptor(target, property) {
|
||
|
+ let currentValue = target[property];
|
||
|
+ return {
|
||
|
+ get() {
|
||
|
+ return function(...args) {
|
||
|
+ let result;
|
||
|
+ result = apply$2(currentValue, this, args);
|
||
|
+ endCallback && endCallback();
|
||
|
+ return result;
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getInnerHTMLDescriptor(target, property) {
|
||
|
+ let desc = Object$8.getOwnPropertyDescriptor(target, property);
|
||
|
+ let {set: prevSetter} = desc || {};
|
||
|
+ return {
|
||
|
+ set(val) {
|
||
|
+ let result;
|
||
|
+ result = call(prevSetter, this, val);
|
||
|
+ endCallback && endCallback();
|
||
|
+ return result;
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Object: NativeObject} = window;
|
||
|
+ function findOwner(root, path) {
|
||
|
+ if (!(root instanceof NativeObject))
|
||
|
+ return;
|
||
|
+
|
||
|
+ let object = root;
|
||
|
+ let chain = $(path).split(".");
|
||
|
+
|
||
|
+ if (chain.length === 0)
|
||
|
+ return;
|
||
|
+
|
||
|
+ for (let i = 0; i < chain.length - 1; i++) {
|
||
|
+ let prop = chain[i];
|
||
|
+
|
||
|
+ if (!hasOwnProperty(object, prop))
|
||
|
+ return;
|
||
|
+
|
||
|
+ object = object[prop];
|
||
|
+
|
||
|
+ if (!(object instanceof NativeObject))
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let prop = chain[chain.length - 1];
|
||
|
+
|
||
|
+ if (hasOwnProperty(object, prop))
|
||
|
+ return [object, prop];
|
||
|
+ }
|
||
|
+
|
||
|
+ const decimals = $(/^\d+$/);
|
||
|
+
|
||
|
+ function overrideValue(value) {
|
||
|
+ switch (value) {
|
||
|
+ case "false":
|
||
|
+ return false;
|
||
|
+ case "true":
|
||
|
+ return true;
|
||
|
+ case "null":
|
||
|
+ return null;
|
||
|
+ case "noopFunc":
|
||
|
+ return () => {};
|
||
|
+ case "trueFunc":
|
||
|
+ return () => true;
|
||
|
+ case "falseFunc":
|
||
|
+ return () => false;
|
||
|
+ case "emptyArray":
|
||
|
+ return [];
|
||
|
+ case "emptyObj":
|
||
|
+ return {};
|
||
|
+ case "undefined":
|
||
|
+ return void 0;
|
||
|
+ case "":
|
||
|
+ return value;
|
||
|
+ default:
|
||
|
+ if (decimals.test(value))
|
||
|
+ return parseFloat(value);
|
||
|
+
|
||
|
+ throw new Error$7("[override-property-read snippet]: " +
|
||
|
+ `Value "${value}" is not valid.`);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {HTMLScriptElement: HTMLScriptElement$1, Object: Object$7, ReferenceError: ReferenceError$1} = $(window);
|
||
|
+ let Script = Object$7.getPrototypeOf(HTMLScriptElement$1);
|
||
|
+
|
||
|
+ function abortCurrentInlineScript(api, search = null) {
|
||
|
+ const debugLog = getDebugger("abort-current-inline-script");
|
||
|
+ const re = search ? toRegExp(search) : null;
|
||
|
+
|
||
|
+ const rid = randomId();
|
||
|
+ const us = $(document).currentScript;
|
||
|
+
|
||
|
+ let object = window;
|
||
|
+ const path = $(api).split(".");
|
||
|
+ const name = $(path).pop();
|
||
|
+
|
||
|
+ for (let node of $(path)) {
|
||
|
+ object = object[node];
|
||
|
+ if (
|
||
|
+ !object || !(typeof object == "object" || typeof object == "function")) {
|
||
|
+ debugLog(path, " is not found");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ const {get: prevGetter, set: prevSetter} =
|
||
|
+ Object$7.getOwnPropertyDescriptor(object, name) || {};
|
||
|
+
|
||
|
+ let currentValue = object[name];
|
||
|
+ if (typeof currentValue === "undefined")
|
||
|
+ debugLog("The property", name, "doesn't exist yet. Check typos.");
|
||
|
+
|
||
|
+ const abort = () => {
|
||
|
+ const element = $(document).currentScript;
|
||
|
+ if (element instanceof Script &&
|
||
|
+ $(element, "HTMLScriptElement").src == "" &&
|
||
|
+ element != us &&
|
||
|
+ (!re || re.test($(element).textContent))) {
|
||
|
+ debugLog(path, " is aborted \n", element);
|
||
|
+ throw new ReferenceError$1(rid);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const descriptor = {
|
||
|
+ get() {
|
||
|
+ abort();
|
||
|
+
|
||
|
+ if (prevGetter)
|
||
|
+ return call(prevGetter, this);
|
||
|
+
|
||
|
+ return currentValue;
|
||
|
+ },
|
||
|
+ set(value) {
|
||
|
+ abort();
|
||
|
+
|
||
|
+ if (prevSetter)
|
||
|
+ call(prevSetter, this, value);
|
||
|
+ else
|
||
|
+ currentValue = value;
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ wrapPropertyAccess(object, name, descriptor);
|
||
|
+
|
||
|
+ overrideOnError(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnIframePropertyRead(...properties) {
|
||
|
+ abortOnIframe(properties, true, false);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnIframePropertyWrite(...properties) {
|
||
|
+ abortOnIframe(properties, false, true);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnPropertyRead(property, setConfigurable) {
|
||
|
+ const configurableFlag = !(setConfigurable === "false");
|
||
|
+ abortOnRead("abort-on-property-read", window, property, configurableFlag);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnPropertyWrite(property, setConfigurable) {
|
||
|
+ const configurableFlag = !(setConfigurable === "false");
|
||
|
+ abortOnWrite("abort-on-property-write", window, property, configurableFlag);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Error: Error$6} = $(window);
|
||
|
+ let {cookie: documentCookies} = accessor(document);
|
||
|
+
|
||
|
+ function cookieRemover(cookie) {
|
||
|
+ if (!cookie)
|
||
|
+ throw new Error$6("[cookie-remover snippet]: No cookie to remove.");
|
||
|
+
|
||
|
+ let debugLog = getDebugger("cookie-remover");
|
||
|
+ let re = toRegExp(cookie);
|
||
|
+
|
||
|
+ if (!$(/^http|^about/).test(location.protocol)) {
|
||
|
+ debugLog("Snippet only works for http or https and about.");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ debugLog("Parsing cookies for matches");
|
||
|
+
|
||
|
+ for (const pair of $(getCookieMatches())) {
|
||
|
+ let $hostname = $(location.hostname);
|
||
|
+ let name = $(pair).split("=")[0];
|
||
|
+ let expires = "expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
||
|
+ let path = "path=/";
|
||
|
+ let domain = "domain=" + $hostname.slice($hostname.indexOf(".") + 1);
|
||
|
+
|
||
|
+ documentCookies(`${$(name).trim()}=;${expires};${path};${domain}`);
|
||
|
+
|
||
|
+ debugLog(`Set expiration date on ${name}`);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getCookieMatches() {
|
||
|
+ const arr = $(documentCookies()).split(";");
|
||
|
+ return arr.filter(str => re.test($(str).split("=")[0]));
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ document: document$1,
|
||
|
+ getComputedStyle,
|
||
|
+ isExtensionContext,
|
||
|
+ variables: variables$1,
|
||
|
+ Array,
|
||
|
+ MutationObserver: MutationObserver$2,
|
||
|
+ Object: Object$6,
|
||
|
+ XPathEvaluator,
|
||
|
+ XPathExpression,
|
||
|
+ XPathResult
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ let {querySelectorAll} = document$1;
|
||
|
+ let $$ = querySelectorAll && bind(querySelectorAll, document$1);
|
||
|
+
|
||
|
+ const {assign, setPrototypeOf} = Object$6;
|
||
|
+
|
||
|
+ class $XPathExpression extends XPathExpression {
|
||
|
+ evaluate(...args) {
|
||
|
+ return setPrototypeOf(
|
||
|
+ apply$2(super.evaluate, this, args),
|
||
|
+ XPathResult.prototype
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ class $XPathEvaluator extends XPathEvaluator {
|
||
|
+ createExpression(...args) {
|
||
|
+ return setPrototypeOf(
|
||
|
+ apply$2(super.createExpression, this, args),
|
||
|
+ $XPathExpression.prototype
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function hideElement(element) {
|
||
|
+ if (variables$1.hidden.has(element))
|
||
|
+ return;
|
||
|
+
|
||
|
+ notifyElementHidden(element);
|
||
|
+
|
||
|
+ variables$1.hidden.add(element);
|
||
|
+
|
||
|
+ let {style} = $(element);
|
||
|
+ let $style = $(style, "CSSStyleDeclaration");
|
||
|
+ let properties = $([]);
|
||
|
+ let {debugCSSProperties} = libEnvironment;
|
||
|
+
|
||
|
+ for (let [key, value] of (debugCSSProperties || [["display", "none"]])) {
|
||
|
+ $style.setProperty(key, value, "important");
|
||
|
+ properties.push([key, $style.getPropertyValue(key)]);
|
||
|
+ }
|
||
|
+
|
||
|
+ new MutationObserver$2(() => {
|
||
|
+ for (let [key, value] of properties) {
|
||
|
+ let propertyValue = $style.getPropertyValue(key);
|
||
|
+ let propertyPriority = $style.getPropertyPriority(key);
|
||
|
+ if (propertyValue != value || propertyPriority != "important")
|
||
|
+ $style.setProperty(key, value, "important");
|
||
|
+ }
|
||
|
+ }).observe(element, {attributes: true,
|
||
|
+ attributeFilter: ["style"]});
|
||
|
+ }
|
||
|
+
|
||
|
+ function notifyElementHidden(element) {
|
||
|
+ if (isExtensionContext && typeof checkElement === "function")
|
||
|
+ checkElement(element);
|
||
|
+ }
|
||
|
+
|
||
|
+ function initQueryAndApply(selector) {
|
||
|
+ let $selector = selector;
|
||
|
+ if ($selector.startsWith("xpath(") &&
|
||
|
+ $selector.endsWith(")")) {
|
||
|
+ let xpathQuery = $selector.slice(6, -1);
|
||
|
+ let evaluator = new $XPathEvaluator();
|
||
|
+ let expression = evaluator.createExpression(xpathQuery, null);
|
||
|
+
|
||
|
+ let flag = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
|
||
|
+
|
||
|
+ return cb => {
|
||
|
+ if (!cb)
|
||
|
+ return;
|
||
|
+ let result = expression.evaluate(document$1, flag, null);
|
||
|
+ let {snapshotLength} = result;
|
||
|
+ for (let i = 0; i < snapshotLength; i++)
|
||
|
+ cb(result.snapshotItem(i));
|
||
|
+ };
|
||
|
+ }
|
||
|
+ return cb => $$(selector).forEach(cb);
|
||
|
+ }
|
||
|
+
|
||
|
+ function initQueryAll(selector) {
|
||
|
+ let $selector = selector;
|
||
|
+ if ($selector.startsWith("xpath(") &&
|
||
|
+ $selector.endsWith(")")) {
|
||
|
+ let queryAndApply = initQueryAndApply(selector);
|
||
|
+ return () => {
|
||
|
+ let elements = $([]);
|
||
|
+ queryAndApply(e => elements.push(e));
|
||
|
+ return elements;
|
||
|
+ };
|
||
|
+ }
|
||
|
+ return () => Array.from($$(selector));
|
||
|
+ }
|
||
|
+
|
||
|
+ let {ELEMENT_NODE, TEXT_NODE, prototype: NodeProto} = Node;
|
||
|
+ let {prototype: ElementProto$1} = Element;
|
||
|
+ let {prototype: HTMLElementProto} = HTMLElement;
|
||
|
+
|
||
|
+ let {
|
||
|
+ console: console$2,
|
||
|
+ variables,
|
||
|
+ DOMParser,
|
||
|
+ Error: Error$5,
|
||
|
+ MutationObserver: MutationObserver$1,
|
||
|
+ Object: Object$5,
|
||
|
+ ReferenceError
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ let {getOwnPropertyDescriptor} = Object$5;
|
||
|
+
|
||
|
+ function freezeElement(selector, options = "", ...exceptions) {
|
||
|
+ let observer;
|
||
|
+ let subtree = false;
|
||
|
+ let shouldAbort = false;
|
||
|
+ let exceptionSelectors = $(exceptions).filter(e => !isRegex(e));
|
||
|
+ let regexExceptions = $(exceptions).filter(e => isRegex(e)).map(toRegExp);
|
||
|
+ let rid = randomId();
|
||
|
+ let targetNodes;
|
||
|
+ let queryAll = initQueryAll(selector);
|
||
|
+
|
||
|
+ checkOptions();
|
||
|
+ let data = {
|
||
|
+ selector,
|
||
|
+ shouldAbort,
|
||
|
+ rid,
|
||
|
+ exceptionSelectors,
|
||
|
+ regexExceptions,
|
||
|
+ changeId: 0
|
||
|
+ };
|
||
|
+ if (!variables.frozen.has(document)) {
|
||
|
+ variables.frozen.set(document, true);
|
||
|
+ proxyNativeProperties();
|
||
|
+ }
|
||
|
+ observer = new MutationObserver$1(searchAndAttach);
|
||
|
+ observer.observe(document, {childList: true, subtree: true});
|
||
|
+ searchAndAttach();
|
||
|
+
|
||
|
+ function isRegex(s) {
|
||
|
+ return s.length >= 2 && s[0] == "/" && s[s.length - 1] == "/";
|
||
|
+ }
|
||
|
+
|
||
|
+ function checkOptions() {
|
||
|
+ let optionsChunks = $(options).split("+");
|
||
|
+ if (optionsChunks.length === 1 && optionsChunks[0] === "")
|
||
|
+ optionsChunks = [];
|
||
|
+ for (let chunk of optionsChunks) {
|
||
|
+ switch (chunk) {
|
||
|
+ case "subtree":
|
||
|
+ subtree = true;
|
||
|
+ break;
|
||
|
+ case "abort":
|
||
|
+ shouldAbort = true;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ throw new Error$5("[freeze] Unknown option passed to the snippet." +
|
||
|
+ " [selector]: " + selector +
|
||
|
+ " [option]: " + chunk);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function proxyNativeProperties() {
|
||
|
+ let descriptor;
|
||
|
+
|
||
|
+ descriptor = getAppendChildDescriptor(
|
||
|
+ NodeProto, "appendChild", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "appendChild", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendChildDescriptor(
|
||
|
+ NodeProto, "insertBefore", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "insertBefore", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendChildDescriptor(
|
||
|
+ NodeProto, "replaceChild", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "replaceChild", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1, "append", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "append", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1, "prepend", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "prepend", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "replaceWith",
|
||
|
+ isFrozenOrHasFrozenParent,
|
||
|
+ getSnippetDataFromNodeOrParent
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "replaceWith", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "after",
|
||
|
+ isFrozenOrHasFrozenParent,
|
||
|
+ getSnippetDataFromNodeOrParent
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "after", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "before",
|
||
|
+ isFrozenOrHasFrozenParent,
|
||
|
+ getSnippetDataFromNodeOrParent
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "before", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInsertAdjacentDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "insertAdjacentElement",
|
||
|
+ isFrozenAndInsideTarget,
|
||
|
+ getSnippetDataBasedOnTarget
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentElement", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInsertAdjacentDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "insertAdjacentHTML",
|
||
|
+ isFrozenAndInsideTarget,
|
||
|
+ getSnippetDataBasedOnTarget
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentHTML", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInsertAdjacentDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "insertAdjacentText",
|
||
|
+ isFrozenAndInsideTarget,
|
||
|
+ getSnippetDataBasedOnTarget
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentText", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInnerHTMLDescriptor(
|
||
|
+ ElementProto$1, "innerHTML", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "innerHTML", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInnerHTMLDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "outerHTML",
|
||
|
+ isFrozenOrHasFrozenParent,
|
||
|
+ getSnippetDataFromNodeOrParent
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "outerHTML", descriptor);
|
||
|
+
|
||
|
+ descriptor = getTextContentDescriptor(
|
||
|
+ NodeProto, "textContent", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "textContent", descriptor);
|
||
|
+
|
||
|
+ descriptor = getTextContentDescriptor(
|
||
|
+ HTMLElementProto, "innerText", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(HTMLElementProto, "innerText", descriptor);
|
||
|
+
|
||
|
+ descriptor = getTextContentDescriptor(
|
||
|
+ NodeProto, "nodeValue", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "nodeValue", descriptor);
|
||
|
+
|
||
|
+ function isFrozen(node) {
|
||
|
+ return node && variables.frozen.has(node);
|
||
|
+ }
|
||
|
+
|
||
|
+ function isFrozenOrHasFrozenParent(node) {
|
||
|
+ try {
|
||
|
+ return node &&
|
||
|
+ (variables.frozen.has(node) ||
|
||
|
+ variables.frozen.has($(node).parentNode));
|
||
|
+ }
|
||
|
+ catch (error) {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function isFrozenAndInsideTarget(node, isInsideTarget) {
|
||
|
+ try {
|
||
|
+ return node &&
|
||
|
+ (variables.frozen.has(node) && isInsideTarget ||
|
||
|
+ variables.frozen.has($(node).parentNode) &&
|
||
|
+ !isInsideTarget);
|
||
|
+ }
|
||
|
+ catch (error) {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function getSnippetData(node) {
|
||
|
+ return variables.frozen.get(node);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getSnippetDataFromNodeOrParent(node) {
|
||
|
+ try {
|
||
|
+ if (variables.frozen.has(node))
|
||
|
+ return variables.frozen.get(node);
|
||
|
+ let parent = $(node).parentNode;
|
||
|
+ return variables.frozen.get(parent);
|
||
|
+ }
|
||
|
+ catch (error) {}
|
||
|
+ }
|
||
|
+
|
||
|
+ function getSnippetDataBasedOnTarget(node, isInsideTarget) {
|
||
|
+ try {
|
||
|
+ if (variables.frozen.has(node) && isInsideTarget)
|
||
|
+ return variables.frozen.get(node);
|
||
|
+ let parent = $(node).parentNode;
|
||
|
+ return variables.frozen.get(parent);
|
||
|
+ }
|
||
|
+ catch (error) {}
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function searchAndAttach() {
|
||
|
+ targetNodes = queryAll();
|
||
|
+ markNodes(targetNodes, false);
|
||
|
+ }
|
||
|
+
|
||
|
+ function markNodes(nodes, isChild = true) {
|
||
|
+ for (let node of nodes) {
|
||
|
+ if (!variables.frozen.has(node)) {
|
||
|
+ variables.frozen.set(node, data);
|
||
|
+ if (!isChild && subtree) {
|
||
|
+ new MutationObserver$1(mutationsList => {
|
||
|
+ for (let mutation of $(mutationsList))
|
||
|
+ markNodes($(mutation, "MutationRecord").addedNodes);
|
||
|
+ }).observe(node, {childList: true, subtree: true});
|
||
|
+ }
|
||
|
+ if (subtree && $(node).nodeType === ELEMENT_NODE)
|
||
|
+ markNodes($(node).childNodes);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function logPrefixed(id, ...args) {
|
||
|
+ log(`[freeze][${id}] `, ...args);
|
||
|
+ }
|
||
|
+
|
||
|
+ function logChange(nodeOrDOMString, target, property, snippetData) {
|
||
|
+ let targetSelector = snippetData.selector;
|
||
|
+ let chgId = snippetData.changeId;
|
||
|
+ let isDOMString = typeof nodeOrDOMString == "string";
|
||
|
+ let action = snippetData.shouldAbort ? "aborting" : "watching";
|
||
|
+ console$2.groupCollapsed(`[freeze][${chgId}] ${action}: ${targetSelector}`);
|
||
|
+ switch (property) {
|
||
|
+ case "appendChild":
|
||
|
+ case "append":
|
||
|
+ case "prepend":
|
||
|
+ case "insertBefore":
|
||
|
+ case "replaceChild":
|
||
|
+ case "insertAdjacentElement":
|
||
|
+ case "insertAdjacentHTML":
|
||
|
+ case "insertAdjacentText":
|
||
|
+ case "innerHTML":
|
||
|
+ case "outerHTML":
|
||
|
+ logPrefixed(chgId,
|
||
|
+ isDOMString ? "text: " : "node: ",
|
||
|
+ nodeOrDOMString);
|
||
|
+ logPrefixed(chgId, "added to node: ", target);
|
||
|
+ break;
|
||
|
+ case "replaceWith":
|
||
|
+ case "after":
|
||
|
+ case "before":
|
||
|
+ logPrefixed(chgId,
|
||
|
+ isDOMString ? "text: " : "node: ",
|
||
|
+ nodeOrDOMString);
|
||
|
+ logPrefixed(chgId, "added to node: ", $(target).parentNode);
|
||
|
+ break;
|
||
|
+ case "textContent":
|
||
|
+ case "innerText":
|
||
|
+ case "nodeValue":
|
||
|
+ logPrefixed(chgId, "content of node: ", target);
|
||
|
+ logPrefixed(chgId, "changed to: ", nodeOrDOMString);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ logPrefixed(chgId, `using the function "${property}"`);
|
||
|
+ console$2.groupEnd();
|
||
|
+ snippetData.changeId++;
|
||
|
+ }
|
||
|
+
|
||
|
+ function isExceptionNode(element, expSelectors) {
|
||
|
+ if (expSelectors) {
|
||
|
+ let $element = $(element);
|
||
|
+ for (let exception of expSelectors) {
|
||
|
+ if ($element.matches(exception))
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ function isExceptionText(string, regExceptions) {
|
||
|
+ if (regExceptions) {
|
||
|
+ for (let exception of regExceptions) {
|
||
|
+ if (exception.test(string))
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ function abort(id) {
|
||
|
+ throw new ReferenceError(id);
|
||
|
+ }
|
||
|
+
|
||
|
+ function checkHTML(htmlText, parent, property, snippetData) {
|
||
|
+ let domparser = new DOMParser();
|
||
|
+ let {body} = $(domparser.parseFromString(htmlText, "text/html"));
|
||
|
+ let nodes = $(body).childNodes;
|
||
|
+ let accepted = checkMultiple(nodes, parent, property, snippetData);
|
||
|
+ let content = $(accepted).map(node => {
|
||
|
+ switch ($(node).nodeType) {
|
||
|
+ case ELEMENT_NODE:
|
||
|
+ return $(node).outerHTML;
|
||
|
+ case TEXT_NODE:
|
||
|
+ return $(node).textContent;
|
||
|
+ default:
|
||
|
+ return "";
|
||
|
+ }
|
||
|
+ });
|
||
|
+ return content.join("");
|
||
|
+ }
|
||
|
+
|
||
|
+ function checkMultiple(nodesOrDOMStrings, parent, property, snippetData) {
|
||
|
+ let accepted = $([]);
|
||
|
+ for (let nodeOrDOMString of nodesOrDOMStrings) {
|
||
|
+ if (checkShouldInsert(nodeOrDOMString, parent, property, snippetData))
|
||
|
+ accepted.push(nodeOrDOMString);
|
||
|
+ }
|
||
|
+ return accepted;
|
||
|
+ }
|
||
|
+
|
||
|
+ function checkShouldInsert(nodeOrDOMString, parent, property, snippetData) {
|
||
|
+ let aborting = snippetData.shouldAbort;
|
||
|
+ let regExceptions = snippetData.regexExceptions;
|
||
|
+ let expSelectors = snippetData.exceptionSelectors;
|
||
|
+ let id = snippetData.rid;
|
||
|
+ if (typeof nodeOrDOMString == "string") {
|
||
|
+ let domString = nodeOrDOMString;
|
||
|
+ if (isExceptionText(domString, regExceptions))
|
||
|
+ return true;
|
||
|
+ if (debug())
|
||
|
+ logChange(domString, parent, property, snippetData);
|
||
|
+ if (aborting)
|
||
|
+ abort(id);
|
||
|
+ return debug();
|
||
|
+ }
|
||
|
+
|
||
|
+ let node = nodeOrDOMString;
|
||
|
+ switch ($(node).nodeType) {
|
||
|
+ case ELEMENT_NODE:
|
||
|
+ if (isExceptionNode(node, expSelectors))
|
||
|
+ return true;
|
||
|
+ if (aborting) {
|
||
|
+ if (debug())
|
||
|
+ logChange(node, parent, property, snippetData);
|
||
|
+ abort(id);
|
||
|
+ }
|
||
|
+ if (debug()) {
|
||
|
+ hideElement(node);
|
||
|
+ logChange(node, parent, property, snippetData);
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ return false;
|
||
|
+ case TEXT_NODE:
|
||
|
+ if (isExceptionText($(node).textContent, regExceptions))
|
||
|
+ return true;
|
||
|
+ if (debug())
|
||
|
+ logChange(node, parent, property, snippetData);
|
||
|
+ if (aborting)
|
||
|
+ abort(id);
|
||
|
+ return false;
|
||
|
+ default:
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function getAppendChildDescriptor(target, property, shouldValidate,
|
||
|
+ getSnippetData) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let origin = desc.get && call(desc.get, target) || desc.value;
|
||
|
+ if (!origin)
|
||
|
+ return;
|
||
|
+
|
||
|
+ return {
|
||
|
+ get() {
|
||
|
+ return function(...args) {
|
||
|
+ if (shouldValidate(this)) {
|
||
|
+ let snippetData = getSnippetData(this);
|
||
|
+ if (snippetData) {
|
||
|
+ let incomingNode = args[0];
|
||
|
+ if (!checkShouldInsert(incomingNode, this, property, snippetData))
|
||
|
+ return incomingNode;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return apply$2(origin, this, args);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getAppendDescriptor(
|
||
|
+ target, property, shouldValidate, getSnippetData
|
||
|
+ ) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let origin = desc.get && call(desc.get, target) || desc.value;
|
||
|
+ if (!origin)
|
||
|
+ return;
|
||
|
+ return {
|
||
|
+ get() {
|
||
|
+ return function(...nodesOrDOMStrings) {
|
||
|
+ if (!shouldValidate(this))
|
||
|
+ return apply$2(origin, this, nodesOrDOMStrings);
|
||
|
+
|
||
|
+ let snippetData = getSnippetData(this);
|
||
|
+ if (!snippetData)
|
||
|
+ return apply$2(origin, this, nodesOrDOMStrings);
|
||
|
+
|
||
|
+ let accepted = checkMultiple(
|
||
|
+ nodesOrDOMStrings, this, property, snippetData
|
||
|
+ );
|
||
|
+ if (accepted.length > 0)
|
||
|
+ return apply$2(origin, this, accepted);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getInsertAdjacentDescriptor(
|
||
|
+ target, property, shouldValidate, getSnippetData
|
||
|
+ ) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let origin = desc.get && call(desc.get, target) || desc.value;
|
||
|
+ if (!origin)
|
||
|
+ return;
|
||
|
+
|
||
|
+ return {
|
||
|
+ get() {
|
||
|
+ return function(...args) {
|
||
|
+ let [position, value] = args;
|
||
|
+ let isInsideTarget =
|
||
|
+ position === "afterbegin" || position === "beforeend";
|
||
|
+ if (shouldValidate(this, isInsideTarget)) {
|
||
|
+ let snippetData = getSnippetData(this, isInsideTarget);
|
||
|
+ if (snippetData) {
|
||
|
+ let parent = isInsideTarget ?
|
||
|
+ this :
|
||
|
+ $(this).parentNode;
|
||
|
+ let finalValue;
|
||
|
+ switch (property) {
|
||
|
+ case "insertAdjacentElement":
|
||
|
+ if (!checkShouldInsert(value, parent, property, snippetData))
|
||
|
+ return value;
|
||
|
+ break;
|
||
|
+
|
||
|
+ case "insertAdjacentHTML":
|
||
|
+ finalValue = checkHTML(value, parent, property, snippetData);
|
||
|
+ if (finalValue)
|
||
|
+ return call(origin, this, position, finalValue);
|
||
|
+
|
||
|
+ return;
|
||
|
+
|
||
|
+ case "insertAdjacentText":
|
||
|
+ if (!checkShouldInsert(value, parent, property, snippetData))
|
||
|
+ return;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return apply$2(origin, this, args);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getInnerHTMLDescriptor(
|
||
|
+ target, property, shouldValidate, getSnippetData
|
||
|
+ ) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let {set: prevSetter} = desc;
|
||
|
+ if (!prevSetter)
|
||
|
+ return;
|
||
|
+
|
||
|
+ return {
|
||
|
+ set(htmlText) {
|
||
|
+ if (!shouldValidate(this))
|
||
|
+ return call(prevSetter, this, htmlText);
|
||
|
+
|
||
|
+ let snippetData = getSnippetData(this);
|
||
|
+ if (!snippetData)
|
||
|
+ return call(prevSetter, this, htmlText);
|
||
|
+ let finalValue = checkHTML(htmlText, this, property, snippetData);
|
||
|
+ if (finalValue)
|
||
|
+ return call(prevSetter, this, finalValue);
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getTextContentDescriptor(
|
||
|
+ target, property, shouldValidate, getSnippetData
|
||
|
+ ) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let {set: prevSetter} = desc;
|
||
|
+ if (!prevSetter)
|
||
|
+ return;
|
||
|
+
|
||
|
+ return {
|
||
|
+ set(domString) {
|
||
|
+ if (!shouldValidate(this))
|
||
|
+ return call(prevSetter, this, domString);
|
||
|
+
|
||
|
+ let snippetData = getSnippetData(this);
|
||
|
+ if (!snippetData)
|
||
|
+ return call(prevSetter, this, domString);
|
||
|
+ if (checkShouldInsert(domString, this, property, snippetData))
|
||
|
+ return call(prevSetter, this, domString);
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ $(window);
|
||
|
+
|
||
|
+ function raceWinner(name, lose) {
|
||
|
+
|
||
|
+ return noop;
|
||
|
+ }
|
||
|
+
|
||
|
+ const {Map: Map$4, MutationObserver, Object: Object$4, Set, WeakSet: WeakSet$1} = $(window);
|
||
|
+
|
||
|
+ let ElementProto = Element.prototype;
|
||
|
+ let {attachShadow} = ElementProto;
|
||
|
+
|
||
|
+ let hiddenShadowRoots = new WeakSet$1();
|
||
|
+ let searches = new Map$4();
|
||
|
+ let observer = null;
|
||
|
+
|
||
|
+ function hideIfShadowContains(search, selector = "*") {
|
||
|
+
|
||
|
+ let key = `${search}\\${selector}`;
|
||
|
+ if (!searches.has(key)) {
|
||
|
+ searches.set(key, [toRegExp(search), selector, raceWinner()
|
||
|
+ ]);
|
||
|
+ }
|
||
|
+
|
||
|
+ const debugLog = getDebugger("hide-if-shadow-contain");
|
||
|
+
|
||
|
+ if (!observer) {
|
||
|
+ observer = new MutationObserver(records => {
|
||
|
+ let visited = new Set();
|
||
|
+ for (let {target} of $(records)) {
|
||
|
+
|
||
|
+ let parent = $(target).parentNode;
|
||
|
+ while (parent)
|
||
|
+ [target, parent] = [parent, $(target).parentNode];
|
||
|
+
|
||
|
+ if (hiddenShadowRoots.has(target))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (visited.has(target))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ visited.add(target);
|
||
|
+ for (let [re, selfOrParent, win] of searches.values()) {
|
||
|
+ if (re.test($(target).textContent)) {
|
||
|
+ let closest = $(target.host).closest(selfOrParent);
|
||
|
+ if (closest) {
|
||
|
+ win();
|
||
|
+
|
||
|
+ $(target).appendChild(
|
||
|
+ document.createElement("style")
|
||
|
+ ).textContent = ":host {display: none !important}";
|
||
|
+
|
||
|
+ hideElement(closest);
|
||
|
+
|
||
|
+ hiddenShadowRoots.add(target);
|
||
|
+ debugLog("Hiding: ", closest, " for params: ", ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ Object$4.defineProperty(ElementProto, "attachShadow", {
|
||
|
+
|
||
|
+ value: proxy(attachShadow, function() {
|
||
|
+
|
||
|
+ let root = apply$2(attachShadow, this, arguments);
|
||
|
+ debugLog("attachShadow is called for: ", root);
|
||
|
+
|
||
|
+ observer.observe(root, {
|
||
|
+ childList: true,
|
||
|
+ characterData: true,
|
||
|
+ subtree: true
|
||
|
+ });
|
||
|
+
|
||
|
+ return root;
|
||
|
+ })
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ const {Error: Error$4, JSON: JSON$2, Map: Map$3, Object: Object$3} = $(window);
|
||
|
+
|
||
|
+ let paths$1 = null;
|
||
|
+
|
||
|
+ function jsonOverride(rawOverridePaths, value,
|
||
|
+ rawNeedlePaths = "", filter = "") {
|
||
|
+ if (!rawOverridePaths)
|
||
|
+ throw new Error$4("[json-override snippet]: Missing paths to override.");
|
||
|
+
|
||
|
+ if (typeof value == "undefined")
|
||
|
+ throw new Error$4("[json-override snippet]: No value to override with.");
|
||
|
+
|
||
|
+ if (!paths$1) {
|
||
|
+ let debugLog = getDebugger("json-override");
|
||
|
+
|
||
|
+ let {parse} = JSON$2;
|
||
|
+ paths$1 = new Map$3();
|
||
|
+
|
||
|
+ Object$3.defineProperty(window.JSON, "parse", {
|
||
|
+ value: proxy(parse, function(str) {
|
||
|
+ let result = apply$2(parse, this, arguments);
|
||
|
+
|
||
|
+ for (let {prune, needle, filter: flt, value: val} of paths$1.values()) {
|
||
|
+ if (flt && !flt.test(str))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ($(needle).some(path => !findOwner(result, path)))
|
||
|
+ return result;
|
||
|
+
|
||
|
+ for (let path of prune) {
|
||
|
+ let details = findOwner(result, path);
|
||
|
+ if (typeof details != "undefined") {
|
||
|
+ debugLog(`Found ${path} replaced it with ${val}`);
|
||
|
+ details[0][details[1]] = overrideValue(val);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return result;
|
||
|
+ })
|
||
|
+ });
|
||
|
+ debugLog("Wrapped JSON.parse for override");
|
||
|
+ }
|
||
|
+
|
||
|
+ paths$1.set(rawOverridePaths, {
|
||
|
+ prune: $(rawOverridePaths).split(/ +/),
|
||
|
+ needle: rawNeedlePaths.length ? $(rawNeedlePaths).split(/ +/) : [],
|
||
|
+ filter: filter ? toRegExp(filter) : null,
|
||
|
+ value
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Error: Error$3, JSON: JSON$1, Map: Map$2, Object: Object$2} = $(window);
|
||
|
+
|
||
|
+ let paths = null;
|
||
|
+
|
||
|
+ function jsonPrune(rawPrunePaths, rawNeedlePaths = "") {
|
||
|
+ if (!rawPrunePaths)
|
||
|
+ throw new Error$3("Missing paths to prune");
|
||
|
+
|
||
|
+ if (!paths) {
|
||
|
+ let debugLog = getDebugger("json-prune");
|
||
|
+
|
||
|
+ let {parse} = JSON$1;
|
||
|
+ paths = new Map$2();
|
||
|
+
|
||
|
+ Object$2.defineProperty(window.JSON, "parse", {
|
||
|
+ value: proxy(parse, function() {
|
||
|
+ let result = apply$2(parse, this, arguments);
|
||
|
+
|
||
|
+ for (let {prune, needle} of paths.values()) {
|
||
|
+ if ($(needle).some(path => !findOwner(result, path)))
|
||
|
+ return result;
|
||
|
+
|
||
|
+ for (let path of prune) {
|
||
|
+ let details = findOwner(result, path);
|
||
|
+ if (typeof details != "undefined") {
|
||
|
+ debugLog(`Found ${path} and deleted`);
|
||
|
+ delete details[0][details[1]];
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return result;
|
||
|
+ })
|
||
|
+ });
|
||
|
+ debugLog("Wrapped JSON.parse for prune");
|
||
|
+ }
|
||
|
+
|
||
|
+ paths.set(rawPrunePaths, {
|
||
|
+ prune: $(rawPrunePaths).split(/ +/),
|
||
|
+ needle: rawNeedlePaths.length ? $(rawNeedlePaths).split(/ +/) : []
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Error: Error$2} = $(window);
|
||
|
+
|
||
|
+ function overridePropertyRead(property, value) {
|
||
|
+ if (!property) {
|
||
|
+ throw new Error$2("[override-property-read snippet]: " +
|
||
|
+ "No property to override.");
|
||
|
+ }
|
||
|
+ if (typeof value === "undefined") {
|
||
|
+ throw new Error$2("[override-property-read snippet]: " +
|
||
|
+ "No value to override with.");
|
||
|
+ }
|
||
|
+
|
||
|
+ let debugLog = getDebugger("override-property-read");
|
||
|
+
|
||
|
+ let cValue = overrideValue(value);
|
||
|
+
|
||
|
+ let newGetter = () => {
|
||
|
+ debugLog(`${property} override done.`);
|
||
|
+ return cValue;
|
||
|
+ };
|
||
|
+
|
||
|
+ debugLog(`Overriding ${property}.`);
|
||
|
+
|
||
|
+ wrapPropertyAccess(window, property, {get: newGetter, set() {}});
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Error: Error$1, Map: Map$1, Object: Object$1, console: console$1} = $(window);
|
||
|
+
|
||
|
+ let {toString} = Function.prototype;
|
||
|
+ let EventTargetProto = EventTarget.prototype;
|
||
|
+ let {addEventListener} = EventTargetProto;
|
||
|
+
|
||
|
+ let events = null;
|
||
|
+
|
||
|
+ function preventListener(event, eventHandler, selector) {
|
||
|
+ if (!event)
|
||
|
+ throw new Error$1("[prevent-listener snippet]: No event type.");
|
||
|
+
|
||
|
+ if (!events) {
|
||
|
+ events = new Map$1();
|
||
|
+
|
||
|
+ let debugLog = getDebugger("[prevent]");
|
||
|
+
|
||
|
+ Object$1.defineProperty(EventTargetProto, "addEventListener", {
|
||
|
+ value: proxy(addEventListener, function(type, listener) {
|
||
|
+ for (let {evt, handlers, selectors} of events.values()) {
|
||
|
+
|
||
|
+ if (!evt.test(type))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ let isElement = this instanceof Element;
|
||
|
+
|
||
|
+ for (let i = 0; i < handlers.length; i++) {
|
||
|
+ let handler = handlers[i];
|
||
|
+ let sel = selectors[i];
|
||
|
+
|
||
|
+ let handlerMatch = () => handler.test(
|
||
|
+ call(
|
||
|
+ toString,
|
||
|
+ typeof listener === "function" ?
|
||
|
+ listener : listener.handleEvent
|
||
|
+ )
|
||
|
+ );
|
||
|
+
|
||
|
+ if (
|
||
|
+ (handler && !handlerMatch()) ||
|
||
|
+ (sel && !(isElement && $(this).matches(sel)))
|
||
|
+ )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (debug()) {
|
||
|
+ console$1.groupCollapsed("DEBUG [prevent] was successful");
|
||
|
+ debugLog(`type: ${type} matching ${evt}`);
|
||
|
+ debugLog("handler:", listener);
|
||
|
+ if (handler)
|
||
|
+ debugLog(`matching ${handler}`);
|
||
|
+ if (sel)
|
||
|
+ debugLog("on element: ", this, ` matching ${sel}`);
|
||
|
+ debugLog("was prevented from being added");
|
||
|
+ console$1.groupEnd();
|
||
|
+ }
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return apply$2(addEventListener, this, arguments);
|
||
|
+ })
|
||
|
+ });
|
||
|
+
|
||
|
+ debugLog("Wrapped addEventListener");
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!events.has(event))
|
||
|
+ events.set(event, {evt: toRegExp(event), handlers: [], selectors: []});
|
||
|
+
|
||
|
+ let {handlers, selectors} = events.get(event);
|
||
|
+
|
||
|
+ handlers.push(eventHandler ? toRegExp(eventHandler) : null);
|
||
|
+ selectors.push(selector);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {URL, fetch} = $(window);
|
||
|
+
|
||
|
+ let {delete: deleteParam, has: hasParam} = caller(URLSearchParams.prototype);
|
||
|
+
|
||
|
+ let parameters;
|
||
|
+
|
||
|
+ function stripFetchQueryParameter(name, urlPattern = null) {
|
||
|
+ const debugLog = getDebugger("strip-fetch-query-parameter");
|
||
|
+
|
||
|
+ if (!parameters) {
|
||
|
+ parameters = new Map();
|
||
|
+ window.fetch = proxy(fetch, (...args) => {
|
||
|
+ let [source] = args;
|
||
|
+ if (typeof source === "string") {
|
||
|
+ let url = new URL(source);
|
||
|
+ for (let [key, reg] of parameters) {
|
||
|
+ if (!reg || reg.test(source)) {
|
||
|
+ if (hasParam(url.searchParams, key)) {
|
||
|
+ debugLog(`${key} has been stripped from url ${source}`);
|
||
|
+ deleteParam(url.searchParams, key);
|
||
|
+ args[0] = url.href;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return apply$2(fetch, self, args);
|
||
|
+ });
|
||
|
+ }
|
||
|
+ parameters.set(name, urlPattern && toRegExp(urlPattern));
|
||
|
+ }
|
||
|
+
|
||
|
+ function trace(...args) {
|
||
|
+
|
||
|
+ apply$2(log, null, args);
|
||
|
+ }
|
||
|
+
|
||
|
+ const snippets = {
|
||
|
+ "abort-current-inline-script": abortCurrentInlineScript,
|
||
|
+ "abort-on-iframe-property-read": abortOnIframePropertyRead,
|
||
|
+ "abort-on-iframe-property-write": abortOnIframePropertyWrite,
|
||
|
+ "abort-on-property-read": abortOnPropertyRead,
|
||
|
+ "abort-on-property-write": abortOnPropertyWrite,
|
||
|
+ "cookie-remover": cookieRemover,
|
||
|
+ "debug": setDebug,
|
||
|
+ "freeze-element": freezeElement,
|
||
|
+ "hide-if-shadow-contains": hideIfShadowContains,
|
||
|
+ "json-override": jsonOverride,
|
||
|
+ "json-prune": jsonPrune,
|
||
|
+ "override-property-read": overridePropertyRead,
|
||
|
+ "prevent-listener": preventListener,
|
||
|
+ "strip-fetch-query-parameter": stripFetchQueryParameter,
|
||
|
+ "trace": trace
|
||
|
+ };
|
||
|
+ let context;
|
||
|
+ for (const [name, ...args] of filters) {
|
||
|
+ if (snippets.hasOwnProperty(name)) {
|
||
|
+ try { context = snippets[name].apply(context, args); }
|
||
|
+ catch (error) { console.error(error); }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ context = void 0;
|
||
|
+};
|
||
|
+const graph = new Map([["abort-current-inline-script",null],["abort-on-iframe-property-read",null],["abort-on-iframe-property-write",null],["abort-on-property-read",null],["abort-on-property-write",null],["cookie-remover",null],["debug",null],["freeze-element",null],["hide-if-shadow-contains",null],["json-override",null],["json-prune",null],["override-property-read",null],["prevent-listener",null],["strip-fetch-query-parameter",null],["trace",null]]);
|
||
|
+callback.get = snippet => graph.get(snippet);
|
||
|
+callback.has = snippet => graph.has(snippet);
|
||
|
+
|
||
|
+ if (t.every(([name]) => !callback.has(name))) return;
|
||
|
+ const append = () => {
|
||
|
+ URL.revokeObjectURL(
|
||
|
+ Object.assign(
|
||
|
+ document.documentElement.appendChild(document.createElement("script")),
|
||
|
+ {async: false, src: URL.createObjectURL(new Blob([
|
||
|
+ "(" + callback + ")(..." + JSON.stringify([e, ...t]) + ")"
|
||
|
+ ]))}
|
||
|
+ ).src
|
||
|
+ );
|
||
|
+ };
|
||
|
+ try { append(); }
|
||
|
+ catch (_) {
|
||
|
+ document.addEventListener("readystatechange", append, {once:true});
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/components/resources/adblocking/snippets/dist/isolated-first.jst b/components/resources/adblocking/snippets/dist/isolated-first.jst
|
||
|
new file mode 100755
|
||
|
--- /dev/null
|
||
|
+++ b/components/resources/adblocking/snippets/dist/isolated-first.jst
|
||
|
@@ -0,0 +1,65 @@
|
||
|
+(e, ...t) => {
|
||
|
+/*!
|
||
|
+ * snippets v0.7.0
|
||
|
+ * https://gitlab.com/eyeo/snippets/-/blob/d7c80795567d9f9c44a6ebaa789baceab85f31f9/dist/isolated-first.jst
|
||
|
+ *
|
||
|
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
|
||
|
+ * Copyright (C) 2006-present eyeo GmbH
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is free software: you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License version 3 as
|
||
|
+ * published by the Free Software Foundation.
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License
|
||
|
+ * along with @eyeo/snippets. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+ */
|
||
|
+ ((environment, ...filters) => {
|
||
|
+const e=Proxy,{apply:t,bind:n,call:o}=Function,r=o.bind(t),i=o.bind(n),s=o.bind(o),a={get:(e,t)=>i(o,e[t])},c=t=>new e(t,a),l={get:(e,t)=>i(e[t],e)},u=t=>new e(t,l),{assign:d,defineProperties:h,freeze:f,getOwnPropertyDescriptor:p,getOwnPropertyDescriptors:w,getPrototypeOf:g}=u(Object),{hasOwnProperty:m}=c({}),{species:b}=Symbol,y={get(e,t){const n=e[t];class o extends n{}const r=w(n.prototype);delete r.constructor,f(h(o.prototype,r));const i=w(n);return delete i.length,delete i.prototype,i[b]={value:o},f(h(o,i))}},v=t=>new e(t,y),S="undefined"!=typeof environment?environment:{};"undefined"==typeof globalThis&&(window.globalThis=window);const{apply:k,ownKeys:E}=u(Reflect),M="world"in S,x=M&&"ISOLATED"===S.world,C=M&&"MAIN"===S.world,T="object"==typeof chrome&&!!chrome.runtime,W="object"==typeof browser&&!!browser.runtime,O=!C&&(x||T||W),P=e=>O?e:L(e,R(e)),{create:L,defineProperties:D,defineProperty:N,freeze:I,getOwnPropertyDescriptor:A,getOwnPropertyDescriptors:R}=u(Object),V=u(globalThis),$=O?globalThis:v(globalThis),{Map:H,RegExp:j,Set:_,WeakMap:F,WeakSet:q}=$,B=(e,t,n=null)=>{const o=E(t);for(const r of E(e)){if(o.includes(r))continue;const i=A(e,r);if(n&&"value"in i){const{value:e}=i;"function"==typeof e&&(i.value=n(e))}N(t,r,i)}},z=e=>{const t=$[e];class n extends t{}const{toString:o,valueOf:r}=t.prototype;D(n.prototype,{toString:{value:o},valueOf:{value:r}});const i=e.toLowerCase(),s=e=>function(){const t=k(e,this,arguments);return typeof t===i?new n(t):t};return B(t,n,s),B(t.prototype,n.prototype,s),n},X=I({frozen:new F,hidden:new q,iframePropertiesToAbort:{read:new _,write:new _},abortedIframes:new F}),J=new j("^[A-Z]");var U=new Proxy(new H([["chrome",O&&(T&&chrome||W&&browser)||void 0],["isExtensionContext",O],["variables",X],["console",P(console)],["document",globalThis.document],["performance",P(performance)],["JSON",P(JSON)],["Map",H],["Math",P(Math)],["Number",O?Number:z("Number")],["RegExp",j],["Set",_],["String",O?String:z("String")],["WeakMap",F],["WeakSet",q],["MouseEvent",MouseEvent]]),{get(e,t){if(e.has(t))return e.get(t);let n=globalThis[t];return"function"==typeof n&&(n=(J.test(t)?$:V)[t]),e.set(t,n),n},has:(e,t)=>e.has(t)});const G={WeakSet:WeakSet,WeakMap:WeakMap,WeakValue:class{has(){return!1}set(){}}},{apply:K}=Reflect;const{Map:Q,WeakMap:Y,WeakSet:Z,setTimeout:ee}=U;let te=!0,ne=e=>{e.clear(),te=!te};var oe=function(e){const{WeakSet:t,WeakMap:n,WeakValue:o}=this||G,r=new t,i=new n,s=new o;return function(t){if(r.has(t))return t;if(i.has(t))return i.get(t);if(s.has(t))return s.get(t);const n=K(e,this,arguments);return r.add(n),n!==t&&("object"==typeof t&&t?i:s).set(t,n),n}}.bind({WeakMap:Y,WeakSet:Z,WeakValue:class extends Q{set(e,t){return te&&(te=!te,ee(ne,0,this)),super.set(e,t)}}});const{concat:re,includes:ie,join:se,reduce:ae,unshift:ce}=c([]),le=v(globalThis),{Map:ue,WeakMap:de}=le,he=new ue,fe=t=>{const n=(e=>{const t=[];let n=e;for(;n;){if(he.has(n))ce(t,he.get(n));else{const e=w(n);he.set(n,e),ce(t,e)}n=g(n)}return ce(t,{}),r(d,null,t)})("function"==typeof t?t.prototype:t),o={get(e,t){if(t in n){const{value:o,get:r}=n[t];if(r)return s(r,e);if("function"==typeof o)return i(o,e)}return e[t]},set(e,t,o){if(t in n){const{set:r}=n[t];if(r)return s(r,e,o),!0}return e[t]=o,!0}};return t=>new e(t,o)},{isExtensionContext:pe,Array:we,Number:ge,String:me,Object:be}=U,{isArray:ye}=we,{getOwnPropertyDescriptor:ve,setPrototypeOf:Se}=be,{toString:ke}=be.prototype,{slice:Ee}=me.prototype,{get:Me}=ve(Node.prototype,"nodeType"),xe=pe?{}:{Attr:fe(Attr),CanvasRenderingContext2D:fe(CanvasRenderingContext2D),CSSStyleDeclaration:fe(CSSStyleDeclaration),Document:fe(Document),Element:fe(Element),HTMLCanvasElement:fe(HTMLCanvasElement),HTMLElement:fe(HTMLElement),HTMLImageElement:fe(HTMLImageElement),HTMLScriptElement:fe(HTMLScriptElement),MutationRecord:fe(MutationRecord),Node:fe(Node),ShadowRoot:fe(ShadowRoot),get CSS2Properties(){return xe.CSSStyleDeclaration}},Ce=(e,t)=>{if("Element"!==t&&t in xe)return xe[t](e);if(ye(e))return Se(e,we.prototype);const n=(e=>s(Ee,s(ke,e),8,-1))(e);if(n in xe)return xe[n](e)
|
||
|
+const snippets=fn;
|
||
|
+let context;
|
||
|
+for (const [name, ...args] of filters) {
|
||
|
+if (snippets.hasOwnProperty(name)) {
|
||
|
+try { context = snippets[name].apply(context, args); }
|
||
|
+catch (error) { console.error(error); }
|
||
|
+}
|
||
|
+}
|
||
|
+context = void 0;
|
||
|
+})(e, ...t);
|
||
|
+
|
||
|
+const callback = (environment, ...filters) => {
|
||
|
+const e=Proxy,{apply:t,bind:r,call:n}=Function,o=n.bind(t),i=n.bind(r),s=n.bind(n),a={get:(e,t)=>i(n,e[t])},l=t=>new e(t,a),c=(t,r)=>new e(t,{apply:(e,t,n)=>o(r,t,n)}),u={get:(e,t)=>i(e[t],e)},f=t=>new e(t,u),{assign:p,defineProperties:d,freeze:h,getOwnPropertyDescriptor:w,getOwnPropertyDescriptors:g,getPrototypeOf:y}=f(Object),{hasOwnProperty:m}=l({}),{species:b}=Symbol,v={get(e,t){const r=e[t];class n extends r{}const o=g(r.prototype);delete o.constructor,h(d(n.prototype,o));const i=g(r);return delete i.length,delete i.prototype,i[b]={value:n},h(d(n,i))}},E=t=>new e(t,v),S="undefined"!=typeof environment?environment:{};"undefined"==typeof globalThis&&(window.globalThis=window);const{apply:M,ownKeys:T}=f(Reflect),x="world"in S,O=x&&"ISOLATED"===S.world,P=x&&"MAIN"===S.world,j="object"==typeof chrome&&!!chrome.runtime,N="object"==typeof browser&&!!browser.runtime,L=!P&&(O||j||N),k=e=>L?e:C(e,H(e)),{create:C,defineProperties:A,defineProperty:W,freeze:$,getOwnPropertyDescriptor:D,getOwnPropertyDescriptors:H}=f(Object),z=f(globalThis),R=L?globalThis:E(globalThis),{Map:F,RegExp:I,Set:J,WeakMap:V,WeakSet:B}=R,U=(e,t,r=null)=>{const n=T(t);for(const o of T(e)){if(n.includes(o))continue;const i=D(e,o);if(r&&"value"in i){const{value:e}=i;"function"==typeof e&&(i.value=r(e))}W(t,o,i)}},_=e=>{const t=R[e];class r extends t{}const{toString:n,valueOf:o}=t.prototype;A(r.prototype,{toString:{value:n},valueOf:{value:o}});const i=e.toLowerCase(),s=e=>function(){const t=M(e,this,arguments);return typeof t===i?new r(t):t};return U(t,r,s),U(t.prototype,r.prototype,s),r},X=$({frozen:new V,hidden:new B,iframePropertiesToAbort:{read:new J,write:new J},abortedIframes:new V}),q=new I("^[A-Z]");var G=new Proxy(new F([["chrome",L&&(j&&chrome||N&&browser)||void 0],["isExtensionContext",L],["variables",X],["console",k(console)],["document",globalThis.document],["performance",k(performance)],["JSON",k(JSON)],["Map",F],["Math",k(Math)],["Number",L?Number:_("Number")],["RegExp",I],["Set",J],["String",L?String:_("String")],["WeakMap",V],["WeakSet",B],["MouseEvent",MouseEvent]]),{get(e,t){if(e.has(t))return e.get(t);let r=globalThis[t];return"function"==typeof r&&(r=(q.test(t)?R:z)[t]),e.set(t,r),r},has:(e,t)=>e.has(t)});const K={WeakSet:WeakSet,WeakMap:WeakMap,WeakValue:class{has(){return!1}set(){}}},{apply:Y}=Reflect;const{Map:Z,WeakMap:Q,WeakSet:ee,setTimeout:te}=G;let re=!0,ne=e=>{e.clear(),re=!re};var oe=function(e){const{WeakSet:t,WeakMap:r,WeakValue:n}=this||K,o=new t,i=new r,s=new n;return function(t){if(o.has(t))return t;if(i.has(t))return i.get(t);if(s.has(t))return s.get(t);const r=Y(e,this,arguments);return o.add(r),r!==t&&("object"==typeof t&&t?i:s).set(t,r),r}}.bind({WeakMap:Q,WeakSet:ee,WeakValue:class extends Z{set(e,t){return re&&(re=!re,te(ne,0,this)),super.set(e,t)}}});const{concat:ie,includes:se,join:ae,reduce:le,unshift:ce}=l([]),ue=E(globalThis),{Map:fe,WeakMap:pe}=ue,de=new fe,he=t=>{const r=(e=>{const t=[];let r=e;for(;r;){if(de.has(r))ce(t,de.get(r));else{const e=g(r);de.set(r,e),ce(t,e)}r=y(r)}return ce(t,{}),o(p,null,t)})("function"==typeof t?t.prototype:t),n={get(e,t){if(t in r){const{value:n,get:o}=r[t];if(o)return s(o,e);if("function"==typeof n)return i(n,e)}return e[t]},set(e,t,n){if(t in r){const{set:o}=r[t];if(o)return s(o,e,n),!0}return e[t]=n,!0}};return t=>new e(t,n)},{isExtensionContext:we,Array:ge,Number:ye,String:me,Object:be}=G,{isArray:ve}=ge,{getOwnPropertyDescriptor:Ee,setPrototypeOf:Se}=be,{toString:Me}=be.prototype,{slice:Te}=me.prototype,{get:xe}=Ee(Node.prototype,"nodeType"),Oe=we?{}:{Attr:he(Attr),CanvasRenderingContext2D:he(CanvasRenderingContext2D),CSSStyleDeclaration:he(CSSStyleDeclaration),Document:he(Document),Element:he(Element),HTMLCanvasElement:he(HTMLCanvasElement),HTMLElement:he(HTMLElement),HTMLImageElement:he(HTMLImageElement),HTMLScriptElement:he(HTMLScriptElement),MutationRecord:he(MutationRecord),Node:he(Node),ShadowRoot:he(ShadowRoot),get CSS2Properties(){return Oe.CSSStyleDeclaration}},Pe=(e,t)=>{if("Element"!==t&&t in Oe)return Oe[t](e);if(ve(e))return Se(e,ge.prototype);const r=(e=>s(T
|
||
|
+const snippets=Pr;
|
||
|
+let context;
|
||
|
+for (const [name, ...args] of filters) {
|
||
|
+if (snippets.hasOwnProperty(name)) {
|
||
|
+try { context = snippets[name].apply(context, args); }
|
||
|
+catch (error) { console.error(error); }
|
||
|
+}
|
||
|
+}
|
||
|
+context = void 0;
|
||
|
+};
|
||
|
+const graph = new Map([["abort-current-inline-script",null],["abort-on-iframe-property-read",null],["abort-on-iframe-property-write",null],["abort-on-property-read",null],["abort-on-property-write",null],["cookie-remover",null],["debug",null],["freeze-element",null],["hide-if-shadow-contains",null],["json-override",null],["json-prune",null],["override-property-read",null],["prevent-listener",null],["strip-fetch-query-parameter",null],["trace",null]]);
|
||
|
+callback.get = snippet => graph.get(snippet);
|
||
|
+callback.has = snippet => graph.has(snippet);
|
||
|
+
|
||
|
+ if (t.every(([name]) => !callback.has(name))) return;
|
||
|
+ const append = () => {
|
||
|
+ URL.revokeObjectURL(
|
||
|
+ Object.assign(
|
||
|
+ document.documentElement.appendChild(document.createElement("script")),
|
||
|
+ {async: false, src: URL.createObjectURL(new Blob([
|
||
|
+ "(" + callback + ")(..." + JSON.stringify([e, ...t]) + ")"
|
||
|
+ ]))}
|
||
|
+ ).src
|
||
|
+ );
|
||
|
+ };
|
||
|
+ try { append(); }
|
||
|
+ catch (_) {
|
||
|
+ document.addEventListener("readystatechange", append, {once:true});
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/components/resources/adblocking/snippets/dist/isolated-first.source.jst b/components/resources/adblocking/snippets/dist/isolated-first.source.jst
|
||
|
new file mode 100755
|
||
|
--- /dev/null
|
||
|
+++ b/components/resources/adblocking/snippets/dist/isolated-first.source.jst
|
||
|
@@ -0,0 +1,3624 @@
|
||
|
+(e, ...t) => {
|
||
|
+/*!
|
||
|
+ * snippets v0.7.0 (https://gitlab.com/eyeo/anti-cv/snippets/-/tree/5f54b58f039a604088d65991f2829a9a081832f6/dist)
|
||
|
+ * https://gitlab.com/eyeo/snippets/-/commit/d7c80795567d9f9c44a6ebaa789baceab85f31f9
|
||
|
+ *
|
||
|
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
|
||
|
+ * Copyright (C) 2006-present eyeo GmbH
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is free software: you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License version 3 as
|
||
|
+ * published by the Free Software Foundation.
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License
|
||
|
+ * along with @eyeo/snippets. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+ */
|
||
|
+ ((environment, ...filters) => {
|
||
|
+ /*!
|
||
|
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
|
||
|
+ * Copyright (C) 2006-present eyeo GmbH
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is free software: you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License version 3 as
|
||
|
+ * published by the Free Software Foundation.
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License
|
||
|
+ * along with @eyeo/snippets. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+ */
|
||
|
+ const $$1 = Proxy;
|
||
|
+
|
||
|
+ const {apply: a, bind: b, call: c} = Function;
|
||
|
+ const apply$2 = c.bind(a);
|
||
|
+ const bind = c.bind(b);
|
||
|
+ const call = c.bind(c);
|
||
|
+
|
||
|
+ const callerHandler = {
|
||
|
+ get(target, name) {
|
||
|
+ return bind(c, target[name]);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const caller = target => new $$1(target, callerHandler);
|
||
|
+
|
||
|
+ const handler$2 = {
|
||
|
+ get(target, name) {
|
||
|
+ return bind(target[name], target);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const bound = target => new $$1(target, handler$2);
|
||
|
+
|
||
|
+ const {
|
||
|
+ assign: assign$1,
|
||
|
+ defineProperties: defineProperties$1,
|
||
|
+ freeze: freeze$1,
|
||
|
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$2,
|
||
|
+ getOwnPropertyDescriptors: getOwnPropertyDescriptors$1,
|
||
|
+ getPrototypeOf
|
||
|
+ } = bound(Object);
|
||
|
+
|
||
|
+ const {hasOwnProperty} = caller({});
|
||
|
+
|
||
|
+ const {species} = Symbol;
|
||
|
+
|
||
|
+ const handler$1 = {
|
||
|
+ get(target, name) {
|
||
|
+ const Native = target[name];
|
||
|
+ class Secure extends Native {}
|
||
|
+
|
||
|
+ const proto = getOwnPropertyDescriptors$1(Native.prototype);
|
||
|
+ delete proto.constructor;
|
||
|
+ freeze$1(defineProperties$1(Secure.prototype, proto));
|
||
|
+
|
||
|
+ const statics = getOwnPropertyDescriptors$1(Native);
|
||
|
+ delete statics.length;
|
||
|
+ delete statics.prototype;
|
||
|
+ statics[species] = {value: Secure};
|
||
|
+ return freeze$1(defineProperties$1(Secure, statics));
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const secure = target => new $$1(target, handler$1);
|
||
|
+
|
||
|
+ const libEnvironment = typeof environment !== "undefined" ? environment :
|
||
|
+ {};
|
||
|
+
|
||
|
+ if (typeof globalThis === "undefined")
|
||
|
+ window.globalThis = window;
|
||
|
+
|
||
|
+ const {apply: apply$1, ownKeys} = bound(Reflect);
|
||
|
+
|
||
|
+ const worldEnvDefined = "world" in libEnvironment;
|
||
|
+ const isIsolatedWorld = worldEnvDefined && libEnvironment.world === "ISOLATED";
|
||
|
+ const isMainWorld = worldEnvDefined && libEnvironment.world === "MAIN";
|
||
|
+ const isChrome = typeof chrome === "object" && !!chrome.runtime;
|
||
|
+ const isOtherThanChrome = typeof browser === "object" && !!browser.runtime;
|
||
|
+ const isExtensionContext$2 = !isMainWorld &&
|
||
|
+ (isIsolatedWorld || isChrome || isOtherThanChrome);
|
||
|
+ const copyIfExtension = value => isExtensionContext$2 ?
|
||
|
+ value :
|
||
|
+ create(value, getOwnPropertyDescriptors(value));
|
||
|
+
|
||
|
+ const {
|
||
|
+ create,
|
||
|
+ defineProperties,
|
||
|
+ defineProperty,
|
||
|
+ freeze,
|
||
|
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$1,
|
||
|
+ getOwnPropertyDescriptors
|
||
|
+ } = bound(Object);
|
||
|
+
|
||
|
+ const invokes = bound(globalThis);
|
||
|
+ const classes = isExtensionContext$2 ? globalThis : secure(globalThis);
|
||
|
+ const {Map: Map$5, RegExp: RegExp$1, Set, WeakMap: WeakMap$3, WeakSet: WeakSet$b} = classes;
|
||
|
+
|
||
|
+ const augment = (source, target, method = null) => {
|
||
|
+ const known = ownKeys(target);
|
||
|
+ for (const key of ownKeys(source)) {
|
||
|
+ if (known.includes(key))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ const descriptor = getOwnPropertyDescriptor$1(source, key);
|
||
|
+ if (method && "value" in descriptor) {
|
||
|
+ const {value} = descriptor;
|
||
|
+ if (typeof value === "function")
|
||
|
+ descriptor.value = method(value);
|
||
|
+ }
|
||
|
+ defineProperty(target, key, descriptor);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const primitive = name => {
|
||
|
+ const Super = classes[name];
|
||
|
+ class Class extends Super {}
|
||
|
+ const {toString, valueOf} = Super.prototype;
|
||
|
+ defineProperties(Class.prototype, {
|
||
|
+ toString: {value: toString},
|
||
|
+ valueOf: {value: valueOf}
|
||
|
+ });
|
||
|
+ const type = name.toLowerCase();
|
||
|
+ const method = callback => function() {
|
||
|
+ const result = apply$1(callback, this, arguments);
|
||
|
+ return typeof result === type ? new Class(result) : result;
|
||
|
+ };
|
||
|
+ augment(Super, Class, method);
|
||
|
+ augment(Super.prototype, Class.prototype, method);
|
||
|
+ return Class;
|
||
|
+ };
|
||
|
+
|
||
|
+ const variables$1 = freeze({
|
||
|
+ frozen: new WeakMap$3(),
|
||
|
+ hidden: new WeakSet$b(),
|
||
|
+ iframePropertiesToAbort: {
|
||
|
+ read: new Set(),
|
||
|
+ write: new Set()
|
||
|
+ },
|
||
|
+ abortedIframes: new WeakMap$3()
|
||
|
+ });
|
||
|
+
|
||
|
+ const startsCapitalized = new RegExp$1("^[A-Z]");
|
||
|
+
|
||
|
+ var env = new Proxy(new Map$5([
|
||
|
+
|
||
|
+ ["chrome", (
|
||
|
+ isExtensionContext$2 && (
|
||
|
+ (isChrome && chrome) ||
|
||
|
+ (isOtherThanChrome && browser)
|
||
|
+ )
|
||
|
+ ) || void 0],
|
||
|
+ ["isExtensionContext", isExtensionContext$2],
|
||
|
+ ["variables", variables$1],
|
||
|
+
|
||
|
+ ["console", copyIfExtension(console)],
|
||
|
+ ["document", globalThis.document],
|
||
|
+ ["performance", copyIfExtension(performance)],
|
||
|
+ ["JSON", copyIfExtension(JSON)],
|
||
|
+ ["Map", Map$5],
|
||
|
+ ["Math", copyIfExtension(Math)],
|
||
|
+ ["Number", isExtensionContext$2 ? Number : primitive("Number")],
|
||
|
+ ["RegExp", RegExp$1],
|
||
|
+ ["Set", Set],
|
||
|
+ ["String", isExtensionContext$2 ? String : primitive("String")],
|
||
|
+ ["WeakMap", WeakMap$3],
|
||
|
+ ["WeakSet", WeakSet$b],
|
||
|
+
|
||
|
+ ["MouseEvent", MouseEvent]
|
||
|
+ ]), {
|
||
|
+ get(map, key) {
|
||
|
+ if (map.has(key))
|
||
|
+ return map.get(key);
|
||
|
+
|
||
|
+ let value = globalThis[key];
|
||
|
+ if (typeof value === "function")
|
||
|
+ value = (startsCapitalized.test(key) ? classes : invokes)[key];
|
||
|
+
|
||
|
+ map.set(key, value);
|
||
|
+ return value;
|
||
|
+ },
|
||
|
+ has(map, key) {
|
||
|
+ return map.has(key);
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ class WeakValue {
|
||
|
+ has() { return false; }
|
||
|
+ set() {}
|
||
|
+ }
|
||
|
+
|
||
|
+ const helpers = {WeakSet, WeakMap, WeakValue};
|
||
|
+ const {apply} = Reflect;
|
||
|
+
|
||
|
+ function transformOnce (callback) { const {WeakSet, WeakMap, WeakValue} = (this || helpers);
|
||
|
+ const ws = new WeakSet;
|
||
|
+ const wm = new WeakMap;
|
||
|
+ const wv = new WeakValue;
|
||
|
+ return function (any) {
|
||
|
+ if (ws.has(any))
|
||
|
+ return any;
|
||
|
+
|
||
|
+ if (wm.has(any))
|
||
|
+ return wm.get(any);
|
||
|
+
|
||
|
+ if (wv.has(any))
|
||
|
+ return wv.get(any);
|
||
|
+
|
||
|
+ const value = apply(callback, this, arguments);
|
||
|
+ ws.add(value);
|
||
|
+ if (value !== any)
|
||
|
+ (typeof any === 'object' && any ? wm : wv).set(any, value);
|
||
|
+ return value;
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ const {Map: Map$4, WeakMap: WeakMap$2, WeakSet: WeakSet$a, setTimeout: setTimeout$3} = env;
|
||
|
+
|
||
|
+ let cleanup = true;
|
||
|
+ let cleanUpCallback = map => {
|
||
|
+ map.clear();
|
||
|
+ cleanup = !cleanup;
|
||
|
+ };
|
||
|
+
|
||
|
+ var transformer = transformOnce.bind({
|
||
|
+ WeakMap: WeakMap$2,
|
||
|
+ WeakSet: WeakSet$a,
|
||
|
+
|
||
|
+ WeakValue: class extends Map$4 {
|
||
|
+ set(key, value) {
|
||
|
+ if (cleanup) {
|
||
|
+ cleanup = !cleanup;
|
||
|
+ setTimeout$3(cleanUpCallback, 0, this);
|
||
|
+ }
|
||
|
+ return super.set(key, value);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ const {concat, includes, join, reduce, unshift} = caller([]);
|
||
|
+
|
||
|
+ const globals = secure(globalThis);
|
||
|
+
|
||
|
+ const {
|
||
|
+ Map: Map$3,
|
||
|
+ WeakMap: WeakMap$1
|
||
|
+ } = globals;
|
||
|
+
|
||
|
+ const map = new Map$3;
|
||
|
+ const descriptors = target => {
|
||
|
+ const chain = [];
|
||
|
+ let current = target;
|
||
|
+ while (current) {
|
||
|
+ if (map.has(current))
|
||
|
+ unshift(chain, map.get(current));
|
||
|
+ else {
|
||
|
+ const descriptors = getOwnPropertyDescriptors$1(current);
|
||
|
+ map.set(current, descriptors);
|
||
|
+ unshift(chain, descriptors);
|
||
|
+ }
|
||
|
+ current = getPrototypeOf(current);
|
||
|
+ }
|
||
|
+ unshift(chain, {});
|
||
|
+ return apply$2(assign$1, null, chain);
|
||
|
+ };
|
||
|
+
|
||
|
+ const chain = source => {
|
||
|
+ const target = typeof source === 'function' ? source.prototype : source;
|
||
|
+ const chained = descriptors(target);
|
||
|
+ const handler = {
|
||
|
+ get(target, key) {
|
||
|
+ if (key in chained) {
|
||
|
+ const {value, get} = chained[key];
|
||
|
+ if (get)
|
||
|
+ return call(get, target);
|
||
|
+ if (typeof value === 'function')
|
||
|
+ return bind(value, target);
|
||
|
+ }
|
||
|
+ return target[key];
|
||
|
+ },
|
||
|
+ set(target, key, value) {
|
||
|
+ if (key in chained) {
|
||
|
+ const {set} = chained[key];
|
||
|
+ if (set) {
|
||
|
+ call(set, target, value);
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ target[key] = value;
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ };
|
||
|
+ return target => new $$1(target, handler);
|
||
|
+ };
|
||
|
+
|
||
|
+ const {
|
||
|
+ isExtensionContext: isExtensionContext$1,
|
||
|
+ Array: Array$2,
|
||
|
+ Number: Number$1,
|
||
|
+ String: String$1,
|
||
|
+ Object: Object$2
|
||
|
+ } = env;
|
||
|
+
|
||
|
+ const {isArray} = Array$2;
|
||
|
+ const {getOwnPropertyDescriptor, setPrototypeOf: setPrototypeOf$1} = Object$2;
|
||
|
+
|
||
|
+ const {toString} = Object$2.prototype;
|
||
|
+ const {slice} = String$1.prototype;
|
||
|
+ const getBrand = value => call(slice, call(toString, value), 8, -1);
|
||
|
+
|
||
|
+ const {get: nodeType} = getOwnPropertyDescriptor(Node.prototype, "nodeType");
|
||
|
+
|
||
|
+ const chained = isExtensionContext$1 ? {} : {
|
||
|
+ Attr: chain(Attr),
|
||
|
+ CanvasRenderingContext2D: chain(CanvasRenderingContext2D),
|
||
|
+ CSSStyleDeclaration: chain(CSSStyleDeclaration),
|
||
|
+ Document: chain(Document),
|
||
|
+ Element: chain(Element),
|
||
|
+ HTMLCanvasElement: chain(HTMLCanvasElement),
|
||
|
+ HTMLElement: chain(HTMLElement),
|
||
|
+ HTMLImageElement: chain(HTMLImageElement),
|
||
|
+ HTMLScriptElement: chain(HTMLScriptElement),
|
||
|
+ MutationRecord: chain(MutationRecord),
|
||
|
+ Node: chain(Node),
|
||
|
+ ShadowRoot: chain(ShadowRoot),
|
||
|
+
|
||
|
+ get CSS2Properties() {
|
||
|
+ return chained.CSSStyleDeclaration;
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const upgrade = (value, hint) => {
|
||
|
+ if (hint !== "Element" && hint in chained)
|
||
|
+ return chained[hint](value);
|
||
|
+
|
||
|
+ if (isArray(value))
|
||
|
+ return setPrototypeOf$1(value, Array$2.prototype);
|
||
|
+
|
||
|
+ const brand = getBrand(value);
|
||
|
+ if (brand in chained)
|
||
|
+ return chained[brand](value);
|
||
|
+
|
||
|
+ if (brand in env)
|
||
|
+ return setPrototypeOf$1(value, env[brand].prototype);
|
||
|
+
|
||
|
+ if ("nodeType" in value) {
|
||
|
+ switch (call(nodeType, value)) {
|
||
|
+ case 1:
|
||
|
+ if (!(hint in chained))
|
||
|
+ throw new Error("unknown hint " + hint);
|
||
|
+ return chained[hint](value);
|
||
|
+ case 2:
|
||
|
+ return chained.Attr(value);
|
||
|
+ case 3:
|
||
|
+ return chained.Node(value);
|
||
|
+ case 9:
|
||
|
+ return chained.Document(value);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ throw new Error("unknown brand " + brand);
|
||
|
+ };
|
||
|
+
|
||
|
+ var $ = isExtensionContext$1 ?
|
||
|
+ value => (value === window || value === globalThis ? env : value) :
|
||
|
+ transformer((value, hint = "Element") => {
|
||
|
+ if (value === window || value === globalThis)
|
||
|
+ return env;
|
||
|
+
|
||
|
+ switch (typeof value) {
|
||
|
+ case "object":
|
||
|
+ return value && upgrade(value, hint);
|
||
|
+
|
||
|
+ case "string":
|
||
|
+ return new String$1(value);
|
||
|
+
|
||
|
+ case "number":
|
||
|
+ return new Number$1(value);
|
||
|
+
|
||
|
+ default:
|
||
|
+ throw new Error("unsupported value");
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ let {
|
||
|
+ document: document$1,
|
||
|
+ getComputedStyle: getComputedStyle$5,
|
||
|
+ isExtensionContext,
|
||
|
+ variables,
|
||
|
+ Array: Array$1,
|
||
|
+ MutationObserver: MutationObserver$9,
|
||
|
+ Object: Object$1,
|
||
|
+ XPathEvaluator,
|
||
|
+ XPathExpression,
|
||
|
+ XPathResult
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ let {querySelectorAll} = document$1;
|
||
|
+ let $$ = querySelectorAll && bind(querySelectorAll, document$1);
|
||
|
+
|
||
|
+ const {assign, setPrototypeOf} = Object$1;
|
||
|
+
|
||
|
+ class $XPathExpression extends XPathExpression {
|
||
|
+ evaluate(...args) {
|
||
|
+ return setPrototypeOf(
|
||
|
+ apply$2(super.evaluate, this, args),
|
||
|
+ XPathResult.prototype
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ class $XPathEvaluator extends XPathEvaluator {
|
||
|
+ createExpression(...args) {
|
||
|
+ return setPrototypeOf(
|
||
|
+ apply$2(super.createExpression, this, args),
|
||
|
+ $XPathExpression.prototype
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function hideElement(element) {
|
||
|
+ if (variables.hidden.has(element))
|
||
|
+ return;
|
||
|
+
|
||
|
+ notifyElementHidden(element);
|
||
|
+
|
||
|
+ variables.hidden.add(element);
|
||
|
+
|
||
|
+ let {style} = $(element);
|
||
|
+ let $style = $(style, "CSSStyleDeclaration");
|
||
|
+ let properties = $([]);
|
||
|
+ let {debugCSSProperties} = libEnvironment;
|
||
|
+
|
||
|
+ for (let [key, value] of (debugCSSProperties || [["display", "none"]])) {
|
||
|
+ $style.setProperty(key, value, "important");
|
||
|
+ properties.push([key, $style.getPropertyValue(key)]);
|
||
|
+ }
|
||
|
+
|
||
|
+ new MutationObserver$9(() => {
|
||
|
+ for (let [key, value] of properties) {
|
||
|
+ let propertyValue = $style.getPropertyValue(key);
|
||
|
+ let propertyPriority = $style.getPropertyPriority(key);
|
||
|
+ if (propertyValue != value || propertyPriority != "important")
|
||
|
+ $style.setProperty(key, value, "important");
|
||
|
+ }
|
||
|
+ }).observe(element, {attributes: true,
|
||
|
+ attributeFilter: ["style"]});
|
||
|
+ }
|
||
|
+
|
||
|
+ function notifyElementHidden(element) {
|
||
|
+ if (isExtensionContext && typeof checkElement === "function")
|
||
|
+ checkElement(element);
|
||
|
+ }
|
||
|
+
|
||
|
+ function initQueryAndApply(selector) {
|
||
|
+ let $selector = selector;
|
||
|
+ if ($selector.startsWith("xpath(") &&
|
||
|
+ $selector.endsWith(")")) {
|
||
|
+ let xpathQuery = $selector.slice(6, -1);
|
||
|
+ let evaluator = new $XPathEvaluator();
|
||
|
+ let expression = evaluator.createExpression(xpathQuery, null);
|
||
|
+
|
||
|
+ let flag = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
|
||
|
+
|
||
|
+ return cb => {
|
||
|
+ if (!cb)
|
||
|
+ return;
|
||
|
+ let result = expression.evaluate(document$1, flag, null);
|
||
|
+ let {snapshotLength} = result;
|
||
|
+ for (let i = 0; i < snapshotLength; i++)
|
||
|
+ cb(result.snapshotItem(i));
|
||
|
+ };
|
||
|
+ }
|
||
|
+ return cb => $$(selector).forEach(cb);
|
||
|
+ }
|
||
|
+
|
||
|
+ function initQueryAll(selector) {
|
||
|
+ let $selector = selector;
|
||
|
+ if ($selector.startsWith("xpath(") &&
|
||
|
+ $selector.endsWith(")")) {
|
||
|
+ let queryAndApply = initQueryAndApply(selector);
|
||
|
+ return () => {
|
||
|
+ let elements = $([]);
|
||
|
+ queryAndApply(e => elements.push(e));
|
||
|
+ return elements;
|
||
|
+ };
|
||
|
+ }
|
||
|
+ return () => Array$1.from($$(selector));
|
||
|
+ }
|
||
|
+
|
||
|
+ function hideIfMatches(match, selector, searchSelector, onHideCallback) {
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ let won;
|
||
|
+ const callback = () => {
|
||
|
+ for (const element of $$(searchSelector)) {
|
||
|
+ const closest = $(element).closest(selector);
|
||
|
+ if (closest && match(element, closest)) {
|
||
|
+ won();
|
||
|
+ hideElement(closest);
|
||
|
+ if (typeof onHideCallback === "function")
|
||
|
+ onHideCallback(closest);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+ return assign(
|
||
|
+ new MutationObserver$9(callback),
|
||
|
+ {
|
||
|
+ race(win) {
|
||
|
+ won = win;
|
||
|
+ this.observe(document$1, {childList: true,
|
||
|
+ characterData: true,
|
||
|
+ subtree: true});
|
||
|
+ callback();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ );
|
||
|
+ }
|
||
|
+
|
||
|
+ function isVisible(element, style, closest) {
|
||
|
+ let $style = $(style, "CSSStyleDeclaration");
|
||
|
+ if ($style.getPropertyValue("display") == "none")
|
||
|
+ return false;
|
||
|
+
|
||
|
+ let visibility = $style.getPropertyValue("visibility");
|
||
|
+ if (visibility == "hidden" || visibility == "collapse")
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if (!closest || element == closest)
|
||
|
+ return true;
|
||
|
+
|
||
|
+ let parent = $(element).parentElement;
|
||
|
+ if (!parent)
|
||
|
+ return true;
|
||
|
+
|
||
|
+ return isVisible(parent, getComputedStyle$5(parent), closest);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getComputedCSSText(element) {
|
||
|
+ let style = getComputedStyle$5(element);
|
||
|
+ let {cssText} = style;
|
||
|
+
|
||
|
+ if (cssText)
|
||
|
+ return cssText;
|
||
|
+
|
||
|
+ for (let property of style)
|
||
|
+ cssText += `${property}: ${style[property]}; `;
|
||
|
+
|
||
|
+ return $(cssText).trim();
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Math: Math$2, RegExp} = $(window);
|
||
|
+
|
||
|
+ function regexEscape(string) {
|
||
|
+ return $(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||
|
+ }
|
||
|
+
|
||
|
+ function toRegExp(pattern) {
|
||
|
+ let {length} = pattern;
|
||
|
+
|
||
|
+ if (length > 1 && pattern[0] === "/") {
|
||
|
+ let isCaseSensitive = pattern[length - 1] === "/";
|
||
|
+
|
||
|
+ if (isCaseSensitive || (length > 2 && $(pattern).endsWith("/i"))) {
|
||
|
+ let args = [$(pattern).slice(1, isCaseSensitive ? -1 : -2)];
|
||
|
+ if (!isCaseSensitive)
|
||
|
+ args.push("i");
|
||
|
+
|
||
|
+ return new RegExp(...args);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return new RegExp(regexEscape(pattern));
|
||
|
+ }
|
||
|
+
|
||
|
+ let debugging = false;
|
||
|
+
|
||
|
+ function debug() {
|
||
|
+ return debugging;
|
||
|
+ }
|
||
|
+
|
||
|
+ function setDebug() {
|
||
|
+ debugging = true;
|
||
|
+ }
|
||
|
+
|
||
|
+ const {console: console$1} = $(window);
|
||
|
+
|
||
|
+ const noop = () => {};
|
||
|
+
|
||
|
+ function log(...args) {
|
||
|
+ if (debug())
|
||
|
+ $(args).unshift("%c DEBUG", "font-weight: bold");
|
||
|
+
|
||
|
+ console$1.log(...args);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getDebugger(name) {
|
||
|
+ return bind(debug() ? log : noop, null, name);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Array, Error: Error$3, Map: Map$2, parseInt: parseInt$2} = $(window);
|
||
|
+
|
||
|
+ let stack = null;
|
||
|
+ let won = null;
|
||
|
+
|
||
|
+ function race(action, winners = "1") {
|
||
|
+ switch (action) {
|
||
|
+ case "start":
|
||
|
+ stack = {
|
||
|
+ winners: parseInt$2(winners, 10) || 1,
|
||
|
+ participants: new Map$2()
|
||
|
+ };
|
||
|
+ won = new Array();
|
||
|
+ break;
|
||
|
+ case "end":
|
||
|
+ case "finish":
|
||
|
+ case "stop":
|
||
|
+ stack = null;
|
||
|
+ for (let win of won)
|
||
|
+ win();
|
||
|
+ won = null;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ throw new Error$3(`Invalid action: ${action}`);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function raceWinner(name, lose) {
|
||
|
+
|
||
|
+ if (stack === null)
|
||
|
+ return noop;
|
||
|
+
|
||
|
+ let current = stack;
|
||
|
+ let {participants} = current;
|
||
|
+ participants.set(win, lose);
|
||
|
+
|
||
|
+ return win;
|
||
|
+
|
||
|
+ function win() {
|
||
|
+
|
||
|
+ if (current.winners < 1)
|
||
|
+ return;
|
||
|
+
|
||
|
+ let debugLog = getDebugger("race");
|
||
|
+ debugLog(`${name} won the race`);
|
||
|
+
|
||
|
+ if (current === stack) {
|
||
|
+ won.push(win);
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ participants.delete(win);
|
||
|
+ if (--current.winners < 1) {
|
||
|
+ for (let looser of participants.values())
|
||
|
+ looser();
|
||
|
+
|
||
|
+ participants.clear();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function hideIfContains(search, selector = "*", searchSelector = null) {
|
||
|
+ const debugLog = getDebugger("hide-if-contains");
|
||
|
+ const onHideCallback = node => {
|
||
|
+ debugLog("Matched: ", node, " for selector: ", selector, searchSelector);
|
||
|
+ };
|
||
|
+ let re = toRegExp(search);
|
||
|
+
|
||
|
+ const mo = hideIfMatches(element => re.test($(element).textContent),
|
||
|
+ selector,
|
||
|
+ searchSelector,
|
||
|
+ onHideCallback);
|
||
|
+ mo.race(raceWinner(
|
||
|
+ "hide-if-contains",
|
||
|
+ () => {
|
||
|
+ mo.disconnect();
|
||
|
+ }
|
||
|
+ ));
|
||
|
+ }
|
||
|
+
|
||
|
+ const handler = {
|
||
|
+ get(target, name) {
|
||
|
+ const context = target;
|
||
|
+ while (!hasOwnProperty(target, name))
|
||
|
+ target = getPrototypeOf(target);
|
||
|
+ const {get, set} = getOwnPropertyDescriptor$2(target, name);
|
||
|
+ return function () {
|
||
|
+ return arguments.length ?
|
||
|
+ apply$2(set, context, arguments) :
|
||
|
+ call(get, context);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const accessor = target => new $$1(target, handler);
|
||
|
+
|
||
|
+ $(window);
|
||
|
+
|
||
|
+ accessor(window);
|
||
|
+
|
||
|
+ $(/^\d+$/);
|
||
|
+
|
||
|
+ function getPromiseFromEvent(item, event) {
|
||
|
+ return new Promise(
|
||
|
+ resolve => {
|
||
|
+ const listener = () => {
|
||
|
+ item.removeEventListener(event, listener);
|
||
|
+ resolve();
|
||
|
+ };
|
||
|
+ item.addEventListener(event, listener);
|
||
|
+ }
|
||
|
+ );
|
||
|
+ }
|
||
|
+
|
||
|
+ function waitUntilEvent(
|
||
|
+ debugLog,
|
||
|
+ mainLogic,
|
||
|
+ waitUntil) {
|
||
|
+ if (waitUntil) {
|
||
|
+
|
||
|
+ if (waitUntil === "load") {
|
||
|
+ debugLog("Waiting until window.load");
|
||
|
+
|
||
|
+ window.onload = () => {
|
||
|
+ debugLog("Window.load fired.");
|
||
|
+ mainLogic();
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ else if (waitUntil === "loading" ||
|
||
|
+ waitUntil === "interactive" ||
|
||
|
+ waitUntil === "complete") {
|
||
|
+ debugLog("Waiting document state until :", waitUntil);
|
||
|
+
|
||
|
+ document.onreadystatechange = () => {
|
||
|
+ debugLog("Document state changed:", document.readyState);
|
||
|
+ if (document.readyState === waitUntil)
|
||
|
+ mainLogic();
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ else {
|
||
|
+ debugLog("Waiting until ", waitUntil, " event is triggered on document");
|
||
|
+ getPromiseFromEvent(document, waitUntil).then(() => {
|
||
|
+ debugLog(waitUntil, " is triggered on document, starting the snippet");
|
||
|
+ mainLogic();
|
||
|
+ }).catch(err => {
|
||
|
+ debugLog("There was an error while waiting for the event.", err);
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+
|
||
|
+ mainLogic();
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {MutationObserver: MutationObserver$8, WeakSet: WeakSet$9, getComputedStyle: getComputedStyle$4} = $(window);
|
||
|
+
|
||
|
+ function hideIfContainsAndMatchesStyle(search,
|
||
|
+ selector = "*",
|
||
|
+ searchSelector = null,
|
||
|
+ style = null,
|
||
|
+ searchStyle = null,
|
||
|
+ waitUntil,
|
||
|
+ windowWidthMin = null,
|
||
|
+ windowWidthMax = null
|
||
|
+ ) {
|
||
|
+ const debugLog = getDebugger("hide-if-contains-and-matches-style");
|
||
|
+ const hiddenMap = new WeakSet$9();
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ const searchRegExp = toRegExp(search);
|
||
|
+
|
||
|
+ const styleRegExp = style ? toRegExp(style) : null;
|
||
|
+ const searchStyleRegExp = searchStyle ? toRegExp(searchStyle) : null;
|
||
|
+ const mainLogic = () => {
|
||
|
+ const callback = () => {
|
||
|
+ if ((windowWidthMin && window.innerWidth < windowWidthMin) ||
|
||
|
+ (windowWidthMax && window.innerWidth > windowWidthMax)
|
||
|
+ )
|
||
|
+ return;
|
||
|
+ for (const element of $$(searchSelector)) {
|
||
|
+ if (hiddenMap.has(element))
|
||
|
+ continue;
|
||
|
+ if (searchRegExp.test($(element).textContent)) {
|
||
|
+ if (!searchStyleRegExp ||
|
||
|
+ searchStyleRegExp.test(getComputedCSSText(element))) {
|
||
|
+ const closest = $(element).closest(selector);
|
||
|
+ if (!closest)
|
||
|
+ continue;
|
||
|
+ if (!styleRegExp || styleRegExp.test(getComputedCSSText(closest))) {
|
||
|
+ win();
|
||
|
+ hideElement(closest);
|
||
|
+ hiddenMap.add(element);
|
||
|
+ debugLog("Matched: ",
|
||
|
+ closest,
|
||
|
+ "which contains: ",
|
||
|
+ element,
|
||
|
+ " for params: ",
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ debugLog(
|
||
|
+ "In this element the searchStyle matched but style didn't:\n",
|
||
|
+ closest,
|
||
|
+ getComputedStyle$4(closest),
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ debugLog(
|
||
|
+ "In this element the searchStyle didn't match:\n",
|
||
|
+ element,
|
||
|
+ getComputedStyle$4(element),
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const mo = new MutationObserver$8(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-contains-and-matches-style",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {childList: true, characterData: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ clearTimeout,
|
||
|
+ fetch,
|
||
|
+ getComputedStyle: getComputedStyle$3,
|
||
|
+ setTimeout: setTimeout$2,
|
||
|
+ Map: Map$1,
|
||
|
+ MutationObserver: MutationObserver$7,
|
||
|
+ Uint8Array
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ function hideIfContainsImage(search, selector, searchSelector) {
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ let searchRegExp = toRegExp(search);
|
||
|
+
|
||
|
+ const debugLog = getDebugger("hide-if-contains-image");
|
||
|
+
|
||
|
+ let callback = () => {
|
||
|
+ for (let element of $$(searchSelector)) {
|
||
|
+ let style = getComputedStyle$3(element);
|
||
|
+ let match = $(style["background-image"]).match(/^url\("(.*)"\)$/);
|
||
|
+ if (match) {
|
||
|
+ fetchContent(match[1]).then(content => {
|
||
|
+ if (searchRegExp.test(uint8ArrayToHex(new Uint8Array(content)))) {
|
||
|
+ let closest = $(element).closest(selector);
|
||
|
+ if (closest) {
|
||
|
+ win();
|
||
|
+ hideElement(closest);
|
||
|
+ debugLog("Matched: ", closest, " for:", ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ let mo = new MutationObserver$7(callback);
|
||
|
+ let win = raceWinner(
|
||
|
+ "hide-if-contains-image",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ }
|
||
|
+
|
||
|
+ let fetchContentMap = new Map$1();
|
||
|
+
|
||
|
+ function fetchContent(url, {as = "arrayBuffer", cleanup = 60000} = {}) {
|
||
|
+
|
||
|
+ let uid = as + ":" + url;
|
||
|
+ let details = fetchContentMap.get(uid) || {
|
||
|
+ remove: () => fetchContentMap.delete(uid),
|
||
|
+ result: null,
|
||
|
+ timer: 0
|
||
|
+ };
|
||
|
+ clearTimeout(details.timer);
|
||
|
+ details.timer = setTimeout$2(details.remove, cleanup);
|
||
|
+ if (!details.result) {
|
||
|
+ details.result = fetch(url).then(res => res[as]()).catch(details.remove);
|
||
|
+ fetchContentMap.set(uid, details);
|
||
|
+ }
|
||
|
+ return details.result;
|
||
|
+ }
|
||
|
+
|
||
|
+ function toHex(number, length = 2) {
|
||
|
+ let hex = $(number).toString(16);
|
||
|
+
|
||
|
+ if (hex.length < length)
|
||
|
+ hex = $("0").repeat(length - hex.length) + hex;
|
||
|
+
|
||
|
+ return hex;
|
||
|
+ }
|
||
|
+
|
||
|
+ function uint8ArrayToHex(uint8Array) {
|
||
|
+ return uint8Array.reduce((hex, byte) => hex + toHex(byte), "");
|
||
|
+ }
|
||
|
+
|
||
|
+ const {parseFloat: parseFloat$1, Math: Math$1, MutationObserver: MutationObserver$6, WeakSet: WeakSet$8} = $(window);
|
||
|
+ const {min} = Math$1;
|
||
|
+
|
||
|
+ const ld = (a, b) => {
|
||
|
+ const len1 = a.length + 1;
|
||
|
+ const len2 = b.length + 1;
|
||
|
+ const d = [[0]];
|
||
|
+ let i = 0;
|
||
|
+ let I = 0;
|
||
|
+
|
||
|
+ while (++i < len2)
|
||
|
+ d[0][i] = i;
|
||
|
+
|
||
|
+ i = 0;
|
||
|
+ while (++i < len1) {
|
||
|
+ const c = a[I];
|
||
|
+ let j = 0;
|
||
|
+ let J = 0;
|
||
|
+ d[i] = [i];
|
||
|
+ while (++j < len2) {
|
||
|
+ d[i][j] = min(d[I][j] + 1, d[i][J] + 1, d[I][J] + (c != b[J]));
|
||
|
+ ++J;
|
||
|
+ }
|
||
|
+ ++I;
|
||
|
+ }
|
||
|
+ return d[len1 - 1][len2 - 1];
|
||
|
+ };
|
||
|
+
|
||
|
+ function hideIfContainsSimilarText(
|
||
|
+ search, selector,
|
||
|
+ searchSelector = null,
|
||
|
+ ignoreChars = 0,
|
||
|
+ maxSearches = 0
|
||
|
+ ) {
|
||
|
+ const visitedNodes = new WeakSet$8();
|
||
|
+ const debugLog = getDebugger("hide-if-contains-similar-text");
|
||
|
+ const $search = $(search);
|
||
|
+ const {length} = $search;
|
||
|
+ const chars = length + parseFloat$1(ignoreChars) || 0;
|
||
|
+ const find = $([...$search]).sort();
|
||
|
+ const guard = parseFloat$1(maxSearches) || Infinity;
|
||
|
+
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ debugLog("Looking for similar text: " + $search);
|
||
|
+
|
||
|
+ const callback = () => {
|
||
|
+ for (const element of $$(searchSelector)) {
|
||
|
+ if (visitedNodes.has(element))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ visitedNodes.add(element);
|
||
|
+ const {innerText} = $(element);
|
||
|
+ const loop = min(guard, innerText.length - chars + 1);
|
||
|
+ for (let i = 0; i < loop; i++) {
|
||
|
+ const str = $(innerText).substr(i, chars);
|
||
|
+ const distance = ld(find, $([...str]).sort()) - ignoreChars;
|
||
|
+ if (distance <= 0) {
|
||
|
+ const closest = $(element).closest(selector);
|
||
|
+ debugLog("Found similar text: " + $search, closest);
|
||
|
+ if (closest) {
|
||
|
+ win();
|
||
|
+ hideElement(closest);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ let mo = new MutationObserver$6(callback);
|
||
|
+ let win = raceWinner(
|
||
|
+ "hide-if-contains-similar-text",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {childList: true, characterData: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ }
|
||
|
+
|
||
|
+ let {getComputedStyle: getComputedStyle$2, Map, WeakSet: WeakSet$7, parseFloat} = $(window);
|
||
|
+
|
||
|
+ const {ELEMENT_NODE: ELEMENT_NODE$2, TEXT_NODE} = Node;
|
||
|
+
|
||
|
+ function hideIfContainsVisibleText(search, selector,
|
||
|
+ searchSelector = null,
|
||
|
+ ...attributes) {
|
||
|
+ let entries = $([]);
|
||
|
+ const optionalParameters = new Map([
|
||
|
+ ["-snippet-box-margin", "2"],
|
||
|
+ ["-disable-bg-color-check", "false"],
|
||
|
+ ["-check-is-contained", "false"]
|
||
|
+ ]);
|
||
|
+
|
||
|
+ for (let attr of attributes) {
|
||
|
+ attr = $(attr);
|
||
|
+ let markerIndex = attr.indexOf(":");
|
||
|
+ if (markerIndex < 0)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ let key = attr.slice(0, markerIndex).trim().toString();
|
||
|
+ let value = attr.slice(markerIndex + 1).trim().toString();
|
||
|
+
|
||
|
+ if (key && value) {
|
||
|
+ if (optionalParameters.has(key))
|
||
|
+ optionalParameters.set(key, value);
|
||
|
+ else
|
||
|
+ entries.push([key, value]);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let defaultEntries = $([
|
||
|
+ ["opacity", "0"],
|
||
|
+ ["font-size", "0px"],
|
||
|
+
|
||
|
+ ["color", "rgba(0, 0, 0, 0)"]
|
||
|
+ ]);
|
||
|
+
|
||
|
+ let attributesMap = new Map(defaultEntries.concat(entries));
|
||
|
+
|
||
|
+ function isTextVisible(element, style, {bgColorCheck = true} = {}) {
|
||
|
+ if (!style)
|
||
|
+ style = getComputedStyle$2(element);
|
||
|
+
|
||
|
+ style = $(style);
|
||
|
+
|
||
|
+ for (const [key, value] of attributesMap) {
|
||
|
+ let valueAsRegex = toRegExp(value);
|
||
|
+ if (valueAsRegex.test(style.getPropertyValue(key)))
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ let color = style.getPropertyValue("color");
|
||
|
+ if (bgColorCheck && style.getPropertyValue("background-color") == color)
|
||
|
+ return false;
|
||
|
+
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ function getPseudoContent(element, pseudo, {bgColorCheck = true} = {}) {
|
||
|
+ let style = getComputedStyle$2(element, pseudo);
|
||
|
+ if (!isVisible(element, style) ||
|
||
|
+ !isTextVisible(element, style, {bgColorCheck}))
|
||
|
+ return "";
|
||
|
+
|
||
|
+ let {content} = $(style);
|
||
|
+ if (content && content !== "none") {
|
||
|
+ let strings = $([]);
|
||
|
+
|
||
|
+ content = $(content).trim().replace(
|
||
|
+ /(["'])(?:(?=(\\?))\2.)*?\1/g,
|
||
|
+ value => `\x01${strings.push($(value).slice(1, -1)) - 1}`
|
||
|
+ );
|
||
|
+
|
||
|
+ content = content.replace(
|
||
|
+ /\s*attr\(\s*([^\s,)]+)[^)]*?\)\s*/g,
|
||
|
+ (_, name) => $(element).getAttribute(name) || ""
|
||
|
+ );
|
||
|
+
|
||
|
+ return content.replace(
|
||
|
+ /\x01(\d+)/g,
|
||
|
+ (_, index) => strings[index]);
|
||
|
+ }
|
||
|
+ return "";
|
||
|
+ }
|
||
|
+
|
||
|
+ function isContained(childNode, parentNode, {boxMargin = 2} = {}) {
|
||
|
+ const child = $(childNode).getBoundingClientRect();
|
||
|
+ const parent = $(parentNode).getBoundingClientRect();
|
||
|
+ const stretchedParent = {
|
||
|
+ left: parent.left - boxMargin,
|
||
|
+ right: parent.right + boxMargin,
|
||
|
+ top: parent.top - boxMargin,
|
||
|
+ bottom: parent.bottom + boxMargin
|
||
|
+ };
|
||
|
+ return (
|
||
|
+ (stretchedParent.left <= child.left &&
|
||
|
+ child.left <= stretchedParent.right &&
|
||
|
+ stretchedParent.top <= child.top &&
|
||
|
+ child.top <= stretchedParent.bottom) &&
|
||
|
+ (stretchedParent.top <= child.bottom &&
|
||
|
+ child.bottom <= stretchedParent.bottom &&
|
||
|
+ stretchedParent.left <= child.right &&
|
||
|
+ child.right <= stretchedParent.right)
|
||
|
+ );
|
||
|
+ }
|
||
|
+
|
||
|
+ function getVisibleContent(element,
|
||
|
+ closest,
|
||
|
+ style,
|
||
|
+ parentOverflowNode,
|
||
|
+ originalElement,
|
||
|
+ {
|
||
|
+ boxMargin = 2,
|
||
|
+ bgColorCheck,
|
||
|
+ checkIsContained
|
||
|
+ } = {}) {
|
||
|
+ let checkClosest = !style;
|
||
|
+ if (checkClosest)
|
||
|
+ style = getComputedStyle$2(element);
|
||
|
+
|
||
|
+ if (!isVisible(element, style, checkClosest && closest))
|
||
|
+ return "";
|
||
|
+
|
||
|
+ if (!parentOverflowNode &&
|
||
|
+ (
|
||
|
+ $(style).getPropertyValue("overflow-x") === "hidden" ||
|
||
|
+ $(style).getPropertyValue("overflow-y") === "hidden"
|
||
|
+ )
|
||
|
+ )
|
||
|
+ parentOverflowNode = element;
|
||
|
+
|
||
|
+ let text = getPseudoContent(element, ":before", {bgColorCheck});
|
||
|
+ for (let node of $(element).childNodes) {
|
||
|
+ switch ($(node).nodeType) {
|
||
|
+ case ELEMENT_NODE$2:
|
||
|
+ text += getVisibleContent(node,
|
||
|
+ element,
|
||
|
+ getComputedStyle$2(node),
|
||
|
+ parentOverflowNode,
|
||
|
+ originalElement,
|
||
|
+ {
|
||
|
+ boxMargin,
|
||
|
+ bgColorCheck,
|
||
|
+ checkIsContained
|
||
|
+ }
|
||
|
+ );
|
||
|
+ break;
|
||
|
+ case TEXT_NODE:
|
||
|
+
|
||
|
+ if (parentOverflowNode) {
|
||
|
+ if (isContained(element, parentOverflowNode, {boxMargin}) &&
|
||
|
+ isTextVisible(element, style, {bgColorCheck}))
|
||
|
+ text += $(node).nodeValue;
|
||
|
+ }
|
||
|
+ else if (isTextVisible(element, style, {bgColorCheck})) {
|
||
|
+ if (checkIsContained &&
|
||
|
+ !isContained(element, originalElement, {boxMargin}))
|
||
|
+ continue;
|
||
|
+ text += $(node).nodeValue;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return text + getPseudoContent(element, ":after", {bgColorCheck});
|
||
|
+ }
|
||
|
+ const boxMarginStr = optionalParameters.get("-snippet-box-margin");
|
||
|
+ const boxMargin = parseFloat(boxMarginStr) || 0;
|
||
|
+
|
||
|
+ const bgColorCheckStr = optionalParameters.get("-disable-bg-color-check");
|
||
|
+ const bgColorCheck = !(bgColorCheckStr === "true");
|
||
|
+
|
||
|
+ const checkIsContainedStr = optionalParameters.get("-check-is-contained");
|
||
|
+ const checkIsContained = (checkIsContainedStr === "true");
|
||
|
+
|
||
|
+ let re = toRegExp(search);
|
||
|
+ let seen = new WeakSet$7();
|
||
|
+
|
||
|
+ const mo = hideIfMatches(
|
||
|
+ (element, closest) => {
|
||
|
+ if (seen.has(element))
|
||
|
+ return false;
|
||
|
+
|
||
|
+ seen.add(element);
|
||
|
+ let text = getVisibleContent(element, closest, null, null, element, {
|
||
|
+ boxMargin,
|
||
|
+ bgColorCheck,
|
||
|
+ checkIsContained
|
||
|
+ }
|
||
|
+ );
|
||
|
+ let result = re.test(text);
|
||
|
+ if (debug() && text.length)
|
||
|
+ log(result, re, text);
|
||
|
+ return result;
|
||
|
+ },
|
||
|
+ selector,
|
||
|
+ searchSelector
|
||
|
+ );
|
||
|
+ mo.race(raceWinner(
|
||
|
+ "hide-if-contains-visible-text",
|
||
|
+ () => {
|
||
|
+ mo.disconnect();
|
||
|
+ }
|
||
|
+ ));
|
||
|
+ }
|
||
|
+
|
||
|
+ let {MutationObserver: MutationObserver$5, WeakSet: WeakSet$6, getComputedStyle: getComputedStyle$1} = $(window);
|
||
|
+
|
||
|
+ function hideIfHasAndMatchesStyle(search,
|
||
|
+ selector = "*",
|
||
|
+ searchSelector = null,
|
||
|
+ style = null,
|
||
|
+ searchStyle = null,
|
||
|
+ waitUntil = null,
|
||
|
+ windowWidthMin = null,
|
||
|
+ windowWidthMax = null
|
||
|
+ ) {
|
||
|
+ const debugLog = getDebugger("hide-if-has-and-matches-style");
|
||
|
+ const hiddenMap = new WeakSet$6();
|
||
|
+ if (searchSelector == null)
|
||
|
+ searchSelector = selector;
|
||
|
+
|
||
|
+ const styleRegExp = style ? toRegExp(style) : null;
|
||
|
+ const searchStyleRegExp = searchStyle ? toRegExp(searchStyle) : null;
|
||
|
+ const mainLogic = () => {
|
||
|
+ const callback = () => {
|
||
|
+ if ((windowWidthMin && window.innerWidth < windowWidthMin) ||
|
||
|
+ (windowWidthMax && window.innerWidth > windowWidthMax)
|
||
|
+ )
|
||
|
+ return;
|
||
|
+ for (const element of $$(searchSelector)) {
|
||
|
+ if (hiddenMap.has(element))
|
||
|
+ continue;
|
||
|
+ if ($(element).querySelector(search) &&
|
||
|
+ (!searchStyleRegExp ||
|
||
|
+ searchStyleRegExp.test(getComputedCSSText(element)))) {
|
||
|
+ const closest = $(element).closest(selector);
|
||
|
+ if (closest && (!styleRegExp ||
|
||
|
+ styleRegExp.test(getComputedCSSText(closest)))) {
|
||
|
+ win();
|
||
|
+ hideElement(closest);
|
||
|
+ hiddenMap.add(element);
|
||
|
+ debugLog("Matched: ",
|
||
|
+ closest,
|
||
|
+ "which contains: ",
|
||
|
+ element,
|
||
|
+ " for params: ",
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ debugLog(
|
||
|
+ "In this element the searchStyle matched but style didn't:\n",
|
||
|
+ closest,
|
||
|
+ getComputedStyle$1(closest),
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ debugLog(
|
||
|
+ "In this element the searchStyle didn't match:\n",
|
||
|
+ element,
|
||
|
+ getComputedStyle$1(element),
|
||
|
+ ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const mo = new MutationObserver$5(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-has-and-matches-style",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {getComputedStyle, MutationObserver: MutationObserver$4, WeakSet: WeakSet$5} = $(window);
|
||
|
+
|
||
|
+ function hideIfLabelledBy(search, selector, searchSelector = null) {
|
||
|
+ let sameSelector = searchSelector == null;
|
||
|
+
|
||
|
+ let searchRegExp = toRegExp(search);
|
||
|
+
|
||
|
+ let matched = new WeakSet$5();
|
||
|
+
|
||
|
+ let callback = () => {
|
||
|
+ for (let node of $$(selector)) {
|
||
|
+ let closest = sameSelector ?
|
||
|
+ node :
|
||
|
+ $(node).closest(searchSelector);
|
||
|
+ if (!closest || !isVisible(node, getComputedStyle(node), closest))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ let attr = $(node).getAttribute("aria-labelledby");
|
||
|
+ let fallback = () => {
|
||
|
+ if (matched.has(closest))
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (searchRegExp.test(
|
||
|
+ $(node).getAttribute("aria-label") || ""
|
||
|
+ )) {
|
||
|
+ win();
|
||
|
+ matched.add(closest);
|
||
|
+ hideElement(closest);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ if (attr) {
|
||
|
+ for (let label of $(attr).split(/\s+/)) {
|
||
|
+ let target = $(document).getElementById(label);
|
||
|
+ if (target) {
|
||
|
+ if (!matched.has(target) && searchRegExp.test(target.innerText)) {
|
||
|
+ win();
|
||
|
+ matched.add(target);
|
||
|
+ hideElement(closest);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ fallback();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ fallback();
|
||
|
+ }
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ let mo = new MutationObserver$4(callback);
|
||
|
+ let win = raceWinner(
|
||
|
+ "hide-if-labelled-by",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(document, {characterData: true, childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ }
|
||
|
+
|
||
|
+ $(window);
|
||
|
+
|
||
|
+ const noopProfile = {
|
||
|
+ mark() {},
|
||
|
+ end() {},
|
||
|
+ toString() {
|
||
|
+ return "{mark(){},end(){}}";
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ function profile(id, rate = 10) {
|
||
|
+ return noopProfile;
|
||
|
+ }
|
||
|
+
|
||
|
+ let {MutationObserver: MutationObserver$3, WeakSet: WeakSet$4} = $(window);
|
||
|
+
|
||
|
+ const {ELEMENT_NODE: ELEMENT_NODE$1} = Node;
|
||
|
+
|
||
|
+ function hideIfMatchesXPath(query, scopeQuery) {
|
||
|
+ const {mark, end} = profile();
|
||
|
+ const debugLog = getDebugger("hide-if-matches-xpath");
|
||
|
+
|
||
|
+ const startHidingMutationObserver = scopeNode => {
|
||
|
+ const queryAndApply = initQueryAndApply(`xpath(${query})`);
|
||
|
+ const seenMap = new WeakSet$4();
|
||
|
+ const callback = () => {
|
||
|
+ mark();
|
||
|
+ queryAndApply(node => {
|
||
|
+ if (seenMap.has(node))
|
||
|
+ return false;
|
||
|
+ seenMap.add(node);
|
||
|
+ win();
|
||
|
+ if ($(node).nodeType === ELEMENT_NODE$1)
|
||
|
+ hideElement(node);
|
||
|
+ else
|
||
|
+ $(node).textContent = "";
|
||
|
+ debugLog("Matched: ", node, " for selector: ", query);
|
||
|
+ });
|
||
|
+ end();
|
||
|
+ };
|
||
|
+ const mo = new MutationObserver$3(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-matches-xpath",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(
|
||
|
+ scopeNode, {characterData: true, childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+
|
||
|
+ if (scopeQuery) {
|
||
|
+
|
||
|
+ let count = 0;
|
||
|
+ let scopeMutationObserver;
|
||
|
+ const scopeQueryAndApply = initQueryAndApply(`xpath(${scopeQuery})`);
|
||
|
+ const findMutationScopeNodes = () => {
|
||
|
+ scopeQueryAndApply(scopeNode => {
|
||
|
+
|
||
|
+ startHidingMutationObserver(scopeNode);
|
||
|
+ count++;
|
||
|
+ });
|
||
|
+ if (count > 0)
|
||
|
+ scopeMutationObserver.disconnect();
|
||
|
+ };
|
||
|
+ scopeMutationObserver = new MutationObserver$3(findMutationScopeNodes);
|
||
|
+ scopeMutationObserver.observe(
|
||
|
+ document, {characterData: true, childList: true, subtree: true}
|
||
|
+ );
|
||
|
+ findMutationScopeNodes();
|
||
|
+ }
|
||
|
+ else {
|
||
|
+
|
||
|
+ startHidingMutationObserver(document);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {MutationObserver: MutationObserver$2, WeakSet: WeakSet$3} = $(window);
|
||
|
+
|
||
|
+ const {ELEMENT_NODE} = Node;
|
||
|
+
|
||
|
+ function hideIfMatchesComputedXPath(query, searchQuery, searchRegex,
|
||
|
+ waitUntil) {
|
||
|
+ const {mark, end} = profile();
|
||
|
+ const debugLog = getDebugger("hide-if-matches-computed-xpath");
|
||
|
+
|
||
|
+ if (!searchQuery || !query) {
|
||
|
+ debugLog("No query or searchQuery provided.");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ const computeQuery = foundText => query.replace("{{}}", foundText);
|
||
|
+
|
||
|
+ const startHidingMutationObserver = foundText => {
|
||
|
+ const computedQuery = computeQuery(foundText);
|
||
|
+ debugLog("Starting hiding elements that match query: ", computedQuery);
|
||
|
+ const queryAndApply = initQueryAndApply(`xpath(${computedQuery})`);
|
||
|
+ const seenMap = new WeakSet$3();
|
||
|
+ const callback = () => {
|
||
|
+ mark();
|
||
|
+ queryAndApply(node => {
|
||
|
+ if (seenMap.has(node))
|
||
|
+ return false;
|
||
|
+ seenMap.add(node);
|
||
|
+ win();
|
||
|
+ if ($(node).nodeType === ELEMENT_NODE)
|
||
|
+ hideElement(node);
|
||
|
+ else
|
||
|
+ $(node).textContent = "";
|
||
|
+ debugLog("Matched: ", node, " for selector: ", query);
|
||
|
+ });
|
||
|
+ end();
|
||
|
+ };
|
||
|
+ const mo = new MutationObserver$2(callback);
|
||
|
+ const win = raceWinner(
|
||
|
+ "hide-if-matches-computed-xpath",
|
||
|
+ () => mo.disconnect()
|
||
|
+ );
|
||
|
+ mo.observe(
|
||
|
+ document, {characterData: true, childList: true, subtree: true});
|
||
|
+ callback();
|
||
|
+ };
|
||
|
+
|
||
|
+ const re = toRegExp(searchRegex);
|
||
|
+
|
||
|
+ const mainLogic = () => {
|
||
|
+ if (searchQuery) {
|
||
|
+ debugLog("Started searching for: ", searchQuery);
|
||
|
+ const seenMap = new WeakSet$3();
|
||
|
+ let searchMO;
|
||
|
+ const searchQueryAndApply = initQueryAndApply(`xpath(${searchQuery})`);
|
||
|
+ const findMutationSearchNodes = () => {
|
||
|
+ searchQueryAndApply(searchNode => {
|
||
|
+ if (seenMap.has(searchNode))
|
||
|
+ return false;
|
||
|
+ seenMap.add(searchNode);
|
||
|
+ debugLog("Found node: ", searchNode);
|
||
|
+ if (searchNode.innerHTML) {
|
||
|
+ debugLog("Searching in: ", searchNode.innerHTML);
|
||
|
+ const foundTextArr = searchNode.innerHTML.match(re);
|
||
|
+ if (foundTextArr && foundTextArr.length) {
|
||
|
+ let foundText = "";
|
||
|
+
|
||
|
+ foundTextArr[1] ? foundText = foundTextArr[1] :
|
||
|
+ foundText = foundTextArr[0];
|
||
|
+ debugLog("Matched search query: ", foundText);
|
||
|
+ startHidingMutationObserver(foundText);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+ };
|
||
|
+
|
||
|
+ searchMO = new MutationObserver$2(findMutationSearchNodes);
|
||
|
+ searchMO.observe(
|
||
|
+ document, {characterData: true, childList: true, subtree: true}
|
||
|
+ );
|
||
|
+ findMutationSearchNodes();
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ waitUntilEvent(debugLog, mainLogic, waitUntil);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ parseInt: parseInt$1,
|
||
|
+ setTimeout: setTimeout$1,
|
||
|
+ Error: Error$2,
|
||
|
+ MouseEvent: MouseEvent$2,
|
||
|
+ MutationObserver: MutationObserver$1,
|
||
|
+ WeakSet: WeakSet$2
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ function simulateEvent(event, selector, delay = "0") {
|
||
|
+ if (!event)
|
||
|
+ throw new Error$2("[simulate-event snippet]: No event type provided.");
|
||
|
+ if (!selector)
|
||
|
+ throw new Error$2("[simulate-event snippet]: No selector provided.");
|
||
|
+
|
||
|
+ let queryAndApply = initQueryAndApply(selector);
|
||
|
+ let delayInMiliseconds = parseInt$1(delay, 10);
|
||
|
+ let dispatchedNodes = new WeakSet$2();
|
||
|
+
|
||
|
+ let observer = new MutationObserver$1(findNodesAndDispatchEvents);
|
||
|
+ observer.observe(document, {childList: true, subtree: true});
|
||
|
+ findNodesAndDispatchEvents();
|
||
|
+
|
||
|
+ function findNodesAndDispatchEvents() {
|
||
|
+ queryAndApply(node => {
|
||
|
+ if (!dispatchedNodes.has(node)) {
|
||
|
+ dispatchedNodes.add(node);
|
||
|
+ setTimeout$1(() => {
|
||
|
+ $(node).dispatchEvent(
|
||
|
+ new MouseEvent$2(event, {bubbles: true, cancelable: true})
|
||
|
+ );
|
||
|
+ }, delayInMiliseconds);
|
||
|
+ }
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ parseInt,
|
||
|
+ setTimeout,
|
||
|
+ Error: Error$1,
|
||
|
+ MouseEvent: MouseEvent$1,
|
||
|
+ MutationObserver,
|
||
|
+ WeakSet: WeakSet$1
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ const VALID_TYPES = ["auxclick", "click", "dblclick", "gotpointercapture",
|
||
|
+ "lostpointercapture", "mouseenter", "mousedown",
|
||
|
+ "mouseleave", "mousemove", "mouseout", "mouseover",
|
||
|
+ "mouseup", "pointerdown", "pointerenter",
|
||
|
+ "pointermove", "pointerover", "pointerout",
|
||
|
+ "pointerup", "pointercancel", "pointerleave"];
|
||
|
+
|
||
|
+ function simulateMouseEvent(...selectors) {
|
||
|
+ const debugLog = getDebugger("simulate-mouse-event");
|
||
|
+ const MAX_ARGS = 7;
|
||
|
+ if (selectors.length < 1)
|
||
|
+ throw new Error$1("[simulate-mouse-event snippet]: No selector provided.");
|
||
|
+ if (selectors.length > MAX_ARGS) {
|
||
|
+
|
||
|
+ selectors = selectors.slice(0, MAX_ARGS);
|
||
|
+ }
|
||
|
+ function parseArg(theRule) {
|
||
|
+ if (!theRule)
|
||
|
+ return null;
|
||
|
+
|
||
|
+ const result = {
|
||
|
+ selector: "",
|
||
|
+ continue: false,
|
||
|
+ trigger: false,
|
||
|
+ event: "click",
|
||
|
+ delay: "500",
|
||
|
+ clicked: false,
|
||
|
+ found: false
|
||
|
+ };
|
||
|
+ const textArr = theRule.split("$");
|
||
|
+ let options = [];
|
||
|
+ if (textArr.length >= 2)
|
||
|
+ options = textArr[1].toLowerCase().split(",");
|
||
|
+
|
||
|
+ [result.selector] = textArr;
|
||
|
+
|
||
|
+ for (const option of options) {
|
||
|
+ if (option === "trigger") {
|
||
|
+ result.trigger = true;
|
||
|
+ }
|
||
|
+ else if (option === "continue") {
|
||
|
+ result.continue = true;
|
||
|
+ }
|
||
|
+ else if (option.startsWith("event")) {
|
||
|
+ const event = option.toLowerCase().split("=");
|
||
|
+ event[1] ? result.event = event[1] : result.event = "click";
|
||
|
+ }
|
||
|
+ else if (option.startsWith("delay")) {
|
||
|
+ const delay = option.toLowerCase().split("=");
|
||
|
+ delay[1] ? result.delay = delay[1] : result.delay = "500";
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (!VALID_TYPES.includes(result.event)) {
|
||
|
+ debugLog(result.event,
|
||
|
+ " might be misspelled, check for typos.\n",
|
||
|
+ "These are the supported events:",
|
||
|
+ VALID_TYPES);
|
||
|
+ }
|
||
|
+ return result;
|
||
|
+ }
|
||
|
+
|
||
|
+ const parsedArgs = $([]);
|
||
|
+
|
||
|
+ $(selectors).forEach(rule => {
|
||
|
+ const parsedRule = parseArg(rule);
|
||
|
+ parsedArgs.push(parsedRule);
|
||
|
+ });
|
||
|
+
|
||
|
+ function checkIfAllSelectorsFound() {
|
||
|
+ parsedArgs.forEach(arg => {
|
||
|
+ if (!arg.found) {
|
||
|
+ const queryAll = initQueryAll(arg.selector);
|
||
|
+ const elems = queryAll();
|
||
|
+ if (elems.length > 0)
|
||
|
+ arg.found = true;
|
||
|
+ }
|
||
|
+ });
|
||
|
+ return parsedArgs.every(arg => arg.found);
|
||
|
+ }
|
||
|
+
|
||
|
+ function triggerEvent(node, event, delay) {
|
||
|
+
|
||
|
+ if (!node || !event)
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (event === "click" && node.click) {
|
||
|
+ node.click();
|
||
|
+ debugLog(
|
||
|
+ "Clicked on this node:\n", node, "\nwith a delay of", delay, "ms"
|
||
|
+ );
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ node.dispatchEvent(
|
||
|
+ new MouseEvent$1(event, {bubbles: true, cancelable: true})
|
||
|
+ );
|
||
|
+ debugLog(
|
||
|
+ "A",
|
||
|
+ event,
|
||
|
+ "event was dispatched with a delay of",
|
||
|
+ delay,
|
||
|
+ "ms on this node:\n",
|
||
|
+ node
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+ let allFound = false;
|
||
|
+
|
||
|
+ const [last] = parsedArgs.slice(-1);
|
||
|
+ last.trigger = true;
|
||
|
+
|
||
|
+ let dispatchedNodes = new WeakSet$1();
|
||
|
+
|
||
|
+ let observer = new MutationObserver(findNodesAndDispatchEvents);
|
||
|
+ observer.observe(document, {childList: true, subtree: true});
|
||
|
+ findNodesAndDispatchEvents();
|
||
|
+
|
||
|
+ function findNodesAndDispatchEvents() {
|
||
|
+
|
||
|
+ if (!allFound)
|
||
|
+ allFound = checkIfAllSelectorsFound();
|
||
|
+ if (allFound) {
|
||
|
+ for (const parsedRule of parsedArgs) {
|
||
|
+ const queryAndApply = initQueryAndApply(parsedRule.selector);
|
||
|
+ const delayInMiliseconds = parseInt(parsedRule.delay, 10);
|
||
|
+ if (parsedRule.trigger) {
|
||
|
+ queryAndApply(node => {
|
||
|
+ if (!dispatchedNodes.has(node)) {
|
||
|
+ dispatchedNodes.add(node);
|
||
|
+ if (parsedRule.continue) {
|
||
|
+ setInterval(() => {
|
||
|
+ triggerEvent(node, parsedRule.event, parsedRule.delay);
|
||
|
+ }, delayInMiliseconds);
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ setTimeout(() => {
|
||
|
+ triggerEvent(node, parsedRule.event, parsedRule.delay);
|
||
|
+ }, delayInMiliseconds);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ const snippets = {
|
||
|
+ log,
|
||
|
+ race,
|
||
|
+ "debug": setDebug,
|
||
|
+ "hide-if-matches-xpath": hideIfMatchesXPath,
|
||
|
+ "hide-if-matches-computed-xpath": hideIfMatchesComputedXPath,
|
||
|
+ "hide-if-contains": hideIfContains,
|
||
|
+ "hide-if-contains-similar-text": hideIfContainsSimilarText,
|
||
|
+ "hide-if-contains-visible-text": hideIfContainsVisibleText,
|
||
|
+ "hide-if-contains-and-matches-style": hideIfContainsAndMatchesStyle,
|
||
|
+ "hide-if-has-and-matches-style": hideIfHasAndMatchesStyle,
|
||
|
+ "hide-if-labelled-by": hideIfLabelledBy,
|
||
|
+ "hide-if-contains-image": hideIfContainsImage,
|
||
|
+ "simulate-event-poc": simulateEvent,
|
||
|
+ "simulate-mouse-event": simulateMouseEvent
|
||
|
+ };
|
||
|
+ let context;
|
||
|
+ for (const [name, ...args] of filters) {
|
||
|
+ if (snippets.hasOwnProperty(name)) {
|
||
|
+ try { context = snippets[name].apply(context, args); }
|
||
|
+ catch (error) { console.error(error); }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ context = void 0;
|
||
|
+})(e, ...t);
|
||
|
+
|
||
|
+const callback = (environment, ...filters) => {
|
||
|
+ /*!
|
||
|
+ * This file is part of eyeo's Anti-Circumvention Snippets module (@eyeo/snippets),
|
||
|
+ * Copyright (C) 2006-present eyeo GmbH
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is free software: you can redistribute it and/or modify
|
||
|
+ * it under the terms of the GNU General Public License version 3 as
|
||
|
+ * published by the Free Software Foundation.
|
||
|
+ *
|
||
|
+ * @eyeo/snippets is distributed in the hope that it will be useful,
|
||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
+ * GNU General Public License for more details.
|
||
|
+ *
|
||
|
+ * You should have received a copy of the GNU General Public License
|
||
|
+ * along with @eyeo/snippets. If not, see <http://www.gnu.org/licenses/>.
|
||
|
+ */
|
||
|
+ const $$1 = Proxy;
|
||
|
+
|
||
|
+ const {apply: a, bind: b, call: c} = Function;
|
||
|
+ const apply$2 = c.bind(a);
|
||
|
+ const bind = c.bind(b);
|
||
|
+ const call = c.bind(c);
|
||
|
+
|
||
|
+ const callerHandler = {
|
||
|
+ get(target, name) {
|
||
|
+ return bind(c, target[name]);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const caller = target => new $$1(target, callerHandler);
|
||
|
+
|
||
|
+ const proxy = (source, target) => new $$1(source, {
|
||
|
+ apply: (_, self, args) => apply$2(target, self, args)
|
||
|
+ });
|
||
|
+
|
||
|
+ const handler$2 = {
|
||
|
+ get(target, name) {
|
||
|
+ return bind(target[name], target);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const bound = target => new $$1(target, handler$2);
|
||
|
+
|
||
|
+ const {
|
||
|
+ assign: assign$1,
|
||
|
+ defineProperties: defineProperties$1,
|
||
|
+ freeze: freeze$1,
|
||
|
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$3,
|
||
|
+ getOwnPropertyDescriptors: getOwnPropertyDescriptors$1,
|
||
|
+ getPrototypeOf
|
||
|
+ } = bound(Object);
|
||
|
+
|
||
|
+ const {hasOwnProperty} = caller({});
|
||
|
+
|
||
|
+ const {species} = Symbol;
|
||
|
+
|
||
|
+ const handler$1 = {
|
||
|
+ get(target, name) {
|
||
|
+ const Native = target[name];
|
||
|
+ class Secure extends Native {}
|
||
|
+
|
||
|
+ const proto = getOwnPropertyDescriptors$1(Native.prototype);
|
||
|
+ delete proto.constructor;
|
||
|
+ freeze$1(defineProperties$1(Secure.prototype, proto));
|
||
|
+
|
||
|
+ const statics = getOwnPropertyDescriptors$1(Native);
|
||
|
+ delete statics.length;
|
||
|
+ delete statics.prototype;
|
||
|
+ statics[species] = {value: Secure};
|
||
|
+ return freeze$1(defineProperties$1(Secure, statics));
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const secure = target => new $$1(target, handler$1);
|
||
|
+
|
||
|
+ const libEnvironment = typeof environment !== "undefined" ? environment :
|
||
|
+ {};
|
||
|
+
|
||
|
+ if (typeof globalThis === "undefined")
|
||
|
+ window.globalThis = window;
|
||
|
+
|
||
|
+ const {apply: apply$1, ownKeys} = bound(Reflect);
|
||
|
+
|
||
|
+ const worldEnvDefined = "world" in libEnvironment;
|
||
|
+ const isIsolatedWorld = worldEnvDefined && libEnvironment.world === "ISOLATED";
|
||
|
+ const isMainWorld = worldEnvDefined && libEnvironment.world === "MAIN";
|
||
|
+ const isChrome = typeof chrome === "object" && !!chrome.runtime;
|
||
|
+ const isOtherThanChrome = typeof browser === "object" && !!browser.runtime;
|
||
|
+ const isExtensionContext$2 = !isMainWorld &&
|
||
|
+ (isIsolatedWorld || isChrome || isOtherThanChrome);
|
||
|
+ const copyIfExtension = value => isExtensionContext$2 ?
|
||
|
+ value :
|
||
|
+ create(value, getOwnPropertyDescriptors(value));
|
||
|
+
|
||
|
+ const {
|
||
|
+ create,
|
||
|
+ defineProperties,
|
||
|
+ defineProperty,
|
||
|
+ freeze,
|
||
|
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor$2,
|
||
|
+ getOwnPropertyDescriptors
|
||
|
+ } = bound(Object);
|
||
|
+
|
||
|
+ const invokes = bound(globalThis);
|
||
|
+ const classes = isExtensionContext$2 ? globalThis : secure(globalThis);
|
||
|
+ const {Map: Map$8, RegExp: RegExp$1, Set: Set$2, WeakMap: WeakMap$4, WeakSet: WeakSet$3} = classes;
|
||
|
+
|
||
|
+ const augment = (source, target, method = null) => {
|
||
|
+ const known = ownKeys(target);
|
||
|
+ for (const key of ownKeys(source)) {
|
||
|
+ if (known.includes(key))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ const descriptor = getOwnPropertyDescriptor$2(source, key);
|
||
|
+ if (method && "value" in descriptor) {
|
||
|
+ const {value} = descriptor;
|
||
|
+ if (typeof value === "function")
|
||
|
+ descriptor.value = method(value);
|
||
|
+ }
|
||
|
+ defineProperty(target, key, descriptor);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const primitive = name => {
|
||
|
+ const Super = classes[name];
|
||
|
+ class Class extends Super {}
|
||
|
+ const {toString, valueOf} = Super.prototype;
|
||
|
+ defineProperties(Class.prototype, {
|
||
|
+ toString: {value: toString},
|
||
|
+ valueOf: {value: valueOf}
|
||
|
+ });
|
||
|
+ const type = name.toLowerCase();
|
||
|
+ const method = callback => function() {
|
||
|
+ const result = apply$1(callback, this, arguments);
|
||
|
+ return typeof result === type ? new Class(result) : result;
|
||
|
+ };
|
||
|
+ augment(Super, Class, method);
|
||
|
+ augment(Super.prototype, Class.prototype, method);
|
||
|
+ return Class;
|
||
|
+ };
|
||
|
+
|
||
|
+ const variables$3 = freeze({
|
||
|
+ frozen: new WeakMap$4(),
|
||
|
+ hidden: new WeakSet$3(),
|
||
|
+ iframePropertiesToAbort: {
|
||
|
+ read: new Set$2(),
|
||
|
+ write: new Set$2()
|
||
|
+ },
|
||
|
+ abortedIframes: new WeakMap$4()
|
||
|
+ });
|
||
|
+
|
||
|
+ const startsCapitalized = new RegExp$1("^[A-Z]");
|
||
|
+
|
||
|
+ var env = new Proxy(new Map$8([
|
||
|
+
|
||
|
+ ["chrome", (
|
||
|
+ isExtensionContext$2 && (
|
||
|
+ (isChrome && chrome) ||
|
||
|
+ (isOtherThanChrome && browser)
|
||
|
+ )
|
||
|
+ ) || void 0],
|
||
|
+ ["isExtensionContext", isExtensionContext$2],
|
||
|
+ ["variables", variables$3],
|
||
|
+
|
||
|
+ ["console", copyIfExtension(console)],
|
||
|
+ ["document", globalThis.document],
|
||
|
+ ["performance", copyIfExtension(performance)],
|
||
|
+ ["JSON", copyIfExtension(JSON)],
|
||
|
+ ["Map", Map$8],
|
||
|
+ ["Math", copyIfExtension(Math)],
|
||
|
+ ["Number", isExtensionContext$2 ? Number : primitive("Number")],
|
||
|
+ ["RegExp", RegExp$1],
|
||
|
+ ["Set", Set$2],
|
||
|
+ ["String", isExtensionContext$2 ? String : primitive("String")],
|
||
|
+ ["WeakMap", WeakMap$4],
|
||
|
+ ["WeakSet", WeakSet$3],
|
||
|
+
|
||
|
+ ["MouseEvent", MouseEvent]
|
||
|
+ ]), {
|
||
|
+ get(map, key) {
|
||
|
+ if (map.has(key))
|
||
|
+ return map.get(key);
|
||
|
+
|
||
|
+ let value = globalThis[key];
|
||
|
+ if (typeof value === "function")
|
||
|
+ value = (startsCapitalized.test(key) ? classes : invokes)[key];
|
||
|
+
|
||
|
+ map.set(key, value);
|
||
|
+ return value;
|
||
|
+ },
|
||
|
+ has(map, key) {
|
||
|
+ return map.has(key);
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ class WeakValue {
|
||
|
+ has() { return false; }
|
||
|
+ set() {}
|
||
|
+ }
|
||
|
+
|
||
|
+ const helpers = {WeakSet, WeakMap, WeakValue};
|
||
|
+ const {apply} = Reflect;
|
||
|
+
|
||
|
+ function transformOnce (callback) { const {WeakSet, WeakMap, WeakValue} = (this || helpers);
|
||
|
+ const ws = new WeakSet;
|
||
|
+ const wm = new WeakMap;
|
||
|
+ const wv = new WeakValue;
|
||
|
+ return function (any) {
|
||
|
+ if (ws.has(any))
|
||
|
+ return any;
|
||
|
+
|
||
|
+ if (wm.has(any))
|
||
|
+ return wm.get(any);
|
||
|
+
|
||
|
+ if (wv.has(any))
|
||
|
+ return wv.get(any);
|
||
|
+
|
||
|
+ const value = apply(callback, this, arguments);
|
||
|
+ ws.add(value);
|
||
|
+ if (value !== any)
|
||
|
+ (typeof any === 'object' && any ? wm : wv).set(any, value);
|
||
|
+ return value;
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ const {Map: Map$7, WeakMap: WeakMap$3, WeakSet: WeakSet$2, setTimeout} = env;
|
||
|
+
|
||
|
+ let cleanup = true;
|
||
|
+ let cleanUpCallback = map => {
|
||
|
+ map.clear();
|
||
|
+ cleanup = !cleanup;
|
||
|
+ };
|
||
|
+
|
||
|
+ var transformer = transformOnce.bind({
|
||
|
+ WeakMap: WeakMap$3,
|
||
|
+ WeakSet: WeakSet$2,
|
||
|
+
|
||
|
+ WeakValue: class extends Map$7 {
|
||
|
+ set(key, value) {
|
||
|
+ if (cleanup) {
|
||
|
+ cleanup = !cleanup;
|
||
|
+ setTimeout(cleanUpCallback, 0, this);
|
||
|
+ }
|
||
|
+ return super.set(key, value);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ const {concat, includes, join, reduce, unshift} = caller([]);
|
||
|
+
|
||
|
+ const globals = secure(globalThis);
|
||
|
+
|
||
|
+ const {
|
||
|
+ Map: Map$6,
|
||
|
+ WeakMap: WeakMap$2
|
||
|
+ } = globals;
|
||
|
+
|
||
|
+ const map = new Map$6;
|
||
|
+ const descriptors = target => {
|
||
|
+ const chain = [];
|
||
|
+ let current = target;
|
||
|
+ while (current) {
|
||
|
+ if (map.has(current))
|
||
|
+ unshift(chain, map.get(current));
|
||
|
+ else {
|
||
|
+ const descriptors = getOwnPropertyDescriptors$1(current);
|
||
|
+ map.set(current, descriptors);
|
||
|
+ unshift(chain, descriptors);
|
||
|
+ }
|
||
|
+ current = getPrototypeOf(current);
|
||
|
+ }
|
||
|
+ unshift(chain, {});
|
||
|
+ return apply$2(assign$1, null, chain);
|
||
|
+ };
|
||
|
+
|
||
|
+ const chain = source => {
|
||
|
+ const target = typeof source === 'function' ? source.prototype : source;
|
||
|
+ const chained = descriptors(target);
|
||
|
+ const handler = {
|
||
|
+ get(target, key) {
|
||
|
+ if (key in chained) {
|
||
|
+ const {value, get} = chained[key];
|
||
|
+ if (get)
|
||
|
+ return call(get, target);
|
||
|
+ if (typeof value === 'function')
|
||
|
+ return bind(value, target);
|
||
|
+ }
|
||
|
+ return target[key];
|
||
|
+ },
|
||
|
+ set(target, key, value) {
|
||
|
+ if (key in chained) {
|
||
|
+ const {set} = chained[key];
|
||
|
+ if (set) {
|
||
|
+ call(set, target, value);
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ target[key] = value;
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ };
|
||
|
+ return target => new $$1(target, handler);
|
||
|
+ };
|
||
|
+
|
||
|
+ const {
|
||
|
+ isExtensionContext: isExtensionContext$1,
|
||
|
+ Array: Array$2,
|
||
|
+ Number: Number$1,
|
||
|
+ String: String$1,
|
||
|
+ Object: Object$9
|
||
|
+ } = env;
|
||
|
+
|
||
|
+ const {isArray} = Array$2;
|
||
|
+ const {getOwnPropertyDescriptor: getOwnPropertyDescriptor$1, setPrototypeOf: setPrototypeOf$1} = Object$9;
|
||
|
+
|
||
|
+ const {toString: toString$1} = Object$9.prototype;
|
||
|
+ const {slice} = String$1.prototype;
|
||
|
+ const getBrand = value => call(slice, call(toString$1, value), 8, -1);
|
||
|
+
|
||
|
+ const {get: nodeType} = getOwnPropertyDescriptor$1(Node.prototype, "nodeType");
|
||
|
+
|
||
|
+ const chained = isExtensionContext$1 ? {} : {
|
||
|
+ Attr: chain(Attr),
|
||
|
+ CanvasRenderingContext2D: chain(CanvasRenderingContext2D),
|
||
|
+ CSSStyleDeclaration: chain(CSSStyleDeclaration),
|
||
|
+ Document: chain(Document),
|
||
|
+ Element: chain(Element),
|
||
|
+ HTMLCanvasElement: chain(HTMLCanvasElement),
|
||
|
+ HTMLElement: chain(HTMLElement),
|
||
|
+ HTMLImageElement: chain(HTMLImageElement),
|
||
|
+ HTMLScriptElement: chain(HTMLScriptElement),
|
||
|
+ MutationRecord: chain(MutationRecord),
|
||
|
+ Node: chain(Node),
|
||
|
+ ShadowRoot: chain(ShadowRoot),
|
||
|
+
|
||
|
+ get CSS2Properties() {
|
||
|
+ return chained.CSSStyleDeclaration;
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const upgrade = (value, hint) => {
|
||
|
+ if (hint !== "Element" && hint in chained)
|
||
|
+ return chained[hint](value);
|
||
|
+
|
||
|
+ if (isArray(value))
|
||
|
+ return setPrototypeOf$1(value, Array$2.prototype);
|
||
|
+
|
||
|
+ const brand = getBrand(value);
|
||
|
+ if (brand in chained)
|
||
|
+ return chained[brand](value);
|
||
|
+
|
||
|
+ if (brand in env)
|
||
|
+ return setPrototypeOf$1(value, env[brand].prototype);
|
||
|
+
|
||
|
+ if ("nodeType" in value) {
|
||
|
+ switch (call(nodeType, value)) {
|
||
|
+ case 1:
|
||
|
+ if (!(hint in chained))
|
||
|
+ throw new Error("unknown hint " + hint);
|
||
|
+ return chained[hint](value);
|
||
|
+ case 2:
|
||
|
+ return chained.Attr(value);
|
||
|
+ case 3:
|
||
|
+ return chained.Node(value);
|
||
|
+ case 9:
|
||
|
+ return chained.Document(value);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ throw new Error("unknown brand " + brand);
|
||
|
+ };
|
||
|
+
|
||
|
+ var $ = isExtensionContext$1 ?
|
||
|
+ value => (value === window || value === globalThis ? env : value) :
|
||
|
+ transformer((value, hint = "Element") => {
|
||
|
+ if (value === window || value === globalThis)
|
||
|
+ return env;
|
||
|
+
|
||
|
+ switch (typeof value) {
|
||
|
+ case "object":
|
||
|
+ return value && upgrade(value, hint);
|
||
|
+
|
||
|
+ case "string":
|
||
|
+ return new String$1(value);
|
||
|
+
|
||
|
+ case "number":
|
||
|
+ return new Number$1(value);
|
||
|
+
|
||
|
+ default:
|
||
|
+ throw new Error("unsupported value");
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ const handler = {
|
||
|
+ get(target, name) {
|
||
|
+ const context = target;
|
||
|
+ while (!hasOwnProperty(target, name))
|
||
|
+ target = getPrototypeOf(target);
|
||
|
+ const {get, set} = getOwnPropertyDescriptor$3(target, name);
|
||
|
+ return function () {
|
||
|
+ return arguments.length ?
|
||
|
+ apply$2(set, context, arguments) :
|
||
|
+ call(get, context);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const accessor = target => new $$1(target, handler);
|
||
|
+
|
||
|
+ let debugging = false;
|
||
|
+
|
||
|
+ function debug() {
|
||
|
+ return debugging;
|
||
|
+ }
|
||
|
+
|
||
|
+ function setDebug() {
|
||
|
+ debugging = true;
|
||
|
+ }
|
||
|
+
|
||
|
+ const {console: console$3} = $(window);
|
||
|
+
|
||
|
+ const noop = () => {};
|
||
|
+
|
||
|
+ function log(...args) {
|
||
|
+ if (debug())
|
||
|
+ $(args).unshift("%c DEBUG", "font-weight: bold");
|
||
|
+
|
||
|
+ console$3.log(...args);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getDebugger(name) {
|
||
|
+ return bind(debug() ? log : noop, null, name);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Math: Math$1, RegExp} = $(window);
|
||
|
+
|
||
|
+ function regexEscape(string) {
|
||
|
+ return $(string).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
||
|
+ }
|
||
|
+
|
||
|
+ function toRegExp(pattern) {
|
||
|
+ let {length} = pattern;
|
||
|
+
|
||
|
+ if (length > 1 && pattern[0] === "/") {
|
||
|
+ let isCaseSensitive = pattern[length - 1] === "/";
|
||
|
+
|
||
|
+ if (isCaseSensitive || (length > 2 && $(pattern).endsWith("/i"))) {
|
||
|
+ let args = [$(pattern).slice(1, isCaseSensitive ? -1 : -2)];
|
||
|
+ if (!isCaseSensitive)
|
||
|
+ args.push("i");
|
||
|
+
|
||
|
+ return new RegExp(...args);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return new RegExp(regexEscape(pattern));
|
||
|
+ }
|
||
|
+
|
||
|
+ function randomId() {
|
||
|
+
|
||
|
+ return $(Math$1.floor(Math$1.random() * 2116316160 + 60466176)).toString(36);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ parseFloat,
|
||
|
+ variables: variables$2,
|
||
|
+ Array: Array$1,
|
||
|
+ Error: Error$7,
|
||
|
+ Map: Map$5,
|
||
|
+ Object: Object$8,
|
||
|
+ ReferenceError: ReferenceError$2,
|
||
|
+ Set: Set$1,
|
||
|
+ WeakMap: WeakMap$1
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ let {onerror} = accessor(window);
|
||
|
+
|
||
|
+ let NodeProto$1 = Node.prototype;
|
||
|
+ let ElementProto$2 = Element.prototype;
|
||
|
+
|
||
|
+ let propertyAccessors = null;
|
||
|
+
|
||
|
+ function wrapPropertyAccess(object, property, descriptor,
|
||
|
+ setConfigurable = true) {
|
||
|
+ let $property = $(property);
|
||
|
+ let dotIndex = $property.indexOf(".");
|
||
|
+ if (dotIndex == -1) {
|
||
|
+
|
||
|
+ let currentDescriptor = Object$8.getOwnPropertyDescriptor(object, property);
|
||
|
+ if (currentDescriptor && !currentDescriptor.configurable)
|
||
|
+ return;
|
||
|
+
|
||
|
+ let newDescriptor = Object$8.assign({}, descriptor, {
|
||
|
+ configurable: setConfigurable
|
||
|
+ });
|
||
|
+
|
||
|
+ if (!currentDescriptor && !newDescriptor.get && newDescriptor.set) {
|
||
|
+ let propertyValue = object[property];
|
||
|
+ newDescriptor.get = () => propertyValue;
|
||
|
+ }
|
||
|
+
|
||
|
+ Object$8.defineProperty(object, property, newDescriptor);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let name = $property.slice(0, dotIndex).toString();
|
||
|
+ property = $property.slice(dotIndex + 1).toString();
|
||
|
+ let value = object[name];
|
||
|
+ if (value && (typeof value == "object" || typeof value == "function"))
|
||
|
+ wrapPropertyAccess(value, property, descriptor);
|
||
|
+
|
||
|
+ let currentDescriptor = Object$8.getOwnPropertyDescriptor(object, name);
|
||
|
+ if (currentDescriptor && !currentDescriptor.configurable)
|
||
|
+ return;
|
||
|
+
|
||
|
+ if (!propertyAccessors)
|
||
|
+ propertyAccessors = new WeakMap$1();
|
||
|
+
|
||
|
+ if (!propertyAccessors.has(object))
|
||
|
+ propertyAccessors.set(object, new Map$5());
|
||
|
+
|
||
|
+ let properties = propertyAccessors.get(object);
|
||
|
+ if (properties.has(name)) {
|
||
|
+ properties.get(name).set(property, descriptor);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let toBeWrapped = new Map$5([[property, descriptor]]);
|
||
|
+ properties.set(name, toBeWrapped);
|
||
|
+ Object$8.defineProperty(object, name, {
|
||
|
+ get: () => value,
|
||
|
+ set(newValue) {
|
||
|
+ value = newValue;
|
||
|
+ if (value && (typeof value == "object" || typeof value == "function")) {
|
||
|
+
|
||
|
+ for (let [prop, desc] of toBeWrapped)
|
||
|
+ wrapPropertyAccess(value, prop, desc);
|
||
|
+ }
|
||
|
+ },
|
||
|
+ configurable: setConfigurable
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ function overrideOnError(magic) {
|
||
|
+ let prev = onerror();
|
||
|
+ onerror((...args) => {
|
||
|
+ let message = args.length && args[0];
|
||
|
+ if (typeof message == "string" && $(message).includes(magic))
|
||
|
+ return true;
|
||
|
+ if (typeof prev == "function")
|
||
|
+ return apply$2(prev, this, args);
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnRead(loggingPrefix, context, property,
|
||
|
+ setConfigurable = true) {
|
||
|
+ let debugLog = getDebugger(loggingPrefix);
|
||
|
+
|
||
|
+ if (!property) {
|
||
|
+ debugLog("no property to abort on read");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let rid = randomId();
|
||
|
+
|
||
|
+ function abort() {
|
||
|
+ debugLog(`${property} access aborted`);
|
||
|
+ throw new ReferenceError$2(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ debugLog(`aborting on ${property} access`);
|
||
|
+
|
||
|
+ wrapPropertyAccess(context,
|
||
|
+ property,
|
||
|
+ {get: abort, set() {}},
|
||
|
+ setConfigurable);
|
||
|
+ overrideOnError(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnWrite(loggingPrefix, context, property,
|
||
|
+ setConfigurable = true) {
|
||
|
+ let debugLog = getDebugger(loggingPrefix);
|
||
|
+
|
||
|
+ if (!property) {
|
||
|
+ debugLog("no property to abort on write");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let rid = randomId();
|
||
|
+
|
||
|
+ function abort() {
|
||
|
+ debugLog(`setting ${property} aborted`);
|
||
|
+ throw new ReferenceError$2(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ debugLog(`aborting when setting ${property}`);
|
||
|
+
|
||
|
+ wrapPropertyAccess(context, property, {set: abort}, setConfigurable);
|
||
|
+ overrideOnError(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnIframe(
|
||
|
+ properties,
|
||
|
+ abortRead = false,
|
||
|
+ abortWrite = false
|
||
|
+ ) {
|
||
|
+ let abortedIframes = variables$2.abortedIframes;
|
||
|
+ let iframePropertiesToAbort = variables$2.iframePropertiesToAbort;
|
||
|
+
|
||
|
+ for (let frame of Array$1.from(window.frames)) {
|
||
|
+ if (abortedIframes.has(frame)) {
|
||
|
+ for (let property of properties) {
|
||
|
+ if (abortRead)
|
||
|
+ abortedIframes.get(frame).read.add(property);
|
||
|
+ if (abortWrite)
|
||
|
+ abortedIframes.get(frame).write.add(property);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ for (let property of properties) {
|
||
|
+ if (abortRead)
|
||
|
+ iframePropertiesToAbort.read.add(property);
|
||
|
+ if (abortWrite)
|
||
|
+ iframePropertiesToAbort.write.add(property);
|
||
|
+ }
|
||
|
+
|
||
|
+ queryAndProxyIframe();
|
||
|
+ if (!abortedIframes.has(document)) {
|
||
|
+ abortedIframes.set(document, true);
|
||
|
+ addHooksOnDomAdditions(queryAndProxyIframe);
|
||
|
+ }
|
||
|
+
|
||
|
+ function queryAndProxyIframe() {
|
||
|
+ for (let frame of Array$1.from(window.frames)) {
|
||
|
+
|
||
|
+ if (!abortedIframes.has(frame)) {
|
||
|
+ abortedIframes.set(frame, {
|
||
|
+ read: new Set$1(iframePropertiesToAbort.read),
|
||
|
+ write: new Set$1(iframePropertiesToAbort.write)
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ let readProps = abortedIframes.get(frame).read;
|
||
|
+ if (readProps.size > 0) {
|
||
|
+ let props = Array$1.from(readProps);
|
||
|
+ readProps.clear();
|
||
|
+ for (let property of props)
|
||
|
+ abortOnRead("abort-on-iframe-property-read", frame, property);
|
||
|
+ }
|
||
|
+
|
||
|
+ let writeProps = abortedIframes.get(frame).write;
|
||
|
+ if (writeProps.size > 0) {
|
||
|
+ let props = Array$1.from(writeProps);
|
||
|
+ writeProps.clear();
|
||
|
+ for (let property of props)
|
||
|
+ abortOnWrite("abort-on-iframe-property-write", frame, property);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function addHooksOnDomAdditions(endCallback) {
|
||
|
+ let descriptor;
|
||
|
+
|
||
|
+ wrapAccess(NodeProto$1, ["appendChild", "insertBefore", "replaceChild"]);
|
||
|
+ wrapAccess(ElementProto$2, ["append", "prepend", "replaceWith", "after",
|
||
|
+ "before", "insertAdjacentElement",
|
||
|
+ "insertAdjacentHTML"]);
|
||
|
+
|
||
|
+ descriptor = getInnerHTMLDescriptor(ElementProto$2, "innerHTML");
|
||
|
+ wrapPropertyAccess(ElementProto$2, "innerHTML", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInnerHTMLDescriptor(ElementProto$2, "outerHTML");
|
||
|
+ wrapPropertyAccess(ElementProto$2, "outerHTML", descriptor);
|
||
|
+
|
||
|
+ function wrapAccess(prototype, names) {
|
||
|
+ for (let name of names) {
|
||
|
+ let desc = getAppendChildDescriptor(prototype, name);
|
||
|
+ wrapPropertyAccess(prototype, name, desc);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function getAppendChildDescriptor(target, property) {
|
||
|
+ let currentValue = target[property];
|
||
|
+ return {
|
||
|
+ get() {
|
||
|
+ return function(...args) {
|
||
|
+ let result;
|
||
|
+ result = apply$2(currentValue, this, args);
|
||
|
+ endCallback && endCallback();
|
||
|
+ return result;
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getInnerHTMLDescriptor(target, property) {
|
||
|
+ let desc = Object$8.getOwnPropertyDescriptor(target, property);
|
||
|
+ let {set: prevSetter} = desc || {};
|
||
|
+ return {
|
||
|
+ set(val) {
|
||
|
+ let result;
|
||
|
+ result = call(prevSetter, this, val);
|
||
|
+ endCallback && endCallback();
|
||
|
+ return result;
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Object: NativeObject} = window;
|
||
|
+ function findOwner(root, path) {
|
||
|
+ if (!(root instanceof NativeObject))
|
||
|
+ return;
|
||
|
+
|
||
|
+ let object = root;
|
||
|
+ let chain = $(path).split(".");
|
||
|
+
|
||
|
+ if (chain.length === 0)
|
||
|
+ return;
|
||
|
+
|
||
|
+ for (let i = 0; i < chain.length - 1; i++) {
|
||
|
+ let prop = chain[i];
|
||
|
+
|
||
|
+ if (!hasOwnProperty(object, prop))
|
||
|
+ return;
|
||
|
+
|
||
|
+ object = object[prop];
|
||
|
+
|
||
|
+ if (!(object instanceof NativeObject))
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ let prop = chain[chain.length - 1];
|
||
|
+
|
||
|
+ if (hasOwnProperty(object, prop))
|
||
|
+ return [object, prop];
|
||
|
+ }
|
||
|
+
|
||
|
+ const decimals = $(/^\d+$/);
|
||
|
+
|
||
|
+ function overrideValue(value) {
|
||
|
+ switch (value) {
|
||
|
+ case "false":
|
||
|
+ return false;
|
||
|
+ case "true":
|
||
|
+ return true;
|
||
|
+ case "null":
|
||
|
+ return null;
|
||
|
+ case "noopFunc":
|
||
|
+ return () => {};
|
||
|
+ case "trueFunc":
|
||
|
+ return () => true;
|
||
|
+ case "falseFunc":
|
||
|
+ return () => false;
|
||
|
+ case "emptyArray":
|
||
|
+ return [];
|
||
|
+ case "emptyObj":
|
||
|
+ return {};
|
||
|
+ case "undefined":
|
||
|
+ return void 0;
|
||
|
+ case "":
|
||
|
+ return value;
|
||
|
+ default:
|
||
|
+ if (decimals.test(value))
|
||
|
+ return parseFloat(value);
|
||
|
+
|
||
|
+ throw new Error$7("[override-property-read snippet]: " +
|
||
|
+ `Value "${value}" is not valid.`);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {HTMLScriptElement: HTMLScriptElement$1, Object: Object$7, ReferenceError: ReferenceError$1} = $(window);
|
||
|
+ let Script = Object$7.getPrototypeOf(HTMLScriptElement$1);
|
||
|
+
|
||
|
+ function abortCurrentInlineScript(api, search = null) {
|
||
|
+ const debugLog = getDebugger("abort-current-inline-script");
|
||
|
+ const re = search ? toRegExp(search) : null;
|
||
|
+
|
||
|
+ const rid = randomId();
|
||
|
+ const us = $(document).currentScript;
|
||
|
+
|
||
|
+ let object = window;
|
||
|
+ const path = $(api).split(".");
|
||
|
+ const name = $(path).pop();
|
||
|
+
|
||
|
+ for (let node of $(path)) {
|
||
|
+ object = object[node];
|
||
|
+ if (
|
||
|
+ !object || !(typeof object == "object" || typeof object == "function")) {
|
||
|
+ debugLog(path, " is not found");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ const {get: prevGetter, set: prevSetter} =
|
||
|
+ Object$7.getOwnPropertyDescriptor(object, name) || {};
|
||
|
+
|
||
|
+ let currentValue = object[name];
|
||
|
+ if (typeof currentValue === "undefined")
|
||
|
+ debugLog("The property", name, "doesn't exist yet. Check typos.");
|
||
|
+
|
||
|
+ const abort = () => {
|
||
|
+ const element = $(document).currentScript;
|
||
|
+ if (element instanceof Script &&
|
||
|
+ $(element, "HTMLScriptElement").src == "" &&
|
||
|
+ element != us &&
|
||
|
+ (!re || re.test($(element).textContent))) {
|
||
|
+ debugLog(path, " is aborted \n", element);
|
||
|
+ throw new ReferenceError$1(rid);
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ const descriptor = {
|
||
|
+ get() {
|
||
|
+ abort();
|
||
|
+
|
||
|
+ if (prevGetter)
|
||
|
+ return call(prevGetter, this);
|
||
|
+
|
||
|
+ return currentValue;
|
||
|
+ },
|
||
|
+ set(value) {
|
||
|
+ abort();
|
||
|
+
|
||
|
+ if (prevSetter)
|
||
|
+ call(prevSetter, this, value);
|
||
|
+ else
|
||
|
+ currentValue = value;
|
||
|
+ }
|
||
|
+ };
|
||
|
+
|
||
|
+ wrapPropertyAccess(object, name, descriptor);
|
||
|
+
|
||
|
+ overrideOnError(rid);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnIframePropertyRead(...properties) {
|
||
|
+ abortOnIframe(properties, true, false);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnIframePropertyWrite(...properties) {
|
||
|
+ abortOnIframe(properties, false, true);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnPropertyRead(property, setConfigurable) {
|
||
|
+ const configurableFlag = !(setConfigurable === "false");
|
||
|
+ abortOnRead("abort-on-property-read", window, property, configurableFlag);
|
||
|
+ }
|
||
|
+
|
||
|
+ function abortOnPropertyWrite(property, setConfigurable) {
|
||
|
+ const configurableFlag = !(setConfigurable === "false");
|
||
|
+ abortOnWrite("abort-on-property-write", window, property, configurableFlag);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Error: Error$6} = $(window);
|
||
|
+ let {cookie: documentCookies} = accessor(document);
|
||
|
+
|
||
|
+ function cookieRemover(cookie) {
|
||
|
+ if (!cookie)
|
||
|
+ throw new Error$6("[cookie-remover snippet]: No cookie to remove.");
|
||
|
+
|
||
|
+ let debugLog = getDebugger("cookie-remover");
|
||
|
+ let re = toRegExp(cookie);
|
||
|
+
|
||
|
+ if (!$(/^http|^about/).test(location.protocol)) {
|
||
|
+ debugLog("Snippet only works for http or https and about.");
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ debugLog("Parsing cookies for matches");
|
||
|
+
|
||
|
+ for (const pair of $(getCookieMatches())) {
|
||
|
+ let $hostname = $(location.hostname);
|
||
|
+ let name = $(pair).split("=")[0];
|
||
|
+ let expires = "expires=Thu, 01 Jan 1970 00:00:00 GMT";
|
||
|
+ let path = "path=/";
|
||
|
+ let domain = "domain=" + $hostname.slice($hostname.indexOf(".") + 1);
|
||
|
+
|
||
|
+ documentCookies(`${$(name).trim()}=;${expires};${path};${domain}`);
|
||
|
+
|
||
|
+ debugLog(`Set expiration date on ${name}`);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getCookieMatches() {
|
||
|
+ const arr = $(documentCookies()).split(";");
|
||
|
+ return arr.filter(str => re.test($(str).split("=")[0]));
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ let {
|
||
|
+ document: document$1,
|
||
|
+ getComputedStyle,
|
||
|
+ isExtensionContext,
|
||
|
+ variables: variables$1,
|
||
|
+ Array,
|
||
|
+ MutationObserver: MutationObserver$2,
|
||
|
+ Object: Object$6,
|
||
|
+ XPathEvaluator,
|
||
|
+ XPathExpression,
|
||
|
+ XPathResult
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ let {querySelectorAll} = document$1;
|
||
|
+ let $$ = querySelectorAll && bind(querySelectorAll, document$1);
|
||
|
+
|
||
|
+ const {assign, setPrototypeOf} = Object$6;
|
||
|
+
|
||
|
+ class $XPathExpression extends XPathExpression {
|
||
|
+ evaluate(...args) {
|
||
|
+ return setPrototypeOf(
|
||
|
+ apply$2(super.evaluate, this, args),
|
||
|
+ XPathResult.prototype
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ class $XPathEvaluator extends XPathEvaluator {
|
||
|
+ createExpression(...args) {
|
||
|
+ return setPrototypeOf(
|
||
|
+ apply$2(super.createExpression, this, args),
|
||
|
+ $XPathExpression.prototype
|
||
|
+ );
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function hideElement(element) {
|
||
|
+ if (variables$1.hidden.has(element))
|
||
|
+ return;
|
||
|
+
|
||
|
+ notifyElementHidden(element);
|
||
|
+
|
||
|
+ variables$1.hidden.add(element);
|
||
|
+
|
||
|
+ let {style} = $(element);
|
||
|
+ let $style = $(style, "CSSStyleDeclaration");
|
||
|
+ let properties = $([]);
|
||
|
+ let {debugCSSProperties} = libEnvironment;
|
||
|
+
|
||
|
+ for (let [key, value] of (debugCSSProperties || [["display", "none"]])) {
|
||
|
+ $style.setProperty(key, value, "important");
|
||
|
+ properties.push([key, $style.getPropertyValue(key)]);
|
||
|
+ }
|
||
|
+
|
||
|
+ new MutationObserver$2(() => {
|
||
|
+ for (let [key, value] of properties) {
|
||
|
+ let propertyValue = $style.getPropertyValue(key);
|
||
|
+ let propertyPriority = $style.getPropertyPriority(key);
|
||
|
+ if (propertyValue != value || propertyPriority != "important")
|
||
|
+ $style.setProperty(key, value, "important");
|
||
|
+ }
|
||
|
+ }).observe(element, {attributes: true,
|
||
|
+ attributeFilter: ["style"]});
|
||
|
+ }
|
||
|
+
|
||
|
+ function notifyElementHidden(element) {
|
||
|
+ if (isExtensionContext && typeof checkElement === "function")
|
||
|
+ checkElement(element);
|
||
|
+ }
|
||
|
+
|
||
|
+ function initQueryAndApply(selector) {
|
||
|
+ let $selector = selector;
|
||
|
+ if ($selector.startsWith("xpath(") &&
|
||
|
+ $selector.endsWith(")")) {
|
||
|
+ let xpathQuery = $selector.slice(6, -1);
|
||
|
+ let evaluator = new $XPathEvaluator();
|
||
|
+ let expression = evaluator.createExpression(xpathQuery, null);
|
||
|
+
|
||
|
+ let flag = XPathResult.ORDERED_NODE_SNAPSHOT_TYPE;
|
||
|
+
|
||
|
+ return cb => {
|
||
|
+ if (!cb)
|
||
|
+ return;
|
||
|
+ let result = expression.evaluate(document$1, flag, null);
|
||
|
+ let {snapshotLength} = result;
|
||
|
+ for (let i = 0; i < snapshotLength; i++)
|
||
|
+ cb(result.snapshotItem(i));
|
||
|
+ };
|
||
|
+ }
|
||
|
+ return cb => $$(selector).forEach(cb);
|
||
|
+ }
|
||
|
+
|
||
|
+ function initQueryAll(selector) {
|
||
|
+ let $selector = selector;
|
||
|
+ if ($selector.startsWith("xpath(") &&
|
||
|
+ $selector.endsWith(")")) {
|
||
|
+ let queryAndApply = initQueryAndApply(selector);
|
||
|
+ return () => {
|
||
|
+ let elements = $([]);
|
||
|
+ queryAndApply(e => elements.push(e));
|
||
|
+ return elements;
|
||
|
+ };
|
||
|
+ }
|
||
|
+ return () => Array.from($$(selector));
|
||
|
+ }
|
||
|
+
|
||
|
+ let {ELEMENT_NODE, TEXT_NODE, prototype: NodeProto} = Node;
|
||
|
+ let {prototype: ElementProto$1} = Element;
|
||
|
+ let {prototype: HTMLElementProto} = HTMLElement;
|
||
|
+
|
||
|
+ let {
|
||
|
+ console: console$2,
|
||
|
+ variables,
|
||
|
+ DOMParser,
|
||
|
+ Error: Error$5,
|
||
|
+ MutationObserver: MutationObserver$1,
|
||
|
+ Object: Object$5,
|
||
|
+ ReferenceError
|
||
|
+ } = $(window);
|
||
|
+
|
||
|
+ let {getOwnPropertyDescriptor} = Object$5;
|
||
|
+
|
||
|
+ function freezeElement(selector, options = "", ...exceptions) {
|
||
|
+ let observer;
|
||
|
+ let subtree = false;
|
||
|
+ let shouldAbort = false;
|
||
|
+ let exceptionSelectors = $(exceptions).filter(e => !isRegex(e));
|
||
|
+ let regexExceptions = $(exceptions).filter(e => isRegex(e)).map(toRegExp);
|
||
|
+ let rid = randomId();
|
||
|
+ let targetNodes;
|
||
|
+ let queryAll = initQueryAll(selector);
|
||
|
+
|
||
|
+ checkOptions();
|
||
|
+ let data = {
|
||
|
+ selector,
|
||
|
+ shouldAbort,
|
||
|
+ rid,
|
||
|
+ exceptionSelectors,
|
||
|
+ regexExceptions,
|
||
|
+ changeId: 0
|
||
|
+ };
|
||
|
+ if (!variables.frozen.has(document)) {
|
||
|
+ variables.frozen.set(document, true);
|
||
|
+ proxyNativeProperties();
|
||
|
+ }
|
||
|
+ observer = new MutationObserver$1(searchAndAttach);
|
||
|
+ observer.observe(document, {childList: true, subtree: true});
|
||
|
+ searchAndAttach();
|
||
|
+
|
||
|
+ function isRegex(s) {
|
||
|
+ return s.length >= 2 && s[0] == "/" && s[s.length - 1] == "/";
|
||
|
+ }
|
||
|
+
|
||
|
+ function checkOptions() {
|
||
|
+ let optionsChunks = $(options).split("+");
|
||
|
+ if (optionsChunks.length === 1 && optionsChunks[0] === "")
|
||
|
+ optionsChunks = [];
|
||
|
+ for (let chunk of optionsChunks) {
|
||
|
+ switch (chunk) {
|
||
|
+ case "subtree":
|
||
|
+ subtree = true;
|
||
|
+ break;
|
||
|
+ case "abort":
|
||
|
+ shouldAbort = true;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ throw new Error$5("[freeze] Unknown option passed to the snippet." +
|
||
|
+ " [selector]: " + selector +
|
||
|
+ " [option]: " + chunk);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function proxyNativeProperties() {
|
||
|
+ let descriptor;
|
||
|
+
|
||
|
+ descriptor = getAppendChildDescriptor(
|
||
|
+ NodeProto, "appendChild", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "appendChild", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendChildDescriptor(
|
||
|
+ NodeProto, "insertBefore", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "insertBefore", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendChildDescriptor(
|
||
|
+ NodeProto, "replaceChild", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "replaceChild", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1, "append", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "append", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1, "prepend", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "prepend", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "replaceWith",
|
||
|
+ isFrozenOrHasFrozenParent,
|
||
|
+ getSnippetDataFromNodeOrParent
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "replaceWith", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "after",
|
||
|
+ isFrozenOrHasFrozenParent,
|
||
|
+ getSnippetDataFromNodeOrParent
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "after", descriptor);
|
||
|
+
|
||
|
+ descriptor = getAppendDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "before",
|
||
|
+ isFrozenOrHasFrozenParent,
|
||
|
+ getSnippetDataFromNodeOrParent
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "before", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInsertAdjacentDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "insertAdjacentElement",
|
||
|
+ isFrozenAndInsideTarget,
|
||
|
+ getSnippetDataBasedOnTarget
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentElement", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInsertAdjacentDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "insertAdjacentHTML",
|
||
|
+ isFrozenAndInsideTarget,
|
||
|
+ getSnippetDataBasedOnTarget
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentHTML", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInsertAdjacentDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "insertAdjacentText",
|
||
|
+ isFrozenAndInsideTarget,
|
||
|
+ getSnippetDataBasedOnTarget
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "insertAdjacentText", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInnerHTMLDescriptor(
|
||
|
+ ElementProto$1, "innerHTML", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "innerHTML", descriptor);
|
||
|
+
|
||
|
+ descriptor = getInnerHTMLDescriptor(
|
||
|
+ ElementProto$1,
|
||
|
+ "outerHTML",
|
||
|
+ isFrozenOrHasFrozenParent,
|
||
|
+ getSnippetDataFromNodeOrParent
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(ElementProto$1, "outerHTML", descriptor);
|
||
|
+
|
||
|
+ descriptor = getTextContentDescriptor(
|
||
|
+ NodeProto, "textContent", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "textContent", descriptor);
|
||
|
+
|
||
|
+ descriptor = getTextContentDescriptor(
|
||
|
+ HTMLElementProto, "innerText", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(HTMLElementProto, "innerText", descriptor);
|
||
|
+
|
||
|
+ descriptor = getTextContentDescriptor(
|
||
|
+ NodeProto, "nodeValue", isFrozen, getSnippetData
|
||
|
+ );
|
||
|
+ wrapPropertyAccess(NodeProto, "nodeValue", descriptor);
|
||
|
+
|
||
|
+ function isFrozen(node) {
|
||
|
+ return node && variables.frozen.has(node);
|
||
|
+ }
|
||
|
+
|
||
|
+ function isFrozenOrHasFrozenParent(node) {
|
||
|
+ try {
|
||
|
+ return node &&
|
||
|
+ (variables.frozen.has(node) ||
|
||
|
+ variables.frozen.has($(node).parentNode));
|
||
|
+ }
|
||
|
+ catch (error) {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function isFrozenAndInsideTarget(node, isInsideTarget) {
|
||
|
+ try {
|
||
|
+ return node &&
|
||
|
+ (variables.frozen.has(node) && isInsideTarget ||
|
||
|
+ variables.frozen.has($(node).parentNode) &&
|
||
|
+ !isInsideTarget);
|
||
|
+ }
|
||
|
+ catch (error) {
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function getSnippetData(node) {
|
||
|
+ return variables.frozen.get(node);
|
||
|
+ }
|
||
|
+
|
||
|
+ function getSnippetDataFromNodeOrParent(node) {
|
||
|
+ try {
|
||
|
+ if (variables.frozen.has(node))
|
||
|
+ return variables.frozen.get(node);
|
||
|
+ let parent = $(node).parentNode;
|
||
|
+ return variables.frozen.get(parent);
|
||
|
+ }
|
||
|
+ catch (error) {}
|
||
|
+ }
|
||
|
+
|
||
|
+ function getSnippetDataBasedOnTarget(node, isInsideTarget) {
|
||
|
+ try {
|
||
|
+ if (variables.frozen.has(node) && isInsideTarget)
|
||
|
+ return variables.frozen.get(node);
|
||
|
+ let parent = $(node).parentNode;
|
||
|
+ return variables.frozen.get(parent);
|
||
|
+ }
|
||
|
+ catch (error) {}
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function searchAndAttach() {
|
||
|
+ targetNodes = queryAll();
|
||
|
+ markNodes(targetNodes, false);
|
||
|
+ }
|
||
|
+
|
||
|
+ function markNodes(nodes, isChild = true) {
|
||
|
+ for (let node of nodes) {
|
||
|
+ if (!variables.frozen.has(node)) {
|
||
|
+ variables.frozen.set(node, data);
|
||
|
+ if (!isChild && subtree) {
|
||
|
+ new MutationObserver$1(mutationsList => {
|
||
|
+ for (let mutation of $(mutationsList))
|
||
|
+ markNodes($(mutation, "MutationRecord").addedNodes);
|
||
|
+ }).observe(node, {childList: true, subtree: true});
|
||
|
+ }
|
||
|
+ if (subtree && $(node).nodeType === ELEMENT_NODE)
|
||
|
+ markNodes($(node).childNodes);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function logPrefixed(id, ...args) {
|
||
|
+ log(`[freeze][${id}] `, ...args);
|
||
|
+ }
|
||
|
+
|
||
|
+ function logChange(nodeOrDOMString, target, property, snippetData) {
|
||
|
+ let targetSelector = snippetData.selector;
|
||
|
+ let chgId = snippetData.changeId;
|
||
|
+ let isDOMString = typeof nodeOrDOMString == "string";
|
||
|
+ let action = snippetData.shouldAbort ? "aborting" : "watching";
|
||
|
+ console$2.groupCollapsed(`[freeze][${chgId}] ${action}: ${targetSelector}`);
|
||
|
+ switch (property) {
|
||
|
+ case "appendChild":
|
||
|
+ case "append":
|
||
|
+ case "prepend":
|
||
|
+ case "insertBefore":
|
||
|
+ case "replaceChild":
|
||
|
+ case "insertAdjacentElement":
|
||
|
+ case "insertAdjacentHTML":
|
||
|
+ case "insertAdjacentText":
|
||
|
+ case "innerHTML":
|
||
|
+ case "outerHTML":
|
||
|
+ logPrefixed(chgId,
|
||
|
+ isDOMString ? "text: " : "node: ",
|
||
|
+ nodeOrDOMString);
|
||
|
+ logPrefixed(chgId, "added to node: ", target);
|
||
|
+ break;
|
||
|
+ case "replaceWith":
|
||
|
+ case "after":
|
||
|
+ case "before":
|
||
|
+ logPrefixed(chgId,
|
||
|
+ isDOMString ? "text: " : "node: ",
|
||
|
+ nodeOrDOMString);
|
||
|
+ logPrefixed(chgId, "added to node: ", $(target).parentNode);
|
||
|
+ break;
|
||
|
+ case "textContent":
|
||
|
+ case "innerText":
|
||
|
+ case "nodeValue":
|
||
|
+ logPrefixed(chgId, "content of node: ", target);
|
||
|
+ logPrefixed(chgId, "changed to: ", nodeOrDOMString);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ logPrefixed(chgId, `using the function "${property}"`);
|
||
|
+ console$2.groupEnd();
|
||
|
+ snippetData.changeId++;
|
||
|
+ }
|
||
|
+
|
||
|
+ function isExceptionNode(element, expSelectors) {
|
||
|
+ if (expSelectors) {
|
||
|
+ let $element = $(element);
|
||
|
+ for (let exception of expSelectors) {
|
||
|
+ if ($element.matches(exception))
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ function isExceptionText(string, regExceptions) {
|
||
|
+ if (regExceptions) {
|
||
|
+ for (let exception of regExceptions) {
|
||
|
+ if (exception.test(string))
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ function abort(id) {
|
||
|
+ throw new ReferenceError(id);
|
||
|
+ }
|
||
|
+
|
||
|
+ function checkHTML(htmlText, parent, property, snippetData) {
|
||
|
+ let domparser = new DOMParser();
|
||
|
+ let {body} = $(domparser.parseFromString(htmlText, "text/html"));
|
||
|
+ let nodes = $(body).childNodes;
|
||
|
+ let accepted = checkMultiple(nodes, parent, property, snippetData);
|
||
|
+ let content = $(accepted).map(node => {
|
||
|
+ switch ($(node).nodeType) {
|
||
|
+ case ELEMENT_NODE:
|
||
|
+ return $(node).outerHTML;
|
||
|
+ case TEXT_NODE:
|
||
|
+ return $(node).textContent;
|
||
|
+ default:
|
||
|
+ return "";
|
||
|
+ }
|
||
|
+ });
|
||
|
+ return content.join("");
|
||
|
+ }
|
||
|
+
|
||
|
+ function checkMultiple(nodesOrDOMStrings, parent, property, snippetData) {
|
||
|
+ let accepted = $([]);
|
||
|
+ for (let nodeOrDOMString of nodesOrDOMStrings) {
|
||
|
+ if (checkShouldInsert(nodeOrDOMString, parent, property, snippetData))
|
||
|
+ accepted.push(nodeOrDOMString);
|
||
|
+ }
|
||
|
+ return accepted;
|
||
|
+ }
|
||
|
+
|
||
|
+ function checkShouldInsert(nodeOrDOMString, parent, property, snippetData) {
|
||
|
+ let aborting = snippetData.shouldAbort;
|
||
|
+ let regExceptions = snippetData.regexExceptions;
|
||
|
+ let expSelectors = snippetData.exceptionSelectors;
|
||
|
+ let id = snippetData.rid;
|
||
|
+ if (typeof nodeOrDOMString == "string") {
|
||
|
+ let domString = nodeOrDOMString;
|
||
|
+ if (isExceptionText(domString, regExceptions))
|
||
|
+ return true;
|
||
|
+ if (debug())
|
||
|
+ logChange(domString, parent, property, snippetData);
|
||
|
+ if (aborting)
|
||
|
+ abort(id);
|
||
|
+ return debug();
|
||
|
+ }
|
||
|
+
|
||
|
+ let node = nodeOrDOMString;
|
||
|
+ switch ($(node).nodeType) {
|
||
|
+ case ELEMENT_NODE:
|
||
|
+ if (isExceptionNode(node, expSelectors))
|
||
|
+ return true;
|
||
|
+ if (aborting) {
|
||
|
+ if (debug())
|
||
|
+ logChange(node, parent, property, snippetData);
|
||
|
+ abort(id);
|
||
|
+ }
|
||
|
+ if (debug()) {
|
||
|
+ hideElement(node);
|
||
|
+ logChange(node, parent, property, snippetData);
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ return false;
|
||
|
+ case TEXT_NODE:
|
||
|
+ if (isExceptionText($(node).textContent, regExceptions))
|
||
|
+ return true;
|
||
|
+ if (debug())
|
||
|
+ logChange(node, parent, property, snippetData);
|
||
|
+ if (aborting)
|
||
|
+ abort(id);
|
||
|
+ return false;
|
||
|
+ default:
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ function getAppendChildDescriptor(target, property, shouldValidate,
|
||
|
+ getSnippetData) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let origin = desc.get && call(desc.get, target) || desc.value;
|
||
|
+ if (!origin)
|
||
|
+ return;
|
||
|
+
|
||
|
+ return {
|
||
|
+ get() {
|
||
|
+ return function(...args) {
|
||
|
+ if (shouldValidate(this)) {
|
||
|
+ let snippetData = getSnippetData(this);
|
||
|
+ if (snippetData) {
|
||
|
+ let incomingNode = args[0];
|
||
|
+ if (!checkShouldInsert(incomingNode, this, property, snippetData))
|
||
|
+ return incomingNode;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return apply$2(origin, this, args);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getAppendDescriptor(
|
||
|
+ target, property, shouldValidate, getSnippetData
|
||
|
+ ) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let origin = desc.get && call(desc.get, target) || desc.value;
|
||
|
+ if (!origin)
|
||
|
+ return;
|
||
|
+ return {
|
||
|
+ get() {
|
||
|
+ return function(...nodesOrDOMStrings) {
|
||
|
+ if (!shouldValidate(this))
|
||
|
+ return apply$2(origin, this, nodesOrDOMStrings);
|
||
|
+
|
||
|
+ let snippetData = getSnippetData(this);
|
||
|
+ if (!snippetData)
|
||
|
+ return apply$2(origin, this, nodesOrDOMStrings);
|
||
|
+
|
||
|
+ let accepted = checkMultiple(
|
||
|
+ nodesOrDOMStrings, this, property, snippetData
|
||
|
+ );
|
||
|
+ if (accepted.length > 0)
|
||
|
+ return apply$2(origin, this, accepted);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getInsertAdjacentDescriptor(
|
||
|
+ target, property, shouldValidate, getSnippetData
|
||
|
+ ) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let origin = desc.get && call(desc.get, target) || desc.value;
|
||
|
+ if (!origin)
|
||
|
+ return;
|
||
|
+
|
||
|
+ return {
|
||
|
+ get() {
|
||
|
+ return function(...args) {
|
||
|
+ let [position, value] = args;
|
||
|
+ let isInsideTarget =
|
||
|
+ position === "afterbegin" || position === "beforeend";
|
||
|
+ if (shouldValidate(this, isInsideTarget)) {
|
||
|
+ let snippetData = getSnippetData(this, isInsideTarget);
|
||
|
+ if (snippetData) {
|
||
|
+ let parent = isInsideTarget ?
|
||
|
+ this :
|
||
|
+ $(this).parentNode;
|
||
|
+ let finalValue;
|
||
|
+ switch (property) {
|
||
|
+ case "insertAdjacentElement":
|
||
|
+ if (!checkShouldInsert(value, parent, property, snippetData))
|
||
|
+ return value;
|
||
|
+ break;
|
||
|
+
|
||
|
+ case "insertAdjacentHTML":
|
||
|
+ finalValue = checkHTML(value, parent, property, snippetData);
|
||
|
+ if (finalValue)
|
||
|
+ return call(origin, this, position, finalValue);
|
||
|
+
|
||
|
+ return;
|
||
|
+
|
||
|
+ case "insertAdjacentText":
|
||
|
+ if (!checkShouldInsert(value, parent, property, snippetData))
|
||
|
+ return;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return apply$2(origin, this, args);
|
||
|
+ };
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getInnerHTMLDescriptor(
|
||
|
+ target, property, shouldValidate, getSnippetData
|
||
|
+ ) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let {set: prevSetter} = desc;
|
||
|
+ if (!prevSetter)
|
||
|
+ return;
|
||
|
+
|
||
|
+ return {
|
||
|
+ set(htmlText) {
|
||
|
+ if (!shouldValidate(this))
|
||
|
+ return call(prevSetter, this, htmlText);
|
||
|
+
|
||
|
+ let snippetData = getSnippetData(this);
|
||
|
+ if (!snippetData)
|
||
|
+ return call(prevSetter, this, htmlText);
|
||
|
+ let finalValue = checkHTML(htmlText, this, property, snippetData);
|
||
|
+ if (finalValue)
|
||
|
+ return call(prevSetter, this, finalValue);
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+
|
||
|
+ function getTextContentDescriptor(
|
||
|
+ target, property, shouldValidate, getSnippetData
|
||
|
+ ) {
|
||
|
+ let desc = getOwnPropertyDescriptor(target, property) || {};
|
||
|
+ let {set: prevSetter} = desc;
|
||
|
+ if (!prevSetter)
|
||
|
+ return;
|
||
|
+
|
||
|
+ return {
|
||
|
+ set(domString) {
|
||
|
+ if (!shouldValidate(this))
|
||
|
+ return call(prevSetter, this, domString);
|
||
|
+
|
||
|
+ let snippetData = getSnippetData(this);
|
||
|
+ if (!snippetData)
|
||
|
+ return call(prevSetter, this, domString);
|
||
|
+ if (checkShouldInsert(domString, this, property, snippetData))
|
||
|
+ return call(prevSetter, this, domString);
|
||
|
+ }
|
||
|
+ };
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ $(window);
|
||
|
+
|
||
|
+ function raceWinner(name, lose) {
|
||
|
+
|
||
|
+ return noop;
|
||
|
+ }
|
||
|
+
|
||
|
+ const {Map: Map$4, MutationObserver, Object: Object$4, Set, WeakSet: WeakSet$1} = $(window);
|
||
|
+
|
||
|
+ let ElementProto = Element.prototype;
|
||
|
+ let {attachShadow} = ElementProto;
|
||
|
+
|
||
|
+ let hiddenShadowRoots = new WeakSet$1();
|
||
|
+ let searches = new Map$4();
|
||
|
+ let observer = null;
|
||
|
+
|
||
|
+ function hideIfShadowContains(search, selector = "*") {
|
||
|
+
|
||
|
+ let key = `${search}\\${selector}`;
|
||
|
+ if (!searches.has(key)) {
|
||
|
+ searches.set(key, [toRegExp(search), selector, raceWinner()
|
||
|
+ ]);
|
||
|
+ }
|
||
|
+
|
||
|
+ const debugLog = getDebugger("hide-if-shadow-contain");
|
||
|
+
|
||
|
+ if (!observer) {
|
||
|
+ observer = new MutationObserver(records => {
|
||
|
+ let visited = new Set();
|
||
|
+ for (let {target} of $(records)) {
|
||
|
+
|
||
|
+ let parent = $(target).parentNode;
|
||
|
+ while (parent)
|
||
|
+ [target, parent] = [parent, $(target).parentNode];
|
||
|
+
|
||
|
+ if (hiddenShadowRoots.has(target))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (visited.has(target))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ visited.add(target);
|
||
|
+ for (let [re, selfOrParent, win] of searches.values()) {
|
||
|
+ if (re.test($(target).textContent)) {
|
||
|
+ let closest = $(target.host).closest(selfOrParent);
|
||
|
+ if (closest) {
|
||
|
+ win();
|
||
|
+
|
||
|
+ $(target).appendChild(
|
||
|
+ document.createElement("style")
|
||
|
+ ).textContent = ":host {display: none !important}";
|
||
|
+
|
||
|
+ hideElement(closest);
|
||
|
+
|
||
|
+ hiddenShadowRoots.add(target);
|
||
|
+ debugLog("Hiding: ", closest, " for params: ", ...arguments);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ });
|
||
|
+
|
||
|
+ Object$4.defineProperty(ElementProto, "attachShadow", {
|
||
|
+
|
||
|
+ value: proxy(attachShadow, function() {
|
||
|
+
|
||
|
+ let root = apply$2(attachShadow, this, arguments);
|
||
|
+ debugLog("attachShadow is called for: ", root);
|
||
|
+
|
||
|
+ observer.observe(root, {
|
||
|
+ childList: true,
|
||
|
+ characterData: true,
|
||
|
+ subtree: true
|
||
|
+ });
|
||
|
+
|
||
|
+ return root;
|
||
|
+ })
|
||
|
+ });
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ const {Error: Error$4, JSON: JSON$2, Map: Map$3, Object: Object$3} = $(window);
|
||
|
+
|
||
|
+ let paths$1 = null;
|
||
|
+
|
||
|
+ function jsonOverride(rawOverridePaths, value,
|
||
|
+ rawNeedlePaths = "", filter = "") {
|
||
|
+ if (!rawOverridePaths)
|
||
|
+ throw new Error$4("[json-override snippet]: Missing paths to override.");
|
||
|
+
|
||
|
+ if (typeof value == "undefined")
|
||
|
+ throw new Error$4("[json-override snippet]: No value to override with.");
|
||
|
+
|
||
|
+ if (!paths$1) {
|
||
|
+ let debugLog = getDebugger("json-override");
|
||
|
+
|
||
|
+ let {parse} = JSON$2;
|
||
|
+ paths$1 = new Map$3();
|
||
|
+
|
||
|
+ Object$3.defineProperty(window.JSON, "parse", {
|
||
|
+ value: proxy(parse, function(str) {
|
||
|
+ let result = apply$2(parse, this, arguments);
|
||
|
+
|
||
|
+ for (let {prune, needle, filter: flt, value: val} of paths$1.values()) {
|
||
|
+ if (flt && !flt.test(str))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if ($(needle).some(path => !findOwner(result, path)))
|
||
|
+ return result;
|
||
|
+
|
||
|
+ for (let path of prune) {
|
||
|
+ let details = findOwner(result, path);
|
||
|
+ if (typeof details != "undefined") {
|
||
|
+ debugLog(`Found ${path} replaced it with ${val}`);
|
||
|
+ details[0][details[1]] = overrideValue(val);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return result;
|
||
|
+ })
|
||
|
+ });
|
||
|
+ debugLog("Wrapped JSON.parse for override");
|
||
|
+ }
|
||
|
+
|
||
|
+ paths$1.set(rawOverridePaths, {
|
||
|
+ prune: $(rawOverridePaths).split(/ +/),
|
||
|
+ needle: rawNeedlePaths.length ? $(rawNeedlePaths).split(/ +/) : [],
|
||
|
+ filter: filter ? toRegExp(filter) : null,
|
||
|
+ value
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Error: Error$3, JSON: JSON$1, Map: Map$2, Object: Object$2} = $(window);
|
||
|
+
|
||
|
+ let paths = null;
|
||
|
+
|
||
|
+ function jsonPrune(rawPrunePaths, rawNeedlePaths = "") {
|
||
|
+ if (!rawPrunePaths)
|
||
|
+ throw new Error$3("Missing paths to prune");
|
||
|
+
|
||
|
+ if (!paths) {
|
||
|
+ let debugLog = getDebugger("json-prune");
|
||
|
+
|
||
|
+ let {parse} = JSON$1;
|
||
|
+ paths = new Map$2();
|
||
|
+
|
||
|
+ Object$2.defineProperty(window.JSON, "parse", {
|
||
|
+ value: proxy(parse, function() {
|
||
|
+ let result = apply$2(parse, this, arguments);
|
||
|
+
|
||
|
+ for (let {prune, needle} of paths.values()) {
|
||
|
+ if ($(needle).some(path => !findOwner(result, path)))
|
||
|
+ return result;
|
||
|
+
|
||
|
+ for (let path of prune) {
|
||
|
+ let details = findOwner(result, path);
|
||
|
+ if (typeof details != "undefined") {
|
||
|
+ debugLog(`Found ${path} and deleted`);
|
||
|
+ delete details[0][details[1]];
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return result;
|
||
|
+ })
|
||
|
+ });
|
||
|
+ debugLog("Wrapped JSON.parse for prune");
|
||
|
+ }
|
||
|
+
|
||
|
+ paths.set(rawPrunePaths, {
|
||
|
+ prune: $(rawPrunePaths).split(/ +/),
|
||
|
+ needle: rawNeedlePaths.length ? $(rawNeedlePaths).split(/ +/) : []
|
||
|
+ });
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Error: Error$2} = $(window);
|
||
|
+
|
||
|
+ function overridePropertyRead(property, value) {
|
||
|
+ if (!property) {
|
||
|
+ throw new Error$2("[override-property-read snippet]: " +
|
||
|
+ "No property to override.");
|
||
|
+ }
|
||
|
+ if (typeof value === "undefined") {
|
||
|
+ throw new Error$2("[override-property-read snippet]: " +
|
||
|
+ "No value to override with.");
|
||
|
+ }
|
||
|
+
|
||
|
+ let debugLog = getDebugger("override-property-read");
|
||
|
+
|
||
|
+ let cValue = overrideValue(value);
|
||
|
+
|
||
|
+ let newGetter = () => {
|
||
|
+ debugLog(`${property} override done.`);
|
||
|
+ return cValue;
|
||
|
+ };
|
||
|
+
|
||
|
+ debugLog(`Overriding ${property}.`);
|
||
|
+
|
||
|
+ wrapPropertyAccess(window, property, {get: newGetter, set() {}});
|
||
|
+ }
|
||
|
+
|
||
|
+ let {Error: Error$1, Map: Map$1, Object: Object$1, console: console$1} = $(window);
|
||
|
+
|
||
|
+ let {toString} = Function.prototype;
|
||
|
+ let EventTargetProto = EventTarget.prototype;
|
||
|
+ let {addEventListener} = EventTargetProto;
|
||
|
+
|
||
|
+ let events = null;
|
||
|
+
|
||
|
+ function preventListener(event, eventHandler, selector) {
|
||
|
+ if (!event)
|
||
|
+ throw new Error$1("[prevent-listener snippet]: No event type.");
|
||
|
+
|
||
|
+ if (!events) {
|
||
|
+ events = new Map$1();
|
||
|
+
|
||
|
+ let debugLog = getDebugger("[prevent]");
|
||
|
+
|
||
|
+ Object$1.defineProperty(EventTargetProto, "addEventListener", {
|
||
|
+ value: proxy(addEventListener, function(type, listener) {
|
||
|
+ for (let {evt, handlers, selectors} of events.values()) {
|
||
|
+
|
||
|
+ if (!evt.test(type))
|
||
|
+ continue;
|
||
|
+
|
||
|
+ let isElement = this instanceof Element;
|
||
|
+
|
||
|
+ for (let i = 0; i < handlers.length; i++) {
|
||
|
+ let handler = handlers[i];
|
||
|
+ let sel = selectors[i];
|
||
|
+
|
||
|
+ let handlerMatch = () => handler.test(
|
||
|
+ call(
|
||
|
+ toString,
|
||
|
+ typeof listener === "function" ?
|
||
|
+ listener : listener.handleEvent
|
||
|
+ )
|
||
|
+ );
|
||
|
+
|
||
|
+ if (
|
||
|
+ (handler && !handlerMatch()) ||
|
||
|
+ (sel && !(isElement && $(this).matches(sel)))
|
||
|
+ )
|
||
|
+ continue;
|
||
|
+
|
||
|
+ if (debug()) {
|
||
|
+ console$1.groupCollapsed("DEBUG [prevent] was successful");
|
||
|
+ debugLog(`type: ${type} matching ${evt}`);
|
||
|
+ debugLog("handler:", listener);
|
||
|
+ if (handler)
|
||
|
+ debugLog(`matching ${handler}`);
|
||
|
+ if (sel)
|
||
|
+ debugLog("on element: ", this, ` matching ${sel}`);
|
||
|
+ debugLog("was prevented from being added");
|
||
|
+ console$1.groupEnd();
|
||
|
+ }
|
||
|
+ return;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return apply$2(addEventListener, this, arguments);
|
||
|
+ })
|
||
|
+ });
|
||
|
+
|
||
|
+ debugLog("Wrapped addEventListener");
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!events.has(event))
|
||
|
+ events.set(event, {evt: toRegExp(event), handlers: [], selectors: []});
|
||
|
+
|
||
|
+ let {handlers, selectors} = events.get(event);
|
||
|
+
|
||
|
+ handlers.push(eventHandler ? toRegExp(eventHandler) : null);
|
||
|
+ selectors.push(selector);
|
||
|
+ }
|
||
|
+
|
||
|
+ let {URL, fetch} = $(window);
|
||
|
+
|
||
|
+ let {delete: deleteParam, has: hasParam} = caller(URLSearchParams.prototype);
|
||
|
+
|
||
|
+ let parameters;
|
||
|
+
|
||
|
+ function stripFetchQueryParameter(name, urlPattern = null) {
|
||
|
+ const debugLog = getDebugger("strip-fetch-query-parameter");
|
||
|
+
|
||
|
+ if (!parameters) {
|
||
|
+ parameters = new Map();
|
||
|
+ window.fetch = proxy(fetch, (...args) => {
|
||
|
+ let [source] = args;
|
||
|
+ if (typeof source === "string") {
|
||
|
+ let url = new URL(source);
|
||
|
+ for (let [key, reg] of parameters) {
|
||
|
+ if (!reg || reg.test(source)) {
|
||
|
+ if (hasParam(url.searchParams, key)) {
|
||
|
+ debugLog(`${key} has been stripped from url ${source}`);
|
||
|
+ deleteParam(url.searchParams, key);
|
||
|
+ args[0] = url.href;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return apply$2(fetch, self, args);
|
||
|
+ });
|
||
|
+ }
|
||
|
+ parameters.set(name, urlPattern && toRegExp(urlPattern));
|
||
|
+ }
|
||
|
+
|
||
|
+ function trace(...args) {
|
||
|
+
|
||
|
+ apply$2(log, null, args);
|
||
|
+ }
|
||
|
+
|
||
|
+ const snippets = {
|
||
|
+ "abort-current-inline-script": abortCurrentInlineScript,
|
||
|
+ "abort-on-iframe-property-read": abortOnIframePropertyRead,
|
||
|
+ "abort-on-iframe-property-write": abortOnIframePropertyWrite,
|
||
|
+ "abort-on-property-read": abortOnPropertyRead,
|
||
|
+ "abort-on-property-write": abortOnPropertyWrite,
|
||
|
+ "cookie-remover": cookieRemover,
|
||
|
+ "debug": setDebug,
|
||
|
+ "freeze-element": freezeElement,
|
||
|
+ "hide-if-shadow-contains": hideIfShadowContains,
|
||
|
+ "json-override": jsonOverride,
|
||
|
+ "json-prune": jsonPrune,
|
||
|
+ "override-property-read": overridePropertyRead,
|
||
|
+ "prevent-listener": preventListener,
|
||
|
+ "strip-fetch-query-parameter": stripFetchQueryParameter,
|
||
|
+ "trace": trace
|
||
|
+ };
|
||
|
+ let context;
|
||
|
+ for (const [name, ...args] of filters) {
|
||
|
+ if (snippets.hasOwnProperty(name)) {
|
||
|
+ try { context = snippets[name].apply(context, args); }
|
||
|
+ catch (error) { console.error(error); }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ context = void 0;
|
||
|
+};
|
||
|
+const graph = new Map([["abort-current-inline-script",null],["abort-on-iframe-property-read",null],["abort-on-iframe-property-write",null],["abort-on-property-read",null],["abort-on-property-write",null],["cookie-remover",null],["debug",null],["freeze-element",null],["hide-if-shadow-contains",null],["json-override",null],["json-prune",null],["override-property-read",null],["prevent-listener",null],["strip-fetch-query-parameter",null],["trace",null]]);
|
||
|
+callback.get = snippet => graph.get(snippet);
|
||
|
+callback.has = snippet => graph.has(snippet);
|
||
|
+
|
||
|
+ if (t.every(([name]) => !callback.has(name))) return;
|
||
|
+ const append = () => {
|
||
|
+ URL.revokeObjectURL(
|
||
|
+ Object.assign(
|
||
|
+ document.documentElement.appendChild(document.createElement("script")),
|
||
|
+ {async: false, src: URL.createObjectURL(new Blob([
|
||
|
+ "(" + callback + ")(..." + JSON.stringify([e, ...t]) + ")"
|
||
|
+ ]))}
|
||
|
+ ).src
|
||
|
+ );
|
||
|
+ };
|
||
|
+ try { append(); }
|
||
|
+ catch (_) {
|
||
|
+ document.addEventListener("readystatechange", append, {once:true});
|
||
|
+ }
|
||
|
+}
|
||
|
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
|
||
|
--- a/third_party/blink/renderer/core/css/style_engine.cc
|
||
|
+++ b/third_party/blink/renderer/core/css/style_engine.cc
|
||
|
@@ -623,6 +623,14 @@ void StyleEngine::UpdateActiveStyleSheetsInShadow(
|
||
|
}
|
||
|
}
|
||
|
|
||
|
+bool StyleEngine::ActiveUserStyleSheetsContainsKey(const StyleSheetKey& injection_key) {
|
||
|
+ for (auto& sheet : injected_user_style_sheets_)
|
||
|
+ if (sheet.first == injection_key)
|
||
|
+ return true;
|
||
|
+
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
void StyleEngine::UpdateActiveUserStyleSheets() {
|
||
|
DCHECK(user_style_dirty_);
|
||
|
|
||
|
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
|
||
|
--- a/third_party/blink/renderer/core/css/style_engine.h
|
||
|
+++ b/third_party/blink/renderer/core/css/style_engine.h
|
||
|
@@ -243,6 +243,7 @@ class CORE_EXPORT StyleEngine final : public GarbageCollected<StyleEngine>,
|
||
|
const ComputedStyle* embedder_style) const;
|
||
|
void ViewportStyleSettingChanged();
|
||
|
|
||
|
+ bool ActiveUserStyleSheetsContainsKey(const StyleSheetKey& injection_key);
|
||
|
void InjectSheet(const StyleSheetKey&,
|
||
|
StyleSheetContents*,
|
||
|
WebCssOrigin = WebCssOrigin::kAuthor);
|
||
|
diff --git a/third_party/blink/renderer/core/exported/web_document.cc b/third_party/blink/renderer/core/exported/web_document.cc
|
||
|
--- a/third_party/blink/renderer/core/exported/web_document.cc
|
||
|
+++ b/third_party/blink/renderer/core/exported/web_document.cc
|
||
|
@@ -274,6 +274,16 @@ WebStyleSheetKey WebDocument::InsertAbpElemhideStylesheet(
|
||
|
Document* document = Unwrap<Document>();
|
||
|
DCHECK(document);
|
||
|
|
||
|
+ const WebStyleSheetKey& injection_key =
|
||
|
+ key && !key->IsNull() ? *key : WebString::FromUTF8("abp");
|
||
|
+ DCHECK(!injection_key.IsEmpty());
|
||
|
+
|
||
|
+ // Check if the css in already present
|
||
|
+ if (document->GetStyleEngine().ActiveUserStyleSheetsContainsKey(injection_key)) {
|
||
|
+ DLOG(WARNING) << "[eyeo] Skip add css as already in collection";
|
||
|
+ return injection_key;
|
||
|
+ }
|
||
|
+
|
||
|
auto* parsed_sheet = MakeGarbageCollected<StyleSheetContents>(
|
||
|
MakeGarbageCollected<CSSParserContext>(*document));
|
||
|
parsed_sheet->ParseString(source_code);
|
||
|
@@ -283,7 +293,7 @@ WebStyleSheetKey WebDocument::InsertAbpElemhideStylesheet(
|
||
|
if (IsValidAbpRule(parsed_sheet->RuleAt(n)))
|
||
|
++n;
|
||
|
else {
|
||
|
- parsed_sheet->SetMutable();
|
||
|
+ parsed_sheet->StartMutation();
|
||
|
parsed_sheet->WrapperDeleteRule(n);
|
||
|
LOG(WARNING) << "[eyeo] Broken rule";
|
||
|
}
|
||
|
@@ -295,9 +305,6 @@ WebStyleSheetKey WebDocument::InsertAbpElemhideStylesheet(
|
||
|
{SchedulingPolicy::DisableBackForwardCache()});
|
||
|
}
|
||
|
|
||
|
- const WebStyleSheetKey& injection_key =
|
||
|
- key && !key->IsNull() ? *key : GenerateStyleSheetKey();
|
||
|
- DCHECK(!injection_key.IsEmpty());
|
||
|
document->GetStyleEngine().InjectSheet(injection_key, parsed_sheet, origin);
|
||
|
return injection_key;
|
||
|
}
|
||
|
diff --git a/tools/typescript/definitions/adblock_private.d.ts b/tools/typescript/definitions/adblock_private.d.ts
|
||
|
--- a/tools/typescript/definitions/adblock_private.d.ts
|
||
|
+++ b/tools/typescript/definitions/adblock_private.d.ts
|
||
|
@@ -33,8 +33,22 @@ declare global {
|
||
|
title: string;
|
||
|
current_version: string;
|
||
|
last_installation_time: string;
|
||
|
+ download_error_count: number;
|
||
|
+ download_success_count: number;
|
||
|
}
|
||
|
|
||
|
+ export function startUpdate(): void;
|
||
|
+
|
||
|
+ export function setPrivilegedFiltersEnabled(
|
||
|
+ enabled: boolean,
|
||
|
+ ): void;
|
||
|
+
|
||
|
+ export function isPrivilegedFiltersEnabled(
|
||
|
+ callback: (
|
||
|
+ result: boolean,
|
||
|
+ ) => void,
|
||
|
+ ): void;
|
||
|
+
|
||
|
export interface SessionStatsEntry {
|
||
|
url: string;
|
||
|
count: number;
|
||
|
--
|
||
|
2.25.1
|