From: chromium-sdk Date: Thu, 12 Oct 2023 14:46:09 +0200 Subject: eyeo Browser Ad filtering Solution: Extension API Module Based on Chromium 118.0.5993.48 Pre-requisites: eyeo Browser Ad filtering Solution: Base Module --- chrome/browser/extensions/BUILD.gn | 9 + chrome/browser/extensions/api/BUILD.gn | 5 + .../adblock_private/adblock_private_api.cc | 674 ++++++++++++ .../api/adblock_private/adblock_private_api.h | 328 ++++++ .../adblock_private_apitest.cc | 984 ++++++++++++++++++ ...browser_context_keyed_service_factories.cc | 8 + .../eyeo_filtering_private_api.cc | 772 ++++++++++++++ .../eyeo_filtering_private_api.h | 360 +++++++ .../eyeo_filtering_private_apitest.cc | 161 +++ .../api/settings_private/prefs_util.cc | 19 + ...browser_context_keyed_service_factories.cc | 4 + .../extension_function_registration_test.cc | 10 + .../common/extensions/api/_api_features.json | 22 + .../extensions/api/_permission_features.json | 12 + .../common/extensions/api/adblock_private.idl | 174 ++++ chrome/common/extensions/api/api_sources.gni | 6 + .../extensions/api/eyeo_filtering_private.idl | 201 ++++ .../permissions/chrome_api_permissions.cc | 8 + .../permissions/permission_set_unittest.cc | 8 + chrome/test/BUILD.gn | 6 + .../api_test/adblock_private/empty.js | 14 + .../api_test/adblock_private/main.html | 29 + .../api_test/adblock_private/manifest.json | 32 + .../api_test/adblock_private/some-popup.html | 24 + .../api_test/adblock_private/test.html | 25 + .../api_test/adblock_private/test.js | 538 ++++++++++ .../api_test/eyeo_filtering_private/empty.js | 14 + .../api_test/eyeo_filtering_private/main.html | 29 + .../eyeo_filtering_private/manifest.json | 31 + .../api_test/eyeo_filtering_private/test.js | 460 ++++++++ .../browser/extension_event_histogram_value.h | 7 +- .../common/mojom/api_permission_id.mojom | 6 + tools/metrics/histograms/enums.xml | 7 +- .../definitions/adblock_private.d.ts | 172 +++ .../definitions/eyeo_filtering_private.d.ts | 249 +++++ 35 files changed, 5406 insertions(+), 2 deletions(-) create mode 100644 chrome/browser/extensions/api/adblock_private/adblock_private_api.cc create mode 100644 chrome/browser/extensions/api/adblock_private/adblock_private_api.h create mode 100644 chrome/browser/extensions/api/adblock_private/adblock_private_apitest.cc create mode 100644 chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.cc create mode 100644 chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.h create mode 100644 chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_apitest.cc create mode 100644 chrome/common/extensions/api/adblock_private.idl create mode 100644 chrome/common/extensions/api/eyeo_filtering_private.idl create mode 100644 chrome/test/data/extensions/api_test/adblock_private/empty.js create mode 100644 chrome/test/data/extensions/api_test/adblock_private/main.html create mode 100644 chrome/test/data/extensions/api_test/adblock_private/manifest.json create mode 100644 chrome/test/data/extensions/api_test/adblock_private/some-popup.html create mode 100644 chrome/test/data/extensions/api_test/adblock_private/test.html create mode 100644 chrome/test/data/extensions/api_test/adblock_private/test.js create mode 100644 chrome/test/data/extensions/api_test/eyeo_filtering_private/empty.js create mode 100644 chrome/test/data/extensions/api_test/eyeo_filtering_private/main.html create mode 100644 chrome/test/data/extensions/api_test/eyeo_filtering_private/manifest.json create mode 100644 chrome/test/data/extensions/api_test/eyeo_filtering_private/test.js create mode 100644 tools/typescript/definitions/adblock_private.d.ts create mode 100644 tools/typescript/definitions/eyeo_filtering_private.d.ts diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn @@ -1,6 +1,10 @@ # Copyright 2014 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# +# This source code is a part of eyeo Chromium SDK. +# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file. + import("//build/config/chromebox_for_meetings/buildflags.gni") import("//build/config/chromeos/ui_mode.gni") @@ -48,6 +52,8 @@ static_library("extensions") { # here. "api/activity_log_private/activity_log_private_api.cc", "api/activity_log_private/activity_log_private_api.h", + "api/adblock_private/adblock_private_api.cc", + "api/adblock_private/adblock_private_api.h", "api/api_browser_context_keyed_service_factories.cc", "api/api_browser_context_keyed_service_factories.h", "api/autofill_private/autofill_private_api.cc", @@ -149,6 +155,8 @@ static_library("extensions") { "api/extension_action/extension_action_api.h", "api/extension_action/extension_page_actions_api_constants.cc", "api/extension_action/extension_page_actions_api_constants.h", + "api/eyeo_filtering_private/eyeo_filtering_private_api.cc", + "api/eyeo_filtering_private/eyeo_filtering_private_api.h", "api/favicon/favicon_util.cc", "api/favicon/favicon_util.h", "api/feedback_private/chrome_feedback_private_delegate.cc", @@ -827,6 +835,7 @@ static_library("extensions") { "//chrome/browser/safe_browsing:metrics_collector", "//chrome/browser/ui/tabs:tab_enums", "//chrome/browser/web_applications", + "//components/adblock/content:browser", "//components/cbor:cbor", "//components/commerce/core:pref_names", "//components/device_reauth", diff --git a/chrome/browser/extensions/api/BUILD.gn b/chrome/browser/extensions/api/BUILD.gn --- a/chrome/browser/extensions/api/BUILD.gn +++ b/chrome/browser/extensions/api/BUILD.gn @@ -1,6 +1,10 @@ # Copyright 2018 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# +# This source code is a part of eyeo Chromium SDK. +# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file. + import("//chrome/common/extensions/api/api_sources.gni") import("//chrome/common/features.gni") @@ -75,6 +79,7 @@ function_registration("api_registration") { # include generated headers from these targets. # TODO(brettw) this should be made unnecessary if possible. "//chrome/common/extensions/api", + "//components/adblock/content:browser", "//components/sync", "//skia", "//third_party/metrics_proto", diff --git a/chrome/browser/extensions/api/adblock_private/adblock_private_api.cc b/chrome/browser/extensions/api/adblock_private/adblock_private_api.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/extensions/api/adblock_private/adblock_private_api.cc @@ -0,0 +1,674 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . + +#include "chrome/browser/extensions/api/adblock_private/adblock_private_api.h" + +#include "base/containers/flat_map.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "base/time/time_to_iso8601.h" +#include "base/values.h" +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/adblock/session_stats_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/common/extensions/api/tabs.h" +#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/session_stats.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "components/sessions/core/session_id.h" +#include "content/public/browser/web_contents.h" +#include "url/gurl.h" + +namespace extensions { + +namespace { + +enum class SubscriptionAction { kInstall, kUninstall }; + +std::string RunSubscriptionAction(SubscriptionAction action, + content::BrowserContext* browser_context, + const GURL& url) { + if (!url.is_valid()) { + return "Invalid URL"; + } + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext(browser_context) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + switch (action) { + case SubscriptionAction::kInstall: + adblock_configuration->AddFilterList(url); + break; + case SubscriptionAction::kUninstall: + adblock_configuration->RemoveFilterList(url); + break; + default: + NOTREACHED(); + } + + return {}; +} + +std::vector CopySessionsStats( + const std::map& source) { + std::vector result; + for (auto& entry : source) { + api::adblock_private::SessionStatsEntry js_entry; + js_entry.url = entry.first.spec(); + js_entry.count = entry.second; + result.emplace_back(std::move(js_entry)); + } + 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 CopySubscriptions( + const std::vector> + current_subscriptions) { + std::vector result; + for (auto& sub : current_subscriptions) { + api::adblock_private::Subscription js_sub; + js_sub.url = sub->GetSourceUrl().spec(); + js_sub.title = sub->GetTitle(); + js_sub.current_version = sub->GetCurrentVersion(); + js_sub.installation_state = + SubscriptionInstallationStateToString(sub->GetInstallationState()); + js_sub.last_installation_time = + base::TimeToISO8601(sub->GetInstallationTime()); + result.emplace_back(std::move(js_sub)); + } + return result; +} + +} // namespace + +template <> +void BrowserContextKeyedAPIFactory< + AdblockPrivateAPI>::DeclareFactoryDependencies() { + DependsOn(adblock::SubscriptionServiceFactory::GetInstance()); + DependsOn(adblock::ResourceClassificationRunnerFactory::GetInstance()); + DependsOn(adblock::SessionStatsFactory::GetInstance()); +} + +// static +BrowserContextKeyedAPIFactory* +AdblockPrivateAPI::GetFactoryInstance() { + static base::NoDestructor> + instance; + return instance.get(); +} + +class AdblockPrivateAPI::AdblockAPIEventRouter + : public adblock::ResourceClassificationRunner::Observer, + public adblock::SubscriptionService::SubscriptionObserver, + public adblock::FilteringConfiguration::Observer { + public: + explicit AdblockAPIEventRouter(content::BrowserContext* context) + : context_(context) { + adblock::ResourceClassificationRunnerFactory::GetForBrowserContext(context_) + ->AddObserver(this); + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext(context_); + subscription_service->AddObserver(this); + subscription_service->GetAdblockFilteringConfiguration()->AddObserver(this); + } + + ~AdblockAPIEventRouter() override { + adblock::ResourceClassificationRunnerFactory::GetForBrowserContext(context_) + ->RemoveObserver(this); + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext(context_); + subscription_service->RemoveObserver(this); + subscription_service->GetAdblockFilteringConfiguration()->RemoveObserver( + this); + } + + // adblock::ResourceClassificationRunner::Observer: + void OnAdMatched(const GURL& url, + adblock::FilterMatchResult match_result, + const std::vector& parent_frame_urls, + adblock::ContentType content_type, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + std::unique_ptr event; + api::adblock_private::AdInfo info = CreateAdInfoObject( + url, subscription, configuration_name, render_frame_host); + info.parent_frame_urls = adblock::utils::ConvertURLs(parent_frame_urls); + info.content_type = adblock::ContentTypeToString(content_type); + + if (match_result == adblock::FilterMatchResult::kBlockRule) { + event = std::make_unique( + events::EYEO_EVENT, api::adblock_private::OnAdBlocked::kEventName, + api::adblock_private::OnAdBlocked::Create(info)); + } else { + DCHECK(match_result == adblock::FilterMatchResult::kAllowRule); + event = std::make_unique( + events::EYEO_EVENT, api::adblock_private::OnAdAllowed::kEventName, + api::adblock_private::OnAdAllowed::Create(info)); + } + + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnPageAllowed(const GURL& url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + api::adblock_private::AdInfo info = CreateAdInfoObject( + url, subscription, configuration_name, render_frame_host); + info.parent_frame_urls = std::vector{}; + info.content_type = ""; + + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, api::adblock_private::OnPageAllowed::kEventName, + api::adblock_private::OnPageAllowed::Create(info)); + + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnPopupMatched(const GURL& url, + adblock::FilterMatchResult match_result, + const GURL& opener_url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + std::unique_ptr event; + api::adblock_private::AdInfo info = CreateAdInfoObject( + url, subscription, configuration_name, render_frame_host); + info.parent_frame_urls = std::vector{opener_url.spec()}; + info.content_type = ""; + + if (match_result == adblock::FilterMatchResult::kBlockRule) { + event = std::make_unique( + events::EYEO_EVENT, api::adblock_private::OnPopupBlocked::kEventName, + api::adblock_private::OnPopupBlocked::Create(info)); + } else { + DCHECK(match_result == adblock::FilterMatchResult::kAllowRule); + event = std::make_unique( + events::EYEO_EVENT, api::adblock_private::OnPopupAllowed::kEventName, + api::adblock_private::OnPopupAllowed::Create(info)); + } + + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + // adblock::SubscriptionService::SubscriptionObserver: + void OnSubscriptionInstalled(const GURL& url) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::adblock_private::OnSubscriptionUpdated::kEventName, + api::adblock_private::OnSubscriptionUpdated::Create(url.spec())); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + // adblock::FilteringConfiguration::Observer: + void OnEnabledStateChanged(adblock::FilteringConfiguration* config) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::adblock_private::OnEnabledStateChanged::kEventName, + api::adblock_private::OnEnabledStateChanged::Create()); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnFilterListsChanged(adblock::FilteringConfiguration* config) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::adblock_private::OnFilterListsChanged::kEventName, + api::adblock_private::OnFilterListsChanged::Create()); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnAllowedDomainsChanged( + adblock::FilteringConfiguration* config) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::adblock_private::OnAllowedDomainsChanged::kEventName, + api::adblock_private::OnAllowedDomainsChanged::Create()); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnCustomFiltersChanged( + adblock::FilteringConfiguration* config) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::adblock_private::OnCustomFiltersChanged::kEventName, + api::adblock_private::OnCustomFiltersChanged::Create()); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + private: + api::adblock_private::AdInfo CreateAdInfoObject( + const GURL& url, + const GURL& subscription, + const std::string& configuration_name, + content::RenderFrameHost* render_frame_host) { + DCHECK(render_frame_host); + api::adblock_private::AdInfo info; + info.url = url.spec(); + info.subscription = subscription.spec(); + info.configuration_name = configuration_name; + info.tab_id = api::tabs::TAB_ID_NONE; + info.window_id = SessionID::InvalidValue().id(); + const content::WebContents* wc = + content::WebContents::FromRenderFrameHost(render_frame_host); + if (wc) { + info.tab_id = ExtensionTabUtil::GetTabId(wc); + info.window_id = ExtensionTabUtil::GetWindowIdOfTab(wc); + } + return info; + } + + raw_ptr context_; +}; + +void AdblockPrivateAPI::Shutdown() { + // EventRouter can be null in tests + if (EventRouter::Get(context_)) { + EventRouter::Get(context_)->UnregisterObserver(this); + } + event_router_.reset(); +} + +// static +AdblockPrivateAPI* AdblockPrivateAPI::Get(content::BrowserContext* context) { + return GetFactoryInstance()->Get(context); +} + +AdblockPrivateAPI::AdblockPrivateAPI(content::BrowserContext* context) + : context_(context) { + // EventRouter can be null in tests + if (EventRouter::Get(context_)) { + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnAdAllowed::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnAdBlocked::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnPageAllowed::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnPopupAllowed::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnPopupBlocked::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnSubscriptionUpdated::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnEnabledStateChanged::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnFilterListsChanged::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnAllowedDomainsChanged::kEventName); + EventRouter::Get(context_)->RegisterObserver( + this, api::adblock_private::OnCustomFiltersChanged::kEventName); + } + // Make sure SessionStats is created so it will start collectings stats + adblock::SessionStatsFactory::GetForBrowserContext(context); +} + +AdblockPrivateAPI::~AdblockPrivateAPI() = default; + +void AdblockPrivateAPI::OnListenerAdded( + const extensions::EventListenerInfo& details) { + event_router_ = + std::make_unique(context_); + EventRouter::Get(context_)->UnregisterObserver(this); +} + +namespace api { + +AdblockPrivateSetEnabledFunction::AdblockPrivateSetEnabledFunction() {} + +AdblockPrivateSetEnabledFunction::~AdblockPrivateSetEnabledFunction() {} + +ExtensionFunction::ResponseAction AdblockPrivateSetEnabledFunction::Run() { + absl::optional params = + api::adblock_private::SetEnabled::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + adblock_configuration->SetEnabled(params->enabled); + return RespondNow(NoArguments()); +} + +AdblockPrivateIsEnabledFunction::AdblockPrivateIsEnabledFunction() {} + +AdblockPrivateIsEnabledFunction::~AdblockPrivateIsEnabledFunction() {} + +ExtensionFunction::ResponseAction AdblockPrivateIsEnabledFunction::Run() { + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + return RespondNow( + ArgumentList(api::adblock_private::IsEnabled::Results::Create( + adblock_configuration->IsEnabled()))); +} + +AdblockPrivateSetAcceptableAdsEnabledFunction:: + AdblockPrivateSetAcceptableAdsEnabledFunction() {} + +AdblockPrivateSetAcceptableAdsEnabledFunction:: + ~AdblockPrivateSetAcceptableAdsEnabledFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateSetAcceptableAdsEnabledFunction::Run() { + absl::optional params = + api::adblock_private::SetAcceptableAdsEnabled::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + if (params->enabled) { + adblock_configuration->AddFilterList(adblock::AcceptableAdsUrl()); + } else { + adblock_configuration->RemoveFilterList(adblock::AcceptableAdsUrl()); + } + + return RespondNow(NoArguments()); +} + +AdblockPrivateIsAcceptableAdsEnabledFunction:: + AdblockPrivateIsAcceptableAdsEnabledFunction() {} + +AdblockPrivateIsAcceptableAdsEnabledFunction:: + ~AdblockPrivateIsAcceptableAdsEnabledFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateIsAcceptableAdsEnabledFunction::Run() { + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + return RespondNow(ArgumentList( + api::adblock_private::IsAcceptableAdsEnabled::Results::Create( + base::ranges::any_of(adblock_configuration->GetFilterLists(), + [&](const auto& url) { + return url == adblock::AcceptableAdsUrl(); + })))); +} + +AdblockPrivateGetBuiltInSubscriptionsFunction:: + AdblockPrivateGetBuiltInSubscriptionsFunction() {} + +AdblockPrivateGetBuiltInSubscriptionsFunction:: + ~AdblockPrivateGetBuiltInSubscriptionsFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateGetBuiltInSubscriptionsFunction::Run() { + auto recommended = adblock::config::GetKnownSubscriptions(); + std::vector result; + for (auto& recommended_one : recommended) { + if (recommended_one.ui_visibility == + adblock::SubscriptionUiVisibility::Visible) { + api::adblock_private::BuiltInSubscription js_recommended; + js_recommended.url = recommended_one.url.spec(); + js_recommended.title = recommended_one.title; + js_recommended.languages = recommended_one.languages; + result.emplace_back(std::move(js_recommended)); + } + } + return RespondNow(ArgumentList( + api::adblock_private::GetBuiltInSubscriptions::Results::Create(result))); +} + +AdblockPrivateInstallSubscriptionFunction:: + AdblockPrivateInstallSubscriptionFunction() {} + +AdblockPrivateInstallSubscriptionFunction:: + ~AdblockPrivateInstallSubscriptionFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateInstallSubscriptionFunction::Run() { + absl::optional params = + api::adblock_private::InstallSubscription::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + auto url = GURL{params->url}; + auto status = RunSubscriptionAction(SubscriptionAction::kInstall, + browser_context(), url); + if (!status.empty()) { + return RespondNow(Error(status)); + } + + return RespondNow(NoArguments()); +} + +AdblockPrivateUninstallSubscriptionFunction:: + AdblockPrivateUninstallSubscriptionFunction() {} + +AdblockPrivateUninstallSubscriptionFunction:: + ~AdblockPrivateUninstallSubscriptionFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateUninstallSubscriptionFunction::Run() { + absl::optional params = + api::adblock_private::UninstallSubscription::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + auto url = GURL{params->url}; + auto status = RunSubscriptionAction(SubscriptionAction::kUninstall, + browser_context(), url); + if (!status.empty()) { + return RespondNow(Error(status)); + } + + return RespondNow(NoArguments()); +} + +AdblockPrivateGetInstalledSubscriptionsFunction:: + AdblockPrivateGetInstalledSubscriptionsFunction() {} + +AdblockPrivateGetInstalledSubscriptionsFunction:: + ~AdblockPrivateGetInstalledSubscriptionsFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateGetInstalledSubscriptionsFunction::Run() { + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()); + auto* adblock_configuration = + subscription_service->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + return RespondNow(ArgumentList( + api::adblock_private::GetInstalledSubscriptions::Results::Create( + CopySubscriptions(subscription_service->GetCurrentSubscriptions( + subscription_service->GetAdblockFilteringConfiguration()))))); +} + +AdblockPrivateAddAllowedDomainFunction:: + AdblockPrivateAddAllowedDomainFunction() {} + +AdblockPrivateAddAllowedDomainFunction:: + ~AdblockPrivateAddAllowedDomainFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateAddAllowedDomainFunction::Run() { + absl::optional params = + api::adblock_private::AddAllowedDomain::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + adblock_configuration->AddAllowedDomain(params->domain); + return RespondNow(NoArguments()); +} + +AdblockPrivateRemoveAllowedDomainFunction:: + AdblockPrivateRemoveAllowedDomainFunction() {} + +AdblockPrivateRemoveAllowedDomainFunction:: + ~AdblockPrivateRemoveAllowedDomainFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateRemoveAllowedDomainFunction::Run() { + absl::optional params = + api::adblock_private::RemoveAllowedDomain::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + adblock_configuration->RemoveAllowedDomain(params->domain); + + return RespondNow(NoArguments()); +} + +AdblockPrivateGetAllowedDomainsFunction:: + AdblockPrivateGetAllowedDomainsFunction() {} + +AdblockPrivateGetAllowedDomainsFunction:: + ~AdblockPrivateGetAllowedDomainsFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateGetAllowedDomainsFunction::Run() { + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + return RespondNow( + ArgumentList(api::adblock_private::GetAllowedDomains::Results::Create( + adblock_configuration->GetAllowedDomains()))); +} + +AdblockPrivateAddCustomFilterFunction::AdblockPrivateAddCustomFilterFunction() { +} + +AdblockPrivateAddCustomFilterFunction:: + ~AdblockPrivateAddCustomFilterFunction() {} + +ExtensionFunction::ResponseAction AdblockPrivateAddCustomFilterFunction::Run() { + absl::optional params = + api::adblock_private::AddCustomFilter::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + adblock_configuration->AddCustomFilter(params->filter); + return RespondNow(NoArguments()); +} + +AdblockPrivateRemoveCustomFilterFunction:: + AdblockPrivateRemoveCustomFilterFunction() {} + +AdblockPrivateRemoveCustomFilterFunction:: + ~AdblockPrivateRemoveCustomFilterFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateGetCustomFiltersFunction::Run() { + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + return RespondNow( + ArgumentList(api::adblock_private::GetCustomFilters::Results::Create( + adblock_configuration->GetCustomFilters()))); +} + +AdblockPrivateGetCustomFiltersFunction:: + AdblockPrivateGetCustomFiltersFunction() {} + +AdblockPrivateGetCustomFiltersFunction:: + ~AdblockPrivateGetCustomFiltersFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateRemoveCustomFilterFunction::Run() { + absl::optional params = + api::adblock_private::RemoveCustomFilter::Params::Create(args()); + EXTENSION_FUNCTION_VALIDATE(params); + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()) + ->GetAdblockFilteringConfiguration(); + DCHECK(adblock_configuration) + << "adblock_private expects \"adblock\" configuration"; + adblock_configuration->RemoveCustomFilter(params->filter); + return RespondNow(NoArguments()); +} + +AdblockPrivateGetSessionAllowedAdsCountFunction:: + AdblockPrivateGetSessionAllowedAdsCountFunction() {} + +AdblockPrivateGetSessionAllowedAdsCountFunction:: + ~AdblockPrivateGetSessionAllowedAdsCountFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateGetSessionAllowedAdsCountFunction::Run() { + auto* session_stats_ = + adblock::SessionStatsFactory::GetForBrowserContext(browser_context()); + return RespondNow(ArgumentList( + api::adblock_private::GetSessionAllowedAdsCount::Results::Create( + CopySessionsStats(session_stats_->GetSessionAllowedAdsCount())))); +} + +AdblockPrivateGetSessionBlockedAdsCountFunction:: + AdblockPrivateGetSessionBlockedAdsCountFunction() {} + +AdblockPrivateGetSessionBlockedAdsCountFunction:: + ~AdblockPrivateGetSessionBlockedAdsCountFunction() {} + +ExtensionFunction::ResponseAction +AdblockPrivateGetSessionBlockedAdsCountFunction::Run() { + auto* session_stats_ = + adblock::SessionStatsFactory::GetForBrowserContext(browser_context()); + return RespondNow(ArgumentList( + api::adblock_private::GetSessionAllowedAdsCount::Results::Create( + CopySessionsStats(session_stats_->GetSessionBlockedAdsCount())))); +} + +} // namespace api +} // namespace extensions diff --git a/chrome/browser/extensions/api/adblock_private/adblock_private_api.h b/chrome/browser/extensions/api/adblock_private/adblock_private_api.h new file mode 100644 --- /dev/null +++ b/chrome/browser/extensions/api/adblock_private/adblock_private_api.h @@ -0,0 +1,328 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . + +#ifndef CHROME_BROWSER_EXTENSIONS_API_ADBLOCK_PRIVATE_ADBLOCK_PRIVATE_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_ADBLOCK_PRIVATE_ADBLOCK_PRIVATE_API_H_ + +#include "base/memory/raw_ptr.h" +#include "chrome/common/extensions/api/adblock_private.h" +#include "extensions/browser/browser_context_keyed_api_factory.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_function.h" + +class Profile; + +namespace extensions { + +class AdblockPrivateAPI : public BrowserContextKeyedAPI, + public EventRouter::Observer { + public: + static BrowserContextKeyedAPIFactory* GetFactoryInstance(); + + static AdblockPrivateAPI* Get(content::BrowserContext* context); + + explicit AdblockPrivateAPI(content::BrowserContext* context); + ~AdblockPrivateAPI() override; + friend class BrowserContextKeyedAPIFactory; + + // BrowserContextKeyedAPI implementation. + static const char* service_name() { return "AdblockPrivateAPI"; } + static const bool kServiceRedirectedInIncognito = true; + static const bool kServiceIsCreatedWithBrowserContext = true; + void Shutdown() override; + + // EventRouter::Observer: + void OnListenerAdded(const extensions::EventListenerInfo& details) override; + + private: + raw_ptr context_; + class AdblockAPIEventRouter; + std::unique_ptr event_router_; +}; + +template <> +void BrowserContextKeyedAPIFactory< + AdblockPrivateAPI>::DeclareFactoryDependencies(); + +namespace api { + +class AdblockPrivateSetEnabledFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.setEnabled", UNKNOWN) + AdblockPrivateSetEnabledFunction(); + + private: + ~AdblockPrivateSetEnabledFunction() override; + + ResponseAction Run() override; + + AdblockPrivateSetEnabledFunction(const AdblockPrivateSetEnabledFunction&) = + delete; + AdblockPrivateSetEnabledFunction& operator=( + const AdblockPrivateSetEnabledFunction&) = delete; +}; + +class AdblockPrivateIsEnabledFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.isEnabled", UNKNOWN) + AdblockPrivateIsEnabledFunction(); + + private: + ~AdblockPrivateIsEnabledFunction() override; + + ResponseAction Run() override; + + AdblockPrivateIsEnabledFunction(const AdblockPrivateIsEnabledFunction&) = + delete; + AdblockPrivateIsEnabledFunction& operator=( + const AdblockPrivateIsEnabledFunction&) = delete; +}; + +class AdblockPrivateSetAcceptableAdsEnabledFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.setAcceptableAdsEnabled", UNKNOWN) + AdblockPrivateSetAcceptableAdsEnabledFunction(); + + private: + ~AdblockPrivateSetAcceptableAdsEnabledFunction() override; + + ResponseAction Run() override; + + AdblockPrivateSetAcceptableAdsEnabledFunction( + const AdblockPrivateSetAcceptableAdsEnabledFunction&) = delete; + AdblockPrivateSetAcceptableAdsEnabledFunction& operator=( + const AdblockPrivateSetAcceptableAdsEnabledFunction&) = delete; +}; + +class AdblockPrivateIsAcceptableAdsEnabledFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.isAcceptableAdsEnabled", UNKNOWN) + AdblockPrivateIsAcceptableAdsEnabledFunction(); + + private: + ~AdblockPrivateIsAcceptableAdsEnabledFunction() override; + + ResponseAction Run() override; + + AdblockPrivateIsAcceptableAdsEnabledFunction( + const AdblockPrivateIsAcceptableAdsEnabledFunction&) = delete; + AdblockPrivateIsAcceptableAdsEnabledFunction& operator=( + const AdblockPrivateIsAcceptableAdsEnabledFunction&) = delete; +}; + +class AdblockPrivateGetBuiltInSubscriptionsFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.getBuiltInSubscriptions", UNKNOWN) + AdblockPrivateGetBuiltInSubscriptionsFunction(); + + private: + ~AdblockPrivateGetBuiltInSubscriptionsFunction() override; + + // ExtensionFunction: + ResponseAction Run() override; + + AdblockPrivateGetBuiltInSubscriptionsFunction( + const AdblockPrivateGetBuiltInSubscriptionsFunction&) = delete; + AdblockPrivateGetBuiltInSubscriptionsFunction& operator=( + const AdblockPrivateGetBuiltInSubscriptionsFunction&) = delete; +}; + +class AdblockPrivateInstallSubscriptionFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.installSubscription", UNKNOWN) + AdblockPrivateInstallSubscriptionFunction(); + + private: + ~AdblockPrivateInstallSubscriptionFunction() override; + + ResponseAction Run() override; + + AdblockPrivateInstallSubscriptionFunction( + const AdblockPrivateInstallSubscriptionFunction&) = delete; + AdblockPrivateInstallSubscriptionFunction& operator=( + const AdblockPrivateInstallSubscriptionFunction&) = delete; +}; + +class AdblockPrivateUninstallSubscriptionFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.uninstallSubscription", UNKNOWN) + AdblockPrivateUninstallSubscriptionFunction(); + + private: + ~AdblockPrivateUninstallSubscriptionFunction() override; + + ResponseAction Run() override; + + AdblockPrivateUninstallSubscriptionFunction( + const AdblockPrivateUninstallSubscriptionFunction&) = delete; + AdblockPrivateUninstallSubscriptionFunction& operator=( + const AdblockPrivateUninstallSubscriptionFunction&) = delete; +}; + +class AdblockPrivateGetInstalledSubscriptionsFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.getInstalledSubscriptions", + UNKNOWN) + AdblockPrivateGetInstalledSubscriptionsFunction(); + + private: + ~AdblockPrivateGetInstalledSubscriptionsFunction() override; + + ResponseAction Run() override; + + AdblockPrivateGetInstalledSubscriptionsFunction( + const AdblockPrivateGetInstalledSubscriptionsFunction&) = delete; + AdblockPrivateGetInstalledSubscriptionsFunction& operator=( + const AdblockPrivateGetInstalledSubscriptionsFunction&) = delete; +}; + +class AdblockPrivateAddAllowedDomainFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.addAllowedDomain", UNKNOWN) + AdblockPrivateAddAllowedDomainFunction(); + + private: + ~AdblockPrivateAddAllowedDomainFunction() override; + + ResponseAction Run() override; + + AdblockPrivateAddAllowedDomainFunction( + const AdblockPrivateAddAllowedDomainFunction&) = delete; + AdblockPrivateAddAllowedDomainFunction& operator=( + const AdblockPrivateAddAllowedDomainFunction&) = delete; +}; + +class AdblockPrivateRemoveAllowedDomainFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.removeAllowedDomain", UNKNOWN) + AdblockPrivateRemoveAllowedDomainFunction(); + + private: + ~AdblockPrivateRemoveAllowedDomainFunction() override; + + ResponseAction Run() override; + + AdblockPrivateRemoveAllowedDomainFunction( + const AdblockPrivateRemoveAllowedDomainFunction&) = delete; + AdblockPrivateRemoveAllowedDomainFunction& operator=( + const AdblockPrivateRemoveAllowedDomainFunction&) = delete; +}; + +class AdblockPrivateGetAllowedDomainsFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.getAllowedDomains", UNKNOWN) + AdblockPrivateGetAllowedDomainsFunction(); + + private: + ~AdblockPrivateGetAllowedDomainsFunction() override; + + ResponseAction Run() override; + + AdblockPrivateGetAllowedDomainsFunction( + const AdblockPrivateGetAllowedDomainsFunction&) = delete; + AdblockPrivateGetAllowedDomainsFunction& operator=( + const AdblockPrivateGetAllowedDomainsFunction&) = delete; +}; + +class AdblockPrivateAddCustomFilterFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.addCustomFilter", UNKNOWN) + AdblockPrivateAddCustomFilterFunction(); + + private: + ~AdblockPrivateAddCustomFilterFunction() override; + + ResponseAction Run() override; + + AdblockPrivateAddCustomFilterFunction( + const AdblockPrivateAddCustomFilterFunction&) = delete; + AdblockPrivateAddCustomFilterFunction& operator=( + const AdblockPrivateAddCustomFilterFunction&) = delete; +}; + +class AdblockPrivateRemoveCustomFilterFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.removeCustomFilter", UNKNOWN) + AdblockPrivateRemoveCustomFilterFunction(); + + private: + ~AdblockPrivateRemoveCustomFilterFunction() override; + + ResponseAction Run() override; + + AdblockPrivateRemoveCustomFilterFunction( + const AdblockPrivateRemoveCustomFilterFunction&) = delete; + AdblockPrivateRemoveCustomFilterFunction& operator=( + const AdblockPrivateRemoveCustomFilterFunction&) = delete; +}; + +class AdblockPrivateGetCustomFiltersFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.getCustomFilters", UNKNOWN) + AdblockPrivateGetCustomFiltersFunction(); + + private: + ~AdblockPrivateGetCustomFiltersFunction() override; + + ResponseAction Run() override; + + AdblockPrivateGetCustomFiltersFunction( + const AdblockPrivateGetCustomFiltersFunction&) = delete; + AdblockPrivateGetCustomFiltersFunction& operator=( + const AdblockPrivateGetCustomFiltersFunction&) = delete; +}; + +class AdblockPrivateGetSessionAllowedAdsCountFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.getSessionAllowedAdsCount", + UNKNOWN) + AdblockPrivateGetSessionAllowedAdsCountFunction(); + + private: + ~AdblockPrivateGetSessionAllowedAdsCountFunction() override; + + ResponseAction Run() override; + + AdblockPrivateGetSessionAllowedAdsCountFunction( + const AdblockPrivateGetSessionAllowedAdsCountFunction&) = delete; + AdblockPrivateGetSessionAllowedAdsCountFunction& operator=( + const AdblockPrivateGetSessionAllowedAdsCountFunction&) = delete; +}; + +class AdblockPrivateGetSessionBlockedAdsCountFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("adblockPrivate.getSessionBlockedAdsCount", + UNKNOWN) + AdblockPrivateGetSessionBlockedAdsCountFunction(); + + private: + ~AdblockPrivateGetSessionBlockedAdsCountFunction() override; + + ResponseAction Run() override; + + AdblockPrivateGetSessionBlockedAdsCountFunction( + const AdblockPrivateGetSessionBlockedAdsCountFunction&) = delete; + AdblockPrivateGetSessionBlockedAdsCountFunction& operator=( + const AdblockPrivateGetSessionBlockedAdsCountFunction&) = delete; +}; + +} // namespace api + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_ADBLOCK_PRIVATE_ADBLOCK_PRIVATE_API_H_ diff --git a/chrome/browser/extensions/api/adblock_private/adblock_private_apitest.cc b/chrome/browser/extensions/api/adblock_private/adblock_private_apitest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/extensions/api/adblock_private/adblock_private_apitest.cc @@ -0,0 +1,984 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . + +#include +#include +#include + +#include "chrome/browser/adblock/adblock_content_browser_client.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/ssl/https_upgrades_interceptor.h" +#include "chrome/browser/ssl/https_upgrades_util.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/api/adblock_private.h" +#include "chrome/common/extensions/api/tabs.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "content/public/test/browser_test.h" +#include "extensions/browser/background_script_executor.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "net/test/embedded_test_server/http_request.h" +#include "net/test/embedded_test_server/http_response.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +namespace { +enum class Mode { Normal, Incognito }; +enum class EyeoExtensionApi { Old, New }; +} // namespace + +class AdblockPrivateApiTest : public ExtensionApiTest, + public testing::WithParamInterface { + public: + AdblockPrivateApiTest() {} + ~AdblockPrivateApiTest() override = default; + AdblockPrivateApiTest(const AdblockPrivateApiTest&) = delete; + AdblockPrivateApiTest& operator=(const AdblockPrivateApiTest&) = delete; + + void SetUpCommandLine(base::CommandLine* command_line) override { + extensions::ExtensionApiTest::SetUpCommandLine(command_line); + if (IsIncognito()) { + command_line->AppendSwitch(switches::kIncognito); + } + } + + protected: + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + + // When any of that fails we need to update comment in adblock_private.idl + ASSERT_EQ(api::tabs::TAB_ID_NONE, -1); + ASSERT_EQ(SessionID::InvalidValue().id(), -1); + + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser()->profile()) + ->GetAdblockFilteringConfiguration() + ->RemoveCustomFilter(adblock::kAllowlistEverythingFilter); + + AdblockContentBrowserClient::ForceAdblockProxyForTesting(); + } + + bool IsIncognito() { return GetParam() == Mode::Incognito; } + + bool RunTest(const std::string& subtest) { + const std::string page_url = "main.html?subtest=" + subtest; + return RunExtensionTest("adblock_private", + {.extension_url = page_url.c_str()}, + {.allow_in_incognito = IsIncognito(), + .load_as_component = !IsIncognito()}); + } + + bool RunTestWithParams(const std::string& subtest, + const std::map& params) { + if (params.empty()) { + return RunTest(subtest); + } + std::string subtest_with_params = subtest; + for (const auto& [key, value] : params) { + subtest_with_params += "&" + key + "=" + value; + } + return RunTest(subtest_with_params); + } + + bool RunTestWithServer(const std::string& subtest, + const std::string& subscription_path, + const std::string& subscription_filters, + std::map params = {{}}) { + net::EmbeddedTestServer https_server{ + net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTPS)}; + https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); + https_server.RegisterRequestHandler(base::BindRepeating( + &AdblockPrivateApiTest::HandleSubscriptionUpdateRequest, + base::Unretained(this), subscription_path, subscription_filters)); + if (!https_server.Start()) { + return false; + } + params.insert( + {"subscription_url", https_server.GetURL(subscription_path).spec()}); + return RunTestWithParams(subtest, params); + } + + std::unique_ptr + HandleSubscriptionUpdateRequest( + const std::string subscription_path, + const std::string subscription_filters, + const net::test_server::HttpRequest& request) { + static const char kSubscriptionHeader[] = + "[Adblock Plus 2.0]\n" + "! Checksum: X5A8vtJDBW2a9EgS9glqbg\n" + "! Version: 202202061935\n" + "! Last modified: 06 Feb 2022 19:35 UTC\n" + "! Expires: 1 days (update frequency)\n\n"; + + if (base::StartsWith(request.relative_url, subscription_path, + base::CompareCase::SENSITIVE)) { + auto http_response = + std::make_unique(); + http_response->set_code(net::HTTP_OK); + http_response->set_content(kSubscriptionHeader + subscription_filters); + http_response->set_content_type("text/plain"); + return std::move(http_response); + } + + // Unhandled requests result in the Embedded test server sending a 404. + return nullptr; + } + + std::map SubscriptionsManagementSetup() { + DCHECK(browser()->profile()); + auto selected = adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser()->profile()) + ->GetAdblockFilteringConfiguration() + ->GetFilterLists(); + const auto easylist = std::find_if( + selected.begin(), selected.end(), [&](const GURL& subscription) { + return base::EndsWith(subscription.path_piece(), "easylist.txt"); + }); + const auto exceptions = std::find_if( + selected.begin(), selected.end(), [&](const GURL& subscription) { + return base::EndsWith(subscription.path_piece(), + "exceptionrules.txt"); + }); + const auto snippets = std::find_if( + selected.begin(), selected.end(), [&](const GURL& subscription) { + return base::EndsWith(subscription.path_piece(), + "abp-filters-anti-cv.txt"); + }); + if (easylist == selected.end() || exceptions == selected.end() || + snippets == selected.end()) { + return std::map{}; + } + return std::map{ + {"easylist", easylist->spec()}, + {"exceptions", exceptions->spec()}, + {"snippets", snippets->spec()}}; + } +}; + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, SetAndCheckEnabled) { + EXPECT_TRUE(RunTest("setEnabled_isEnabled")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, SetAndCheckEnabledNewAPI) { + EXPECT_TRUE(RunTestWithParams("setEnabled_isEnabled", + {{"api", "eyeoFilteringPrivate"}})) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, SetAndCheckAAEnabled) { + EXPECT_TRUE(RunTest("setAAEnabled_isAAEnabled")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, SetAndCheckAAEnabledNewAPI) { + EXPECT_TRUE(RunTest("setAAEnabled_isAAEnabled_newAPI")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, GetBuiltInSubscriptions) { + EXPECT_TRUE(RunTest("getBuiltInSubscriptions")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, + InstalledSubscriptionsDataSchema) { + EXPECT_TRUE(RunTest("installedSubscriptionsDataSchema")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, + InstalledSubscriptionsDataSchemaNewAPI) { + EXPECT_TRUE(RunTestWithParams("installedSubscriptionsDataSchema", + {{"api", "eyeoFilteringPrivate"}})) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, + InstalledSubscriptionsDataSchemaConfigDisabled) { + EXPECT_TRUE(RunTestWithParams("installedSubscriptionsDataSchema", + {{"disabled", "true"}})) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, + InstalledSubscriptionsDataSchemaConfigDisabledNewAPI) { + EXPECT_TRUE(RunTestWithParams( + "installedSubscriptionsDataSchema", + {{"api", "eyeoFilteringPrivate"}, {"disabled", "true"}})) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, InstallSubscriptionInvalidURL) { + EXPECT_TRUE(RunTest("installSubscriptionInvalidURL")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, + InstallSubscriptionInvalidURLNewAPI) { + EXPECT_TRUE(RunTestWithParams("installSubscriptionInvalidURL", + {{"api", "eyeoFilteringPrivate"}})) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, UninstallSubscriptionInvalidURL) { + EXPECT_TRUE(RunTest("uninstallSubscriptionInvalidURL")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, + UninstallSubscriptionInvalidURLNewAPI) { + EXPECT_TRUE(RunTestWithParams("uninstallSubscriptionInvalidURL", + {{"api", "eyeoFilteringPrivate"}})) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, SubscriptionsManagement) { + auto params = SubscriptionsManagementSetup(); + if (params.empty()) { + // Since default configuration has been changed let's skip this test + return; + } + EXPECT_TRUE(RunTestWithParams("subscriptionsManagement", params)) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, SubscriptionsManagementNewAPI) { + auto params = SubscriptionsManagementSetup(); + if (params.empty()) { + // Since default configuration has been changed let's skip this test + return; + } + params.insert({"api", "eyeoFilteringPrivate"}); + SubscriptionsManagementSetup(); + EXPECT_TRUE(RunTestWithParams("subscriptionsManagement", params)) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, + SubscriptionsManagementConfigDisabled) { + auto params = SubscriptionsManagementSetup(); + if (params.empty()) { + // Since default configuration has been changed let's skip this test + return; + } + params.insert({"disabled", "true"}); + EXPECT_TRUE(RunTestWithParams("subscriptionsManagement", params)) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, + SubscriptionsManagementConfigDisabledNewAPI) { + auto params = SubscriptionsManagementSetup(); + if (params.empty()) { + // Since default configuration has been changed let's skip this test + return; + } + params.insert({"api", "eyeoFilteringPrivate"}); + params.insert({"disabled", "true"}); + SubscriptionsManagementSetup(); + EXPECT_TRUE(RunTestWithParams("subscriptionsManagement", params)) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, AllowedDomainsManagement) { + EXPECT_TRUE(RunTest("allowedDomainsManagement")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, AllowedDomainsManagementNewAPI) { + EXPECT_TRUE(RunTestWithParams("allowedDomainsManagement", + {{"api", "eyeoFilteringPrivate"}})) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, CustomFiltersManagement) { + EXPECT_TRUE(RunTest("customFiltersManagement")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, CustomFiltersManagementNewAPI) { + EXPECT_TRUE(RunTestWithParams("customFiltersManagement", + {{"api", "eyeoFilteringPrivate"}})) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, AdBlockedEvents) { + std::string subscription_path = "/testeventssub.txt"; + std::string subscription_filters = "test1.png"; + EXPECT_TRUE(RunTestWithServer("adBlockedEvents", subscription_path, + subscription_filters)) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, AdBlockedEventsNewAPI) { + std::string subscription_path = "/testeventssub.txt"; + std::string subscription_filters = "test1.png"; + std::map params = {{"api", "eyeoFilteringPrivate"}}; + EXPECT_TRUE(RunTestWithServer("adBlockedEvents", subscription_path, + subscription_filters, std::move(params))) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, AdAllowedEvents) { + std::string subscription_path = "/testeventssub.txt"; + std::string subscription_filters = "test2.png\n@@test2.png"; + EXPECT_TRUE(RunTestWithServer("adAllowedEvents", subscription_path, + subscription_filters)) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, AdAllowedEventsNewAPI) { + std::string subscription_path = "/testeventssub.txt"; + std::string subscription_filters = "test2.png\n@@test2.png"; + std::map params = {{"api", "eyeoFilteringPrivate"}}; + EXPECT_TRUE(RunTestWithServer("adAllowedEvents", subscription_path, + subscription_filters, std::move(params))) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, SessionStats) { + std::string subscription_path = "/teststatssub.txt"; + std::string subscription_filters = "test3.png\ntest4.png\n@@test4.png"; + EXPECT_TRUE(RunTestWithServer("sessionStats", subscription_path, + subscription_filters)) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, SessionStatsNewAPI) { + std::string subscription_path = "/teststatssub.txt"; + std::string subscription_filters = "test3.png\ntest4.png\n@@test4.png"; + std::map params = {{"api", "eyeoFilteringPrivate"}}; + EXPECT_TRUE(RunTestWithServer("sessionStats", subscription_path, + subscription_filters, std::move(params))) + << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, AllowedDomainsEvent) { + EXPECT_TRUE(RunTest("allowedDomainsEvent")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, EnabledStateEvent) { + EXPECT_TRUE(RunTest("enabledStateEvent")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, FilterListsEvent) { + EXPECT_TRUE(RunTest("filterListsEvent")) << message_; +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiTest, CustomFiltersEvent) { + EXPECT_TRUE(RunTest("customFiltersEvent")) << message_; +} + +INSTANTIATE_TEST_SUITE_P(All, + AdblockPrivateApiTest, + testing::Values(Mode::Normal, Mode::Incognito)); + +class AdblockPrivateApiBackgroundPageTest + : public ExtensionApiTest, + public testing::WithParamInterface> { + public: + AdblockPrivateApiBackgroundPageTest() {} + ~AdblockPrivateApiBackgroundPageTest() override = default; + AdblockPrivateApiBackgroundPageTest( + const AdblockPrivateApiBackgroundPageTest&) = delete; + AdblockPrivateApiBackgroundPageTest& operator=( + const AdblockPrivateApiBackgroundPageTest&) = delete; + + void SetUpCommandLine(base::CommandLine* command_line) override { + extensions::ExtensionApiTest::SetUpCommandLine(command_line); + if (IsIncognito()) { + command_line->AppendSwitch(switches::kIncognito); + } + } + + protected: + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + + https_test_server_ = std::make_unique( + net::EmbeddedTestServer::TYPE_HTTPS); + https_test_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_OK); + https_test_server_->ServeFilesFromSourceDirectory( + "chrome/test/data/extensions/api_test/adblock_private"); + embedded_test_server()->ServeFilesFromSourceDirectory( + "chrome/test/data/extensions/api_test/adblock_private"); + HttpsUpgradesInterceptor::SetHttpsPortForTesting( + https_test_server_->port()); + HttpsUpgradesInterceptor::SetHttpPortForTesting( + embedded_test_server()->port()); + ASSERT_TRUE(https_test_server_->Start()); + AllowHttpForHostnamesForTesting({"example.com"}, + browser()->profile()->GetPrefs()); + + // Map example.com to localhost. + host_resolver()->AddRule("example.com", "127.0.0.1"); + ASSERT_TRUE(StartEmbeddedTestServer()); + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser()->profile()) + ->GetAdblockFilteringConfiguration() + ->RemoveCustomFilter(adblock::kAllowlistEverythingFilter); + } + + bool IsOldApi() { return std::get<0>(GetParam()) == EyeoExtensionApi::Old; } + + bool IsIncognito() { return std::get<1>(GetParam()) == Mode::Incognito; } + + void ExecuteScript(const std::string& js_code) const { + content::WebContents* web_contents = + browser()->tab_strip_model()->GetWebContentsAt(0); + ASSERT_TRUE(content::ExecJs(web_contents->GetPrimaryMainFrame(), js_code)); + } + + std::string ExecuteScriptInBackgroundPage(const std::string& extension_id, + const std::string& script) { + return ExtensionApiTest::ExecuteScriptInBackgroundPage(extension_id, script) + .GetString(); + } + + void SetupApiObjectAndMethods(const std::string& extension_id) { + std::string setup_script; + if (IsOldApi()) { + setup_script = + "var apiObject = chrome.adblockPrivate;" + "var sessionAllowedCount = 'getSessionAllowedAdsCount';" + "var sessionBlockedCount = 'getSessionBlockedAdsCount';" + "var onAllowedEvent = 'onAdAllowed';" + "var onBlockedEvent = 'onAdBlocked';" + "chrome.test.sendScriptResult('');"; + } else { + setup_script = + "var apiObject = chrome.eyeoFilteringPrivate;" + "var sessionAllowedCount = 'getSessionAllowedRequestsCount';" + "var sessionBlockedCount = 'getSessionBlockedRequestsCount';" + "var onAllowedEvent = 'onRequestAllowed';" + "var onBlockedEvent = 'onRequestBlocked';" + "chrome.test.sendScriptResult('');"; + } + ExecuteScriptInBackgroundPage(extension_id, setup_script); + } + + std::unique_ptr https_test_server_; +}; + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiBackgroundPageTest, PageAllowedEvents) { + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("adblock_private"), + {.allow_in_incognito = IsIncognito()}); + ASSERT_TRUE(extension); + + constexpr char kSetListenersScript[] = R"( + var testData = {}; + testData.pageAllowedCount = 0; + apiObject.onPageAllowed.addListener(function(e) { + if (!e.url.endsWith('test.html')) { + return; + } + testData.pageAllowedCount = testData.pageAllowedCount + 1; + }); + chrome.test.sendScriptResult(''); + )"; + + constexpr char kReadCountersScript[] = R"( + chrome.test.sendScriptResult(testData.pageAllowedCount.toString()); + )"; + + constexpr char kAllowDomainScript[] = R"( + apiObject.addAllowedDomain(%s'example.com'); + chrome.test.sendScriptResult(''); + )"; + + const GURL test_url = https_test_server_->GetURL( + "example.com", "/extensions/api_test/adblock_private/test.html"); + + SetupApiObjectAndMethods(extension->id()); + + ExecuteScriptInBackgroundPage(extension->id(), kSetListenersScript); + + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ( + "0", ExecuteScriptInBackgroundPage(extension->id(), kReadCountersScript)); + + ExecuteScriptInBackgroundPage( + extension->id(), + base::StringPrintf(kAllowDomainScript, IsOldApi() ? "" : "'adblock', ")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ( + "1", ExecuteScriptInBackgroundPage(extension->id(), kReadCountersScript)); +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiBackgroundPageTest, PageAllowedStats) { + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("adblock_private"), + {.allow_in_incognito = IsIncognito()}); + ASSERT_TRUE(extension); + + constexpr char kReadAllowedStatsScript[] = R"( + apiObject[sessionAllowedCount](function(sessionStats) { + let count = 0; + for (const entry of sessionStats) { + if (entry.url === 'adblock:custom') { + count = entry.count; + } + } + chrome.test.sendScriptResult(count.toString()); + }); + )"; + + constexpr char kAllowDomainScript[] = R"( + apiObject.addAllowedDomain(%s'example.com'); + chrome.test.sendScriptResult(''); + )"; + + const GURL test_url = https_test_server_->GetURL( + "example.com", "/extensions/api_test/adblock_private/test.html"); + + SetupApiObjectAndMethods(extension->id()); + + int initial_value = std::stoi( + ExecuteScriptInBackgroundPage(extension->id(), kReadAllowedStatsScript)); + + ExecuteScriptInBackgroundPage( + extension->id(), + base::StringPrintf(kAllowDomainScript, IsOldApi() ? "" : "'adblock', ")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + + ASSERT_EQ(initial_value + 1, std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), kReadAllowedStatsScript))); +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiBackgroundPageTest, PopupEvents) { + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("adblock_private"), + {.allow_in_incognito = IsIncognito()}); + ASSERT_TRUE(extension); + + constexpr char kSetListenersScript[] = R"( + var testData = {}; + testData.popupBlockedCount = 0; + testData.popupAllowedCount = 0; + apiObject.onPopupAllowed.addListener(function(e, blocked) { + if (!e.url.endsWith('some-popup.html')) { + return; + } + testData.popupAllowedCount = testData.popupAllowedCount + 1; + }); + apiObject.onPopupBlocked.addListener(function(e, blocked) { + if (!e.url.endsWith('some-popup.html')) { + return; + } + testData.popupBlockedCount = testData.popupBlockedCount + 1; + }); + chrome.test.sendScriptResult(''); + )"; + + auto read_allowed_stats_script = [](int expected) { + std::string script = base::StringPrintf( + R"( + var intervalAllowedId = setInterval(function() { + if (testData.popupAllowedCount == %d) { + if (intervalAllowedId) { + clearInterval(intervalAllowedId); + intervalAllowedId = null; + } + chrome.test.sendScriptResult( + testData.popupAllowedCount.toString()); + } + }, 100))", + expected); + return script; + }; + + auto read_blocked_stats_script = [](int expected) { + std::string script = base::StringPrintf( + R"( + var intervalBlockedId = setInterval(function() { + if (testData.popupBlockedCount == %d) { + if (intervalBlockedId) { + clearInterval(intervalBlockedId); + intervalBlockedId = null; + } + chrome.test.sendScriptResult( + testData.popupBlockedCount.toString()); + } + }, 100))", + expected); + return script; + }; + + constexpr char kBlockPopupScript[] = R"( + apiObject.addCustomFilter(%s'some-popup.html^$popup'); + chrome.test.sendScriptResult(''); + )"; + + constexpr char kAllowPopupScript[] = R"( + apiObject.addCustomFilter(%s'@@some-popup.html^$popup'); + chrome.test.sendScriptResult(''); + )"; + + const GURL test_url = + embedded_test_server()->GetURL("example.com", "/test.html"); + constexpr char kOpenPopupScript[] = + "document.getElementById('popup_id').click()"; + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + SetupApiObjectAndMethods(extension->id()); + + ExecuteScriptInBackgroundPage(extension->id(), kSetListenersScript); + + ExecuteScriptInBackgroundPage( + extension->id(), + base::StringPrintf(kBlockPopupScript, IsOldApi() ? "" : "'adblock', ")); + ExecuteScript(kOpenPopupScript); + ASSERT_EQ(1, std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), read_blocked_stats_script(1)))); + + ExecuteScriptInBackgroundPage( + extension->id(), + base::StringPrintf(kAllowPopupScript, IsOldApi() ? "" : "'adblock', ")); + ExecuteScript(kOpenPopupScript); + ASSERT_EQ(1, std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), read_allowed_stats_script(1)))); + ASSERT_EQ(1, std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), read_blocked_stats_script(1)))); +} + +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiBackgroundPageTest, PopupStats) { + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("adblock_private"), + {.allow_in_incognito = IsIncognito()}); + ASSERT_TRUE(extension); + + auto read_allowed_stats_script = [](int expected) { + std::string script = base::StringPrintf( + R"( + var intervalAllowedId = setInterval(function() { + apiObject[sessionAllowedCount](function(sessionStats) { + let count = 0; + for (const entry of sessionStats) { + if (entry.url === 'adblock:custom') { + count = entry.count; + } + } + if (%d == 0 || count == %d) { + if (intervalAllowedId) { + clearInterval(intervalAllowedId); + intervalAllowedId = null; + } + chrome.test.sendScriptResult(count.toString()); + } + } + )}, 100))", + expected, expected); + return script; + }; + + auto read_blocked_stats_script = [](int expected) { + std::string script = base::StringPrintf( + R"( + var intervalBlockedId = setInterval(function() { + apiObject[sessionBlockedCount](function(sessionStats) { + let count = 0; + for (const entry of sessionStats) { + if (entry.url === 'adblock:custom') { + count = entry.count; + } + } + if (%d == 0 || count == %d) { + if (intervalBlockedId) { + clearInterval(intervalBlockedId); + intervalBlockedId = null; + } + chrome.test.sendScriptResult(count.toString()); + } + } + )}, 100))", + expected, expected); + return script; + }; + + constexpr char kBlockPopupScript[] = R"( + apiObject.addCustomFilter(%s'some-popup.html^$popup'); + chrome.test.sendScriptResult(''); + )"; + + constexpr char kAllowPopupScript[] = R"( + apiObject.addCustomFilter(%s'@@some-popup.html^$popup'); + chrome.test.sendScriptResult(''); + )"; + + const GURL test_url = + embedded_test_server()->GetURL("example.com", "/test.html"); + constexpr char kOpenPopupScript[] = + "document.getElementById('popup_id').click()"; + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + SetupApiObjectAndMethods(extension->id()); + + int initial_allowed_value = std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), read_allowed_stats_script(0))); + int initial_blocked_value = std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), read_blocked_stats_script(0))); + + ExecuteScriptInBackgroundPage( + extension->id(), + base::StringPrintf(kBlockPopupScript, IsOldApi() ? "" : "'adblock', ")); + ExecuteScript(kOpenPopupScript); + ASSERT_EQ(initial_blocked_value + 1, + std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), + read_blocked_stats_script(initial_blocked_value + 1)))); + + ExecuteScriptInBackgroundPage( + extension->id(), + base::StringPrintf(kAllowPopupScript, IsOldApi() ? "" : "'adblock', ")); + ExecuteScript(kOpenPopupScript); + ASSERT_EQ(initial_allowed_value + 1, + std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), + read_allowed_stats_script(initial_allowed_value + 1)))); + ASSERT_EQ(initial_blocked_value + 1, + std::stoi(ExecuteScriptInBackgroundPage( + extension->id(), + read_blocked_stats_script(initial_blocked_value + 1)))); +} + +INSTANTIATE_TEST_SUITE_P( + All, + AdblockPrivateApiBackgroundPageTest, + testing::Combine(testing::Values(EyeoExtensionApi::Old, + EyeoExtensionApi::New), + testing::Values(Mode::Normal, Mode::Incognito))); + +class AdblockPrivateApiBackgroundPageTestWithRedirect + : public AdblockPrivateApiBackgroundPageTest { + public: + AdblockPrivateApiBackgroundPageTestWithRedirect() {} + ~AdblockPrivateApiBackgroundPageTestWithRedirect() override = default; + AdblockPrivateApiBackgroundPageTestWithRedirect( + const AdblockPrivateApiBackgroundPageTestWithRedirect&) = delete; + AdblockPrivateApiBackgroundPageTestWithRedirect& operator=( + const AdblockPrivateApiBackgroundPageTestWithRedirect&) = delete; + + protected: + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + + // Map domains to localhost. Add redirect handler. + host_resolver()->AddRule(before_redirect_domain, "127.0.0.1"); + host_resolver()->AddRule(after_redirect_domain, "127.0.0.1"); + embedded_test_server()->RegisterRequestHandler(base::BindRepeating( + &AdblockPrivateApiBackgroundPageTestWithRedirect::RequestHandler, + base::Unretained(this))); + ASSERT_TRUE(StartEmbeddedTestServer()); + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser()->profile()) + ->GetAdblockFilteringConfiguration() + ->RemoveCustomFilter(adblock::kAllowlistEverythingFilter); + } + + std::unique_ptr RequestHandler( + const net::test_server::HttpRequest& request) { + if (base::EndsWith(request.relative_url, before_redirect_domain, + base::CompareCase::SENSITIVE)) { + std::unique_ptr http_response( + new net::test_server::BasicHttpResponse()); + http_response->set_code(net::HTTP_FOUND); + http_response->set_content("Redirecting..."); + http_response->set_content_type("text/plain"); + http_response->AddCustomHeader( + "Location", "http://" + after_redirect_domain + ":" + + base::NumberToString(embedded_test_server()->port()) + + request.relative_url.substr( + 0, request.relative_url.size() - + before_redirect_domain.size() - 1)); + return std::move(http_response); + } + if (base::EndsWith(request.relative_url, subresource_with_redirect, + base::CompareCase::SENSITIVE)) { + auto http_response = + std::make_unique(); + http_response->set_code(net::HTTP_OK); + // Create a sub resource url which causes redirection + const GURL url = embedded_test_server()->GetURL( + before_redirect_domain, "/image.png?" + before_redirect_domain); + std::string body = ""; + http_response->set_content(body); + http_response->set_content_type("text/html"); + return std::move(http_response); + } + + // Unhandled requests result in the Embedded test server sending a 404. + return nullptr; + } + + const std::string before_redirect_domain = "before-redirect.com"; + const std::string after_redirect_domain = "after-redirect.com"; + const std::string subresource_with_redirect = "subresource_with_redirect"; +}; + +// Test for DPD-1519 +// This test verifies redirection of a main page +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiBackgroundPageTestWithRedirect, + PageAllowedEvents) { + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("adblock_private"), + {.allow_in_incognito = IsIncognito()}); + ASSERT_TRUE(extension); + + constexpr char kSetListenersScript[] = R"( + var testData = {}; + testData.pageAllowedBeforeRedirectCount = 0; + testData.pageAllowedAfterRedirectCount = 0; + apiObject.onPageAllowed.addListener(function(e) { + if (e.url.includes('before-redirect.com')) { + ++testData.pageAllowedBeforeRedirectCount; + } else if (e.url.includes('after-redirect.com')) { + ++testData.pageAllowedAfterRedirectCount; + } + }); + chrome.test.sendScriptResult(''); + )"; + + constexpr char kReadCountersScript[] = R"( + chrome.test.sendScriptResult( + testData.pageAllowedBeforeRedirectCount + '-' + + testData.pageAllowedAfterRedirectCount); + )"; + + constexpr char kAddAllowDomainScript[] = R"( + apiObject.addAllowedDomain(%s'after-redirect.com'); + chrome.test.sendScriptResult(''); + )"; + + constexpr char kRemoveAllowDomainScript[] = R"( + apiObject.removeAllowedDomain(%s'after-redirect.com'); + chrome.test.sendScriptResult(''); + )"; + + constexpr char kAddBlockDomainFilterScript[] = R"( + apiObject.addCustomFilter(%s'after-redirect.com'); + chrome.test.sendScriptResult(''); + )"; + + // Because RequestHandler handler sees just a 127.0.0.1 instead of + // a domain we are passing here source domain as a path in url. + const GURL test_url = embedded_test_server()->GetURL( + before_redirect_domain, "/" + before_redirect_domain); + + SetupApiObjectAndMethods(extension->id()); + + ExecuteScriptInBackgroundPage(extension->id(), kSetListenersScript); + + // No filter + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ("0-0", ExecuteScriptInBackgroundPage(extension->id(), + kReadCountersScript)); + + // Just allow filter + ExecuteScriptInBackgroundPage( + extension->id(), base::StringPrintf(kAddAllowDomainScript, + IsOldApi() ? "" : "'adblock', ")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ("0-1", ExecuteScriptInBackgroundPage(extension->id(), + kReadCountersScript)); + // Just block filter + ExecuteScriptInBackgroundPage( + extension->id(), base::StringPrintf(kAddBlockDomainFilterScript, + IsOldApi() ? "" : "'adblock', ")); + ExecuteScriptInBackgroundPage( + extension->id(), base::StringPrintf(kRemoveAllowDomainScript, + IsOldApi() ? "" : "'adblock', ")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ("0-1", ExecuteScriptInBackgroundPage(extension->id(), + kReadCountersScript)); + + // Allow and block filter + ExecuteScriptInBackgroundPage( + extension->id(), base::StringPrintf(kAddAllowDomainScript, + IsOldApi() ? "" : "'adblock', ")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ("0-2", ExecuteScriptInBackgroundPage(extension->id(), + kReadCountersScript)); +} + +// This test verifies redirection of a sub resource +IN_PROC_BROWSER_TEST_P(AdblockPrivateApiBackgroundPageTestWithRedirect, + AdMatchedEvents) { + const Extension* extension = + LoadExtension(test_data_dir_.AppendASCII("adblock_private"), + {.allow_in_incognito = IsIncognito()}); + ASSERT_TRUE(extension); + + constexpr char kSetListenersScript[] = R"( + var testData = {}; + testData.adBlockedCount = 0; + testData.adAllowedCount = 0; + apiObject[onBlockedEvent].addListener(function(e) { + if (e.url.includes('http://after-redirect.com')) { + ++testData.adBlockedCount; + } + }); + apiObject[onAllowedEvent].addListener(function(e) { + if (e.url.includes('http://after-redirect.com')) { + ++testData.adAllowedCount; + } + }); + chrome.test.sendScriptResult(''); + )"; + + constexpr char kReadCountersScript[] = R"( + chrome.test.sendScriptResult( + testData.adBlockedCount + '-' + testData.adAllowedCount); + )"; + + constexpr char kAddBlockingFilterScript[] = R"( + apiObject.addCustomFilter(%s'||after-redirect.com*/image.png'); + chrome.test.sendScriptResult(''); + )"; + + constexpr char kAddAllowingFilterScript[] = R"( + apiObject.addCustomFilter(%s'@@||after-redirect.com*/image.png'); + chrome.test.sendScriptResult(''); + )"; + + SetupApiObjectAndMethods(extension->id()); + + ExecuteScriptInBackgroundPage(extension->id(), kSetListenersScript); + + const GURL test_url = embedded_test_server()->GetURL( + after_redirect_domain, "/" + subresource_with_redirect); + + // No filter + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ("0-0", ExecuteScriptInBackgroundPage(extension->id(), + kReadCountersScript)); + + // Just block filter + ExecuteScriptInBackgroundPage( + extension->id(), base::StringPrintf(kAddBlockingFilterScript, + IsOldApi() ? "" : "'adblock', ")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ("1-0", ExecuteScriptInBackgroundPage(extension->id(), + kReadCountersScript)); + + // Allow and block filter + ExecuteScriptInBackgroundPage( + extension->id(), base::StringPrintf(kAddAllowingFilterScript, + IsOldApi() ? "" : "'adblock', ")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), test_url)); + ASSERT_EQ("1-1", ExecuteScriptInBackgroundPage(extension->id(), + kReadCountersScript)); +} + +INSTANTIATE_TEST_SUITE_P( + All, + AdblockPrivateApiBackgroundPageTestWithRedirect, + testing::Combine(testing::Values(EyeoExtensionApi::Old, + EyeoExtensionApi::New), + testing::Values(Mode::Normal, Mode::Incognito))); + +} // namespace extensions diff --git a/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc b/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc --- a/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc +++ b/chrome/browser/extensions/api/api_browser_context_keyed_service_factories.cc @@ -1,12 +1,17 @@ // Copyright 2022 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the +// components/adblock/LICENSE file. #include "chrome/browser/extensions/browser_context_keyed_service_factories.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/extensions/api/activity_log_private/activity_log_private_api.h" +#include "chrome/browser/extensions/api/adblock_private/adblock_private_api.h" #include "chrome/browser/extensions/api/autofill_private/autofill_private_event_router_factory.h" #include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h" #include "chrome/browser/extensions/api/bookmarks/bookmarks_api.h" @@ -15,6 +20,7 @@ #include "chrome/browser/extensions/api/cookies/cookies_api.h" #include "chrome/browser/extensions/api/developer_private/developer_private_api.h" #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" +#include "chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.h" #include "chrome/browser/extensions/api/font_settings/font_settings_api.h" #include "chrome/browser/extensions/api/history/history_api.h" #include "chrome/browser/extensions/api/identity/identity_api.h" @@ -73,6 +79,7 @@ namespace chrome_extensions { void EnsureApiBrowserContextKeyedServiceFactoriesBuilt() { extensions::ActivityLogAPI::GetFactoryInstance(); + extensions::AdblockPrivateAPI::GetFactoryInstance(); extensions::AutofillPrivateEventRouterFactory::GetInstance(); extensions::BluetoothLowEnergyAPI::GetFactoryInstance(); extensions::BookmarksAPI::GetFactoryInstance(); @@ -82,6 +89,7 @@ void EnsureApiBrowserContextKeyedServiceFactoriesBuilt() { extensions::CookiesAPI::GetFactoryInstance(); extensions::DeveloperPrivateAPI::GetFactoryInstance(); extensions::ExtensionActionAPI::GetFactoryInstance(); + extensions::EyeoFilteringPrivateAPI::GetFactoryInstance(); extensions::FontSettingsAPI::GetFactoryInstance(); extensions::HistoryAPI::GetFactoryInstance(); extensions::IdentityAPI::GetFactoryInstance(); 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 new file mode 100644 --- /dev/null +++ b/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.cc @@ -0,0 +1,772 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . + +#include "chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.h" + +#include "base/containers/flat_map.h" +#include "base/logging.h" +#include "base/no_destructor.h" +#include "base/time/time_to_iso8601.h" +#include "base/values.h" +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/adblock/session_stats_factory.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/tabs.h" +#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/configuration/filtering_configuration.h" +#include "components/adblock/core/configuration/persistent_filtering_configuration.h" +#include "components/adblock/core/session_stats.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/prefs/pref_service.h" +#include "components/sessions/core/session_id.h" +#include "content/public/browser/web_contents.h" +#include "url/gurl.h" +namespace extensions { + +namespace { + +constexpr char kConfigurationMissing[] = + "Configuration with name '%s' does not exist!"; + +adblock::FilteringConfiguration* FindFilteringConfiguration( + content::BrowserContext* context, + const std::string& config_name) { + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext(context); + const auto installed_configurations = + subscription_service->GetInstalledFilteringConfigurations(); + auto configuration_it = + base::ranges::find(installed_configurations, config_name, + &adblock::FilteringConfiguration::GetName); + return configuration_it != installed_configurations.end() ? *configuration_it + : nullptr; +} + +enum class SubscriptionAction { kInstall, kUninstall }; + +std::string RunSubscriptionAction( + adblock::FilteringConfiguration* configuration, + SubscriptionAction action, + const GURL& url) { + if (!url.is_valid()) { + return "Invalid URL"; + } + switch (action) { + case SubscriptionAction::kInstall: + configuration->AddFilterList(url); + break; + case SubscriptionAction::kUninstall: + configuration->RemoveFilterList(url); + break; + default: + NOTREACHED(); + } + + return {}; +} + +std::vector CopySessionsStats( + const std::map& source) { + std::vector result; + for (auto& entry : source) { + api::eyeo_filtering_private::SessionStatsEntry js_entry; + js_entry.url = entry.first.spec(); + js_entry.count = entry.second; + result.emplace_back(std::move(js_entry)); + } + 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 CopySubscriptions( + const std::vector> + current_subscriptions) { + std::vector result; + for (auto& sub : current_subscriptions) { + api::eyeo_filtering_private::Subscription js_sub; + js_sub.url = sub->GetSourceUrl().spec(); + js_sub.title = sub->GetTitle(); + js_sub.current_version = sub->GetCurrentVersion(); + js_sub.installation_state = + SubscriptionInstallationStateToString(sub->GetInstallationState()); + js_sub.last_installation_time = + base::TimeToISO8601(sub->GetInstallationTime()); + result.emplace_back(std::move(js_sub)); + } + return result; +} + +} // namespace + +class EyeoFilteringPrivateAPI::EyeoFilteringAPIEventRouter + : public adblock::ResourceClassificationRunner::Observer, + public adblock::SubscriptionService::SubscriptionObserver, + public adblock::FilteringConfiguration::Observer { + public: + explicit EyeoFilteringAPIEventRouter(content::BrowserContext* context) + : context_(context) { + adblock::ResourceClassificationRunnerFactory::GetForBrowserContext(context_) + ->AddObserver(this); + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext(context_); + subscription_service->AddObserver(this); + for (auto* it : + subscription_service->GetInstalledFilteringConfigurations()) { + it->AddObserver(this); + } + } + + ~EyeoFilteringAPIEventRouter() override { + adblock::ResourceClassificationRunnerFactory::GetForBrowserContext(context_) + ->RemoveObserver(this); + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext(context_); + subscription_service->RemoveObserver(this); + for (auto* it : + subscription_service->GetInstalledFilteringConfigurations()) { + it->RemoveObserver(this); + } + } + + // adblock::ResourceClassificationRunner::Observer: + void OnAdMatched(const GURL& url, + adblock::FilterMatchResult match_result, + const std::vector& parent_frame_urls, + adblock::ContentType content_type, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + std::unique_ptr event; + api::eyeo_filtering_private::RequestInfo info = CreateRequestInfoObject( + url, subscription, configuration_name, render_frame_host); + info.parent_frame_urls = adblock::utils::ConvertURLs(parent_frame_urls); + info.content_type = adblock::ContentTypeToString(content_type); + + if (match_result == adblock::FilterMatchResult::kBlockRule) { + event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnRequestBlocked::kEventName, + api::eyeo_filtering_private::OnRequestBlocked::Create(info)); + } else { + DCHECK(match_result == adblock::FilterMatchResult::kAllowRule); + event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnRequestAllowed::kEventName, + api::eyeo_filtering_private::OnRequestAllowed::Create(info)); + } + + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnPageAllowed(const GURL& url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + api::eyeo_filtering_private::RequestInfo info = CreateRequestInfoObject( + url, subscription, configuration_name, render_frame_host); + info.parent_frame_urls = std::vector{}; + info.content_type = ""; + + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnPageAllowed::kEventName, + api::eyeo_filtering_private::OnPageAllowed::Create(info)); + + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnPopupMatched(const GURL& url, + adblock::FilterMatchResult match_result, + const GURL& opener_url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override { + std::unique_ptr event; + api::eyeo_filtering_private::RequestInfo info = CreateRequestInfoObject( + url, subscription, configuration_name, render_frame_host); + info.parent_frame_urls = std::vector{opener_url.spec()}; + info.content_type = ""; + + if (match_result == adblock::FilterMatchResult::kBlockRule) { + event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnPopupBlocked::kEventName, + api::eyeo_filtering_private::OnPopupBlocked::Create(info)); + } else { + DCHECK(match_result == adblock::FilterMatchResult::kAllowRule); + event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnPopupAllowed::kEventName, + api::eyeo_filtering_private::OnPopupAllowed::Create(info)); + } + + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + // adblock::SubscriptionService::SubscriptionObserver: + void OnSubscriptionInstalled(const GURL& url) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnSubscriptionUpdated::kEventName, + api::eyeo_filtering_private::OnSubscriptionUpdated::Create(url.spec())); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnFilteringConfigurationInstalled( + adblock::FilteringConfiguration* config) override { + config->AddObserver(this); + } + + // adblock::FilteringConfiguration::Observer: + void OnEnabledStateChanged(adblock::FilteringConfiguration* config) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnEnabledStateChanged::kEventName, + api::eyeo_filtering_private::OnEnabledStateChanged::Create( + config->GetName())); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnFilterListsChanged(adblock::FilteringConfiguration* config) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnFilterListsChanged::kEventName, + api::eyeo_filtering_private::OnFilterListsChanged::Create( + config->GetName())); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnAllowedDomainsChanged( + adblock::FilteringConfiguration* config) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnAllowedDomainsChanged::kEventName, + api::eyeo_filtering_private::OnAllowedDomainsChanged::Create( + config->GetName())); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + void OnCustomFiltersChanged( + adblock::FilteringConfiguration* config) override { + std::unique_ptr event = std::make_unique( + events::EYEO_EVENT, + api::eyeo_filtering_private::OnCustomFiltersChanged::kEventName, + api::eyeo_filtering_private::OnCustomFiltersChanged::Create( + config->GetName())); + extensions::EventRouter::Get(context_)->BroadcastEvent(std::move(event)); + } + + private: + api::eyeo_filtering_private::RequestInfo CreateRequestInfoObject( + const GURL& url, + const GURL& subscription, + const std::string& configuration_name, + content::RenderFrameHost* render_frame_host) { + DCHECK(render_frame_host); + api::eyeo_filtering_private::RequestInfo info; + info.url = url.spec(); + info.subscription = subscription.spec(); + info.configuration_name = configuration_name; + info.tab_id = api::tabs::TAB_ID_NONE; + info.window_id = SessionID::InvalidValue().id(); + const content::WebContents* wc = + content::WebContents::FromRenderFrameHost(render_frame_host); + if (wc) { + info.tab_id = ExtensionTabUtil::GetTabId(wc); + info.window_id = ExtensionTabUtil::GetWindowIdOfTab(wc); + } + return info; + } + + const raw_ptr context_; +}; + +template <> +void BrowserContextKeyedAPIFactory< + EyeoFilteringPrivateAPI>::DeclareFactoryDependencies() { + DependsOn(adblock::SubscriptionServiceFactory::GetInstance()); +} + +// static +BrowserContextKeyedAPIFactory* +EyeoFilteringPrivateAPI::GetFactoryInstance() { + static base::NoDestructor< + BrowserContextKeyedAPIFactory> + instance; + return instance.get(); +} + +// static +EyeoFilteringPrivateAPI* EyeoFilteringPrivateAPI::Get( + content::BrowserContext* context) { + return GetFactoryInstance()->Get(context); +} + +EyeoFilteringPrivateAPI::EyeoFilteringPrivateAPI( + content::BrowserContext* context) + : context_(context) { + // EventRouter can be null in tests + auto* ev = EventRouter::Get(context_); + if (ev) { + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnRequestAllowed::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnRequestBlocked::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnPageAllowed::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnPopupAllowed::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnPopupBlocked::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnSubscriptionUpdated::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnEnabledStateChanged::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnFilterListsChanged::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnAllowedDomainsChanged::kEventName); + ev->RegisterObserver( + this, api::eyeo_filtering_private::OnCustomFiltersChanged::kEventName); + } + // Make sure SessionStats is created so it will start collectings stats + adblock::SessionStatsFactory::GetForBrowserContext(context); +} + +EyeoFilteringPrivateAPI::~EyeoFilteringPrivateAPI() = default; + +void EyeoFilteringPrivateAPI::Shutdown() { + // EventRouter can be null in tests + if (EventRouter::Get(context_)) { + EventRouter::Get(context_)->UnregisterObserver(this); + } + event_router_.reset(); +} + +void EyeoFilteringPrivateAPI::OnListenerAdded( + const extensions::EventListenerInfo& details) { + event_router_ = + std::make_unique( + context_); + EventRouter::Get(context_)->UnregisterObserver(this); +} + +namespace api { + +EyeoFilteringPrivateCreateConfigurationFunction:: + EyeoFilteringPrivateCreateConfigurationFunction() = default; + +EyeoFilteringPrivateCreateConfigurationFunction:: + ~EyeoFilteringPrivateCreateConfigurationFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateCreateConfigurationFunction::Run() { + absl::optional + params(api::eyeo_filtering_private::CreateConfiguration::Params::Create( + args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()); + const auto installed_configurations = + subscription_service->GetInstalledFilteringConfigurations(); + auto configuration_it = + base::ranges::find(installed_configurations, params->config_name, + &adblock::FilteringConfiguration::GetName); + if (configuration_it == installed_configurations.end()) { + auto new_filtering_configuration = + std::make_unique( + Profile::FromBrowserContext(browser_context()) + ->GetOriginalProfile() + ->GetPrefs(), + params->config_name); + subscription_service->InstallFilteringConfiguration( + std::move(new_filtering_configuration)); + } + return RespondNow(NoArguments()); +} + +EyeoFilteringPrivateRemoveConfigurationFunction:: + EyeoFilteringPrivateRemoveConfigurationFunction() = default; + +EyeoFilteringPrivateRemoveConfigurationFunction:: + ~EyeoFilteringPrivateRemoveConfigurationFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateRemoveConfigurationFunction::Run() { + absl::optional + params(api::eyeo_filtering_private::RemoveConfiguration::Params::Create( + args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()); + const auto installed_configurations = + subscription_service->GetInstalledFilteringConfigurations(); + auto configuration_it = + base::ranges::find(installed_configurations, params->config_name, + &adblock::FilteringConfiguration::GetName); + if (configuration_it != installed_configurations.end()) { + subscription_service->UninstallFilteringConfiguration( + (*configuration_it)->GetName()); + } + return RespondNow(NoArguments()); +} + +EyeoFilteringPrivateGetConfigurationsFunction:: + EyeoFilteringPrivateGetConfigurationsFunction() = default; + +EyeoFilteringPrivateGetConfigurationsFunction:: + ~EyeoFilteringPrivateGetConfigurationsFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateGetConfigurationsFunction::Run() { + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()); + const auto installed_configurations = + subscription_service->GetInstalledFilteringConfigurations(); + std::vector configurations; + base::ranges::transform(installed_configurations, + std::back_inserter(configurations), + [](adblock::FilteringConfiguration* config) { + return config->GetName(); + }); + return RespondNow(ArgumentList( + api::eyeo_filtering_private::GetConfigurations::Results::Create( + std::move(configurations)))); +} + +EyeoFilteringPrivateSetEnabledFunction:: + EyeoFilteringPrivateSetEnabledFunction() = default; + +EyeoFilteringPrivateSetEnabledFunction:: + ~EyeoFilteringPrivateSetEnabledFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateSetEnabledFunction::Run() { + absl::optional params( + api::eyeo_filtering_private::SetEnabled::Params::Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + configuration->SetEnabled(params->enabled); + return RespondNow(NoArguments()); +} + +EyeoFilteringPrivateIsEnabledFunction::EyeoFilteringPrivateIsEnabledFunction() { +} + +EyeoFilteringPrivateIsEnabledFunction:: + ~EyeoFilteringPrivateIsEnabledFunction() = default; + +ExtensionFunction::ResponseAction EyeoFilteringPrivateIsEnabledFunction::Run() { + absl::optional params( + api::eyeo_filtering_private::IsEnabled::Params::Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + return RespondNow( + ArgumentList(api::eyeo_filtering_private::IsEnabled::Results::Create( + configuration->IsEnabled()))); +} + +EyeoFilteringPrivateGetAcceptableAdsUrlFunction:: + EyeoFilteringPrivateGetAcceptableAdsUrlFunction() = default; + +EyeoFilteringPrivateGetAcceptableAdsUrlFunction:: + ~EyeoFilteringPrivateGetAcceptableAdsUrlFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateGetAcceptableAdsUrlFunction::Run() { + return RespondNow(ArgumentList( + api::eyeo_filtering_private::GetAcceptableAdsUrl::Results::Create( + adblock::AcceptableAdsUrl().spec()))); +} + +EyeoFilteringPrivateSubscribeToFilterListFunction:: + EyeoFilteringPrivateSubscribeToFilterListFunction() = default; + +EyeoFilteringPrivateSubscribeToFilterListFunction:: + ~EyeoFilteringPrivateSubscribeToFilterListFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateSubscribeToFilterListFunction::Run() { + absl::optional + params(api::eyeo_filtering_private::SubscribeToFilterList::Params::Create( + args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + auto url = GURL{params->url}; + auto status = + RunSubscriptionAction(configuration, SubscriptionAction::kInstall, url); + if (!status.empty()) { + return RespondNow(Error(status)); + } + + return RespondNow(NoArguments()); +} + +EyeoFilteringPrivateUnsubscribeFromFilterListFunction:: + EyeoFilteringPrivateUnsubscribeFromFilterListFunction() = default; + +EyeoFilteringPrivateUnsubscribeFromFilterListFunction:: + ~EyeoFilteringPrivateUnsubscribeFromFilterListFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateUnsubscribeFromFilterListFunction::Run() { + absl::optional + params(api::eyeo_filtering_private::UnsubscribeFromFilterList::Params:: + Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + auto url = GURL{params->url}; + auto status = + RunSubscriptionAction(configuration, SubscriptionAction::kUninstall, url); + if (!status.empty()) { + return RespondNow(Error(status)); + } + + return RespondNow(NoArguments()); +} + +EyeoFilteringPrivateGetFilterListsFunction:: + EyeoFilteringPrivateGetFilterListsFunction() = default; + +EyeoFilteringPrivateGetFilterListsFunction:: + ~EyeoFilteringPrivateGetFilterListsFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateGetFilterListsFunction::Run() { + absl::optional params( + api::eyeo_filtering_private::GetFilterLists::Params::Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser_context()); + return RespondNow( + ArgumentList(api::eyeo_filtering_private::GetFilterLists::Results::Create( + CopySubscriptions( + subscription_service->GetCurrentSubscriptions(configuration))))); +} + +EyeoFilteringPrivateAddAllowedDomainFunction:: + EyeoFilteringPrivateAddAllowedDomainFunction() = default; + +EyeoFilteringPrivateAddAllowedDomainFunction:: + ~EyeoFilteringPrivateAddAllowedDomainFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateAddAllowedDomainFunction::Run() { + absl::optional params( + api::eyeo_filtering_private::AddAllowedDomain::Params::Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + configuration->AddAllowedDomain(params->domain); + return RespondNow(NoArguments()); +} + +EyeoFilteringPrivateRemoveAllowedDomainFunction:: + EyeoFilteringPrivateRemoveAllowedDomainFunction() = default; + +EyeoFilteringPrivateRemoveAllowedDomainFunction:: + ~EyeoFilteringPrivateRemoveAllowedDomainFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateRemoveAllowedDomainFunction::Run() { + absl::optional + params(api::eyeo_filtering_private::RemoveAllowedDomain::Params::Create( + args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + configuration->RemoveAllowedDomain(params->domain); + return RespondNow(NoArguments()); +} + +EyeoFilteringPrivateGetAllowedDomainsFunction:: + EyeoFilteringPrivateGetAllowedDomainsFunction() = default; + +EyeoFilteringPrivateGetAllowedDomainsFunction:: + ~EyeoFilteringPrivateGetAllowedDomainsFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateGetAllowedDomainsFunction::Run() { + absl::optional params( + api::eyeo_filtering_private::GetAllowedDomains::Params::Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + return RespondNow(ArgumentList( + api::eyeo_filtering_private::GetCustomFilters::Results::Create( + configuration->GetAllowedDomains()))); +} + +EyeoFilteringPrivateAddCustomFilterFunction:: + EyeoFilteringPrivateAddCustomFilterFunction() = default; + +EyeoFilteringPrivateAddCustomFilterFunction:: + ~EyeoFilteringPrivateAddCustomFilterFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateAddCustomFilterFunction::Run() { + absl::optional params( + api::eyeo_filtering_private::AddCustomFilter::Params::Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + configuration->AddCustomFilter(params->filter); + return RespondNow(NoArguments()); +} + +EyeoFilteringPrivateRemoveCustomFilterFunction:: + EyeoFilteringPrivateRemoveCustomFilterFunction() = default; + +EyeoFilteringPrivateRemoveCustomFilterFunction:: + ~EyeoFilteringPrivateRemoveCustomFilterFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateRemoveCustomFilterFunction::Run() { + absl::optional + params(api::eyeo_filtering_private::RemoveCustomFilter::Params::Create( + args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + configuration->RemoveCustomFilter(params->filter); + return RespondNow(NoArguments()); +} + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateGetCustomFiltersFunction::Run() { + absl::optional params( + api::eyeo_filtering_private::GetCustomFilters::Params::Create(args())); + EXTENSION_FUNCTION_VALIDATE(params); + auto* configuration = + FindFilteringConfiguration(browser_context(), params->configuration); + if (!configuration) { + return RespondNow(Error(base::StringPrintf(kConfigurationMissing, + params->configuration.c_str()))); + } + return RespondNow(ArgumentList( + api::eyeo_filtering_private::GetCustomFilters::Results::Create( + configuration->GetCustomFilters()))); +} + +EyeoFilteringPrivateGetCustomFiltersFunction:: + EyeoFilteringPrivateGetCustomFiltersFunction() = default; + +EyeoFilteringPrivateGetCustomFiltersFunction:: + ~EyeoFilteringPrivateGetCustomFiltersFunction() = default; + +EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction:: + EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction() = default; + +EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction:: + ~EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction::Run() { + auto* session_stats = + adblock::SessionStatsFactory::GetForBrowserContext(browser_context()); + return RespondNow(ArgumentList( + api::eyeo_filtering_private::GetSessionAllowedRequestsCount::Results:: + Create( + CopySessionsStats(session_stats->GetSessionAllowedAdsCount())))); +} + +EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction:: + EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction() = default; + +EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction:: + ~EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction() = default; + +ExtensionFunction::ResponseAction +EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction::Run() { + auto* session_stats = + adblock::SessionStatsFactory::GetForBrowserContext(browser_context()); + return RespondNow(ArgumentList( + api::eyeo_filtering_private::GetSessionBlockedRequestsCount::Results:: + Create( + CopySessionsStats(session_stats->GetSessionBlockedAdsCount())))); +} +} // namespace api +} // namespace extensions diff --git a/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.h b/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.h new file mode 100644 --- /dev/null +++ b/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_api.h @@ -0,0 +1,360 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . + +#ifndef CHROME_BROWSER_EXTENSIONS_API_EYEO_FILTERING_PRIVATE_EYEO_FILTERING_PRIVATE_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_EYEO_FILTERING_PRIVATE_EYEO_FILTERING_PRIVATE_API_H_ + +#include "base/memory/raw_ptr.h" +#include "chrome/common/extensions/api/eyeo_filtering_private.h" +#include "extensions/browser/browser_context_keyed_api_factory.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/extension_function.h" + +namespace extensions { + +class EyeoFilteringPrivateAPI : public BrowserContextKeyedAPI, + public EventRouter::Observer { + public: + static BrowserContextKeyedAPIFactory* + GetFactoryInstance(); + + static EyeoFilteringPrivateAPI* Get(content::BrowserContext* context); + + explicit EyeoFilteringPrivateAPI(content::BrowserContext* context); + ~EyeoFilteringPrivateAPI() override; + friend class BrowserContextKeyedAPIFactory; + + // BrowserContextKeyedAPI implementation. + static const char* service_name() { return "EyeoFilteringPrivateAPI"; } + static const bool kServiceRedirectedInIncognito = true; + static const bool kServiceIsCreatedWithBrowserContext = true; + void Shutdown() override; + + // EventRouter::Observer: + void OnListenerAdded(const extensions::EventListenerInfo& details) override; + + private: + const raw_ptr context_; + class EyeoFilteringAPIEventRouter; + std::unique_ptr event_router_; +}; + +template <> +void BrowserContextKeyedAPIFactory< + EyeoFilteringPrivateAPI>::DeclareFactoryDependencies(); + +namespace api { + +class EyeoFilteringPrivateCreateConfigurationFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.createConfiguration", + UNKNOWN) + EyeoFilteringPrivateCreateConfigurationFunction(); + + private: + ~EyeoFilteringPrivateCreateConfigurationFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateCreateConfigurationFunction( + const EyeoFilteringPrivateCreateConfigurationFunction&) = delete; + EyeoFilteringPrivateCreateConfigurationFunction& operator=( + const EyeoFilteringPrivateCreateConfigurationFunction&) = delete; +}; + +class EyeoFilteringPrivateRemoveConfigurationFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.removeConfiguration", + UNKNOWN) + EyeoFilteringPrivateRemoveConfigurationFunction(); + + private: + ~EyeoFilteringPrivateRemoveConfigurationFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateRemoveConfigurationFunction( + const EyeoFilteringPrivateRemoveConfigurationFunction&) = delete; + EyeoFilteringPrivateRemoveConfigurationFunction& operator=( + const EyeoFilteringPrivateRemoveConfigurationFunction&) = delete; +}; + +class EyeoFilteringPrivateGetConfigurationsFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.getConfigurations", UNKNOWN) + EyeoFilteringPrivateGetConfigurationsFunction(); + + private: + ~EyeoFilteringPrivateGetConfigurationsFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateGetConfigurationsFunction( + const EyeoFilteringPrivateGetConfigurationsFunction&) = delete; + EyeoFilteringPrivateGetConfigurationsFunction& operator=( + const EyeoFilteringPrivateGetConfigurationsFunction&) = delete; +}; + +class EyeoFilteringPrivateSetEnabledFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.setEnabled", UNKNOWN) + EyeoFilteringPrivateSetEnabledFunction(); + + private: + ~EyeoFilteringPrivateSetEnabledFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateSetEnabledFunction( + const EyeoFilteringPrivateSetEnabledFunction&) = delete; + EyeoFilteringPrivateSetEnabledFunction& operator=( + const EyeoFilteringPrivateSetEnabledFunction&) = delete; +}; + +class EyeoFilteringPrivateIsEnabledFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.isEnabled", UNKNOWN) + EyeoFilteringPrivateIsEnabledFunction(); + + private: + ~EyeoFilteringPrivateIsEnabledFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateIsEnabledFunction( + const EyeoFilteringPrivateIsEnabledFunction&) = delete; + EyeoFilteringPrivateIsEnabledFunction& operator=( + const EyeoFilteringPrivateIsEnabledFunction&) = delete; +}; + +class EyeoFilteringPrivateGetAcceptableAdsUrlFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.getAcceptableAdsUrl", + UNKNOWN) + EyeoFilteringPrivateGetAcceptableAdsUrlFunction(); + + private: + ~EyeoFilteringPrivateGetAcceptableAdsUrlFunction() override; + + // ExtensionFunction: + ResponseAction Run() override; + + EyeoFilteringPrivateGetAcceptableAdsUrlFunction( + const EyeoFilteringPrivateGetAcceptableAdsUrlFunction&) = delete; + EyeoFilteringPrivateGetAcceptableAdsUrlFunction& operator=( + const EyeoFilteringPrivateGetAcceptableAdsUrlFunction&) = delete; +}; + +class EyeoFilteringPrivateSubscribeToFilterListFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.subscribeToFilterList", + UNKNOWN) + EyeoFilteringPrivateSubscribeToFilterListFunction(); + + private: + ~EyeoFilteringPrivateSubscribeToFilterListFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateSubscribeToFilterListFunction( + const EyeoFilteringPrivateSubscribeToFilterListFunction&) = delete; + EyeoFilteringPrivateSubscribeToFilterListFunction& operator=( + const EyeoFilteringPrivateSubscribeToFilterListFunction&) = delete; +}; + +class EyeoFilteringPrivateUnsubscribeFromFilterListFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.unsubscribeFromFilterList", + UNKNOWN) + EyeoFilteringPrivateUnsubscribeFromFilterListFunction(); + + private: + ~EyeoFilteringPrivateUnsubscribeFromFilterListFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateUnsubscribeFromFilterListFunction( + const EyeoFilteringPrivateUnsubscribeFromFilterListFunction&) = delete; + EyeoFilteringPrivateUnsubscribeFromFilterListFunction& operator=( + const EyeoFilteringPrivateUnsubscribeFromFilterListFunction&) = delete; +}; + +class EyeoFilteringPrivateGetFilterListsFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.getFilterLists", UNKNOWN) + EyeoFilteringPrivateGetFilterListsFunction(); + + private: + ~EyeoFilteringPrivateGetFilterListsFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateGetFilterListsFunction( + const EyeoFilteringPrivateGetFilterListsFunction&) = delete; + EyeoFilteringPrivateGetFilterListsFunction& operator=( + const EyeoFilteringPrivateGetFilterListsFunction&) = delete; +}; + +class EyeoFilteringPrivateAddAllowedDomainFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.addAllowedDomain", UNKNOWN) + EyeoFilteringPrivateAddAllowedDomainFunction(); + + private: + ~EyeoFilteringPrivateAddAllowedDomainFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateAddAllowedDomainFunction( + const EyeoFilteringPrivateAddAllowedDomainFunction&) = delete; + EyeoFilteringPrivateAddAllowedDomainFunction& operator=( + const EyeoFilteringPrivateAddAllowedDomainFunction&) = delete; +}; + +class EyeoFilteringPrivateRemoveAllowedDomainFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.removeAllowedDomain", + UNKNOWN) + EyeoFilteringPrivateRemoveAllowedDomainFunction(); + + private: + ~EyeoFilteringPrivateRemoveAllowedDomainFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateRemoveAllowedDomainFunction( + const EyeoFilteringPrivateRemoveAllowedDomainFunction&) = delete; + EyeoFilteringPrivateRemoveAllowedDomainFunction& operator=( + const EyeoFilteringPrivateRemoveAllowedDomainFunction&) = delete; +}; + +class EyeoFilteringPrivateGetAllowedDomainsFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.getAllowedDomains", UNKNOWN) + EyeoFilteringPrivateGetAllowedDomainsFunction(); + + private: + ~EyeoFilteringPrivateGetAllowedDomainsFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateGetAllowedDomainsFunction( + const EyeoFilteringPrivateGetAllowedDomainsFunction&) = delete; + EyeoFilteringPrivateGetAllowedDomainsFunction& operator=( + const EyeoFilteringPrivateGetAllowedDomainsFunction&) = delete; +}; + +class EyeoFilteringPrivateAddCustomFilterFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.addCustomFilter", UNKNOWN) + EyeoFilteringPrivateAddCustomFilterFunction(); + + private: + ~EyeoFilteringPrivateAddCustomFilterFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateAddCustomFilterFunction( + const EyeoFilteringPrivateAddCustomFilterFunction&) = delete; + EyeoFilteringPrivateAddCustomFilterFunction& operator=( + const EyeoFilteringPrivateAddCustomFilterFunction&) = delete; +}; + +class EyeoFilteringPrivateRemoveCustomFilterFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.removeCustomFilter", UNKNOWN) + EyeoFilteringPrivateRemoveCustomFilterFunction(); + + private: + ~EyeoFilteringPrivateRemoveCustomFilterFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateRemoveCustomFilterFunction( + const EyeoFilteringPrivateRemoveCustomFilterFunction&) = delete; + EyeoFilteringPrivateRemoveCustomFilterFunction& operator=( + const EyeoFilteringPrivateRemoveCustomFilterFunction&) = delete; +}; + +class EyeoFilteringPrivateGetCustomFiltersFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("eyeoFilteringPrivate.getCustomFilters", UNKNOWN) + EyeoFilteringPrivateGetCustomFiltersFunction(); + + private: + ~EyeoFilteringPrivateGetCustomFiltersFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateGetCustomFiltersFunction( + const EyeoFilteringPrivateGetCustomFiltersFunction&) = delete; + EyeoFilteringPrivateGetCustomFiltersFunction& operator=( + const EyeoFilteringPrivateGetCustomFiltersFunction&) = delete; +}; + +class EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION( + "eyeoFilteringPrivate.getSessionAllowedRequestsCount", + UNKNOWN) + EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction(); + + private: + ~EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction( + const EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction&) = + delete; + EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction& operator=( + const EyeoFilteringPrivateGetSessionAllowedRequestsCountFunction&) = + delete; +}; + +class EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION( + "eyeoFilteringPrivate.getSessionBlockedRequestsCount", + UNKNOWN) + EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction(); + + private: + ~EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction() override; + + ResponseAction Run() override; + + EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction( + const EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction&) = + delete; + EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction& operator=( + const EyeoFilteringPrivateGetSessionBlockedRequestsCountFunction&) = + delete; +}; + +} // namespace api + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_EYEO_FILTERING_PRIVATE_EYEO_FILTERING_PRIVATE_API_H_ diff --git a/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_apitest.cc b/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_apitest.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_apitest.cc @@ -0,0 +1,161 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . + +#include "chrome/browser/adblock/adblock_content_browser_client.h" +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/api/adblock_private.h" +#include "components/adblock/core/adblock_controller.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "content/public/test/browser_test.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +namespace { +enum class Mode { Normal, Incognito }; +} // namespace + +class EyeoFilteringPrivateApiTest : public ExtensionApiTest, + public testing::WithParamInterface { + public: + EyeoFilteringPrivateApiTest() {} + ~EyeoFilteringPrivateApiTest() override = default; + EyeoFilteringPrivateApiTest(const EyeoFilteringPrivateApiTest&) = delete; + EyeoFilteringPrivateApiTest& operator=(const EyeoFilteringPrivateApiTest&) = + delete; + + void SetUpCommandLine(base::CommandLine* command_line) override { + extensions::ExtensionApiTest::SetUpCommandLine(command_line); + if (IsIncognito()) { + command_line->AppendSwitch(switches::kIncognito); + } + } + + protected: + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + + auto* adblock_configuration = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + browser()->profile()) + ->GetAdblockFilteringConfiguration(); + if (adblock_configuration) { + adblock_configuration->RemoveCustomFilter( + adblock::kAllowlistEverythingFilter); + } + + AdblockContentBrowserClient::ForceAdblockProxyForTesting(); + } + + bool IsIncognito() { return GetParam() == Mode::Incognito; } + + bool RunTest(const std::string& subtest) { + const std::string page_url = "main.html?subtest=" + subtest; + return RunExtensionTest("eyeo_filtering_private", + {.extension_url = page_url.c_str()}, + {.allow_in_incognito = IsIncognito(), + .load_as_component = !IsIncognito()}); + } +}; + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + CreateRemoveAndGetConfigurations) { + EXPECT_TRUE(RunTest("createRemoveAndGetConfigurations")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + CreateRemoveAndGetConfigurationsWithPromises) { + EXPECT_TRUE(RunTest("createRemoveAndGetConfigurationsWithPromises")) + << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + EnableAndDisableConfiguration) { + EXPECT_TRUE(RunTest("enableAndDisableConfiguration")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + EnableAndDisableConfigurationWithPromises) { + EXPECT_TRUE(RunTest("enableAndDisableConfigurationWithPromises")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + AddAllowedDomainToCustomConfiguration) { + EXPECT_TRUE(RunTest("addAllowedDomainToCustomConfiguration")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + AddAllowedDomainToCustomConfigurationWithPromises) { + EXPECT_TRUE(RunTest("addAllowedDomainToCustomConfigurationWithPromises")) + << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + AddCustomFilterToCustomConfiguration) { + EXPECT_TRUE(RunTest("addCustomFilterToCustomConfiguration")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + AddCustomFilterToCustomConfigurationWithPromises) { + EXPECT_TRUE(RunTest("addCustomFilterToCustomConfigurationWithPromises")) + << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + SubscribeToFilterListInCustomConfiguration) { + EXPECT_TRUE(RunTest("subscribeToFilterListInCustomConfiguration")) + << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + SubscribeToFilterListInCustomConfigurationWithPromises) { + EXPECT_TRUE(RunTest("subscribeToFilterListInCustomConfigurationWithPromises")) + << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, MissingConfiguration) { + EXPECT_TRUE(RunTest("missingConfiguration")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, + MissingConfigurationWithPromises) { + EXPECT_TRUE(RunTest("missingConfigurationWithPromises")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, AllowedDomainsEvent) { + EXPECT_TRUE(RunTest("allowedDomainsEvent")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, EnabledStateEvent) { + EXPECT_TRUE(RunTest("enabledStateEvent")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, FilterListsEvent) { + EXPECT_TRUE(RunTest("filterListsEvent")) << message_; +} + +IN_PROC_BROWSER_TEST_P(EyeoFilteringPrivateApiTest, CustomFiltersEvent) { + EXPECT_TRUE(RunTest("customFiltersEvent")) << message_; +} + +INSTANTIATE_TEST_SUITE_P(All, + EyeoFilteringPrivateApiTest, + testing::Values(Mode::Normal, Mode::Incognito)); + +} // namespace extensions diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc --- a/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the +// components/adblock/LICENSE file. + #include "chrome/browser/extensions/api/settings_private/prefs_util.h" #include @@ -27,6 +31,7 @@ #include "chrome/browser/ssl/generated_https_first_mode_pref.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h" +#include "components/adblock/core/common/adblock_prefs.h" #include "components/autofill/core/common/autofill_prefs.h" #include "components/bookmarks/common/bookmark_pref_names.h" #include "components/browsing_data/core/pref_names.h" @@ -173,6 +178,20 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { return *s_allowlist; s_allowlist = new PrefsUtil::TypedPrefMap(); + // Adblock settings + (*s_allowlist)[adblock::common::prefs::kEnableAdblockLegacy] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_allowlist)[adblock::common::prefs::kEnableAcceptableAdsLegacy] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_allowlist)[adblock::common::prefs::kAdblockSubscriptionsLegacy] = + settings_api::PrefType::PREF_TYPE_LIST; + (*s_allowlist)[adblock::common::prefs::kAdblockCustomSubscriptionsLegacy] = + settings_api::PrefType::PREF_TYPE_LIST; + (*s_allowlist)[adblock::common::prefs::kAdblockAllowedDomainsLegacy] = + settings_api::PrefType::PREF_TYPE_LIST; + (*s_allowlist)[adblock::common::prefs::kAdblockCustomFiltersLegacy] = + settings_api::PrefType::PREF_TYPE_LIST; + // Miscellaneous (*s_allowlist)[::embedder_support::kAlternateErrorPagesEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; diff --git a/chrome/browser/extensions/browser_context_keyed_service_factories.cc b/chrome/browser/extensions/browser_context_keyed_service_factories.cc --- a/chrome/browser/extensions/browser_context_keyed_service_factories.cc +++ b/chrome/browser/extensions/browser_context_keyed_service_factories.cc @@ -1,6 +1,10 @@ // Copyright 2014 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the +// components/adblock/LICENSE file. #include "chrome/browser/extensions/browser_context_keyed_service_factories.h" diff --git a/chrome/browser/extensions/extension_function_registration_test.cc b/chrome/browser/extensions/extension_function_registration_test.cc --- a/chrome/browser/extensions/extension_function_registration_test.cc +++ b/chrome/browser/extensions/extension_function_registration_test.cc @@ -1,6 +1,10 @@ // Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the +// components/adblock/LICENSE file. #include "base/one_shot_event.h" #include "chrome/browser/extensions/extension_browsertest.h" @@ -56,6 +60,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionFunctionRegistrationTest, base::CompareCase::SENSITIVE)) { continue; } + // Eyeo extension API uses UNKNOWN; it's not used in histograms. + if (base::StartsWith(entry.function_name_, "adblockPrivate.") || + base::StartsWith(entry.function_name_, "eyeoFilteringPrivate.")) { + continue; + } + // Some undocumented, unlaunched APIs may also use UNKNOWN if it's unclear // (or unlikely) if they will ever launch. if (base::Contains(kAllowedUnknownHistogramEntries, diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json --- a/chrome/common/extensions/api/_api_features.json +++ b/chrome/common/extensions/api/_api_features.json @@ -1,6 +1,10 @@ // Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file. + // This features file defines extension APIs implemented under src/chrome. // See chrome/common/extensions/api/_features.md to understand this file, as @@ -95,6 +99,16 @@ "chrome://extensions/*" ] }], + "adblockPrivate": [{ + "dependencies": ["permission:adblockPrivate"], + "contexts": ["blessed_extension"] + }, { + "channel": "stable", + "contexts": ["webui"], + "matches": [ + "chrome://settings/*" + ] + }], "app": { "blocklist": [ "2FC374607C2DF285634B67C64A2E356C607091C3", // Quickoffice @@ -497,6 +511,14 @@ "channel": "stable", "contexts": [] }, + "eyeoFilteringPrivate": [{ + "dependencies": ["permission:eyeoFilteringPrivate"], + "contexts": ["blessed_extension"] + }, { + "channel": "stable", + "contexts": ["webui"], + "matches": ["chrome://settings/*"] + }], "fileBrowserHandler": { "dependencies": ["permission:fileBrowserHandler"], "contexts": ["blessed_extension"], 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 @@ -1,6 +1,10 @@ // Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file. + // This features file defines permissions for extension APIs implemented // under src/chrome. @@ -56,6 +60,10 @@ "5107DE9024C329EEA9C9A72D94C16723790C6422" // Apps Developer Tool Dev. ] }, + "adblockPrivate": { + "channel": "stable", + "extension_types": ["extension", "platform_app"] + }, "autofillPrivate": { "channel": "trunk", "extension_types": ["extension", "platform_app"], @@ -343,6 +351,10 @@ "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 new file mode 100644 --- /dev/null +++ b/chrome/common/extensions/api/adblock_private.idl @@ -0,0 +1,174 @@ +// 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 . + +// DEPRECATED: Prefer eyeoFilteringPrivate. +// adblockPrivate API. +// This is a private API exposing ability to control AdblockPlus. +// +// Devs: when modifying this file, ensure you update +// tools/typescript/definitions/adblock_private.d.ts as well. +// See DPD-1870 for more information. +namespace adblockPrivate { + +dictionary BuiltInSubscription { + // Subscription url + DOMString url; + // Subscription title (may be empty) + DOMString title; + // list of languages this subscription is relevant for. + DOMString[] languages; +}; + +dictionary Subscription { + // Subscription url + DOMString url; + // Current state of the subscription. Allowed values are: + // "Installed" + // "Preloaded" + // "Installing" + // See components/adblock/core/subscription/subscription.h for description. + DOMString installation_state; + // Subscription title (may be empty) + DOMString title; + // Subscription version (may be empty) + DOMString current_version; + // Time of last successful installation or update, in ISO 8601 format. + // May be passed directly to the Date constructor. + DOMString last_installation_time; +}; + +dictionary SessionStatsEntry { + // Subscription url or "adblock:custom" when custom filter was matched + DOMString url; + // Subscription hits + long count; +}; + +dictionary AdInfo { + // URL of the ad element which has been matched. + DOMString url; + // URLs of the parent frames. + DOMString[] parent_frame_urls; + // URL of subscription having the filter which matched for the ad. + DOMString subscription; + // Configuration name containing subscription with matched filer. + DOMString configuration_name; + // A string representation of matched ad's resource type. + // Possible values are: + // - "SCRIPT" + // - "IMAGE" + // - "STYLESHEET" + // - "OBJECT" + // - "SUBDOCUMENT" + // - "WEBSOCKET" + // - "WEBRTC" + // - "PING" + // - "XMLHTTPREQUEST" + // - "MEDIA" + // - "FONT" + // - "OTHER" (when none of the above matches) + // See: components/adblock/core/common/content_type.h + DOMString content_type; + // Tab id for which the event is fired. Defaults to -1, a numerical value of + // api::tabs::TAB_ID_NONE which means tab id cannot be obtained. + long tab_id; + // Window id for which the event is fired. Defaults to -1 which is a value + // of SessionID::InvalidValue() which means window id cannot be obtained. + long window_id; +}; + +callback BuiltInSubscriptionsCallback = void(BuiltInSubscription[] result); +callback SubscriptionsCallback = void(Subscription[] result); +callback StateCallback = void(boolean result); +callback CompletionCallback = void(); +callback ListCallback = void(DOMString[] result); +callback SessionStatsCallback = void(SessionStatsEntry[] result); + +interface Functions { + // Allows to turn Adblock on or off. + static void setEnabled(boolean enabled); + // Returns whether Adblock is on. + static void isEnabled(StateCallback callback); + // Allows to turn Acceptable Ads on or off. + static void setAcceptableAdsEnabled(boolean enabled); + // Returns whether Acceptable Ads is on. + static void isAcceptableAdsEnabled(StateCallback callback); + // Gets the list of built-in subscriptions. + static void getBuiltInSubscriptions(BuiltInSubscriptionsCallback callback); + // Adds a domain ads should not be blocked on. + static void addAllowedDomain(DOMString domain); + // Removes a domain ads should not be blocked on. + static void removeAllowedDomain(DOMString domain); + // Returns a list of domains ads are not blocked on. + static void getAllowedDomains(ListCallback callback); + // Adds a custom filter. + static void addCustomFilter(DOMString filter); + // Removes a custom filter. + static void removeCustomFilter(DOMString filter); + // Returns a list of custom filters a user set. + static void getCustomFilters(ListCallback callback); + // Returns number of allowlisted requests in a current session (runtime). + static void getSessionAllowedAdsCount(SessionStatsCallback callback); + // Returns number of blocked requests in a current session (runtime). + static void getSessionBlockedAdsCount(SessionStatsCallback callback); + // Compiles a list of currently installed subscriptions. Filter lists that are + // still being installed and don't yet participate in ad filtering also appear + // on this list and can be discerned by their installation_state. + static void getInstalledSubscriptions(SubscriptionsCallback callback); + // Subscribes to a filter list. If subscription was already done and not + // removed later by "uninstallSubscription()" call then this is a no-op call, + // otherwise it initiates a download. |url| must point to a https URL. + static void installSubscription(DOMString url, + optional CompletionCallback feedback); + // Removes a subscription to a filter list downloaded from |url| and deletes + // corresponding files from disk. Does nothing if the filter list was never + // subscribed to. + static void uninstallSubscription(DOMString url, + optional CompletionCallback feedback); +}; + +interface Events { + // Fired when an ad is explicitly allowed by an exception rule. + static void onAdAllowed(AdInfo info); + + // Fired when an ad is blocked. + static void onAdBlocked(AdInfo info); + + // Fired when a whole page is allowlisted. + static void onPageAllowed(AdInfo info); + + // Fired when a popup is blocked. + static void onPopupBlocked(AdInfo info); + + // Fired when a popup is allowlisted. + static void onPopupAllowed(AdInfo info); + + // Fired when a subscription has been updated. + static void onSubscriptionUpdated(DOMString subscription_url); + + // Fired when ad-filtering enable state changed + static void onEnabledStateChanged(); + + // Fired when subscription added or removed. + static void onFilterListsChanged(); + + // Fired when allowed domain list has been updated. + static void onAllowedDomainsChanged(); + + // Fired when custom filter added or removed. + static void onCustomFiltersChanged(); +}; + +}; diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni --- a/chrome/common/extensions/api/api_sources.gni +++ b/chrome/common/extensions/api/api_sources.gni @@ -1,6 +1,10 @@ # Copyright 2018 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# +# This source code is a part of eyeo Chromium SDK. +# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file. + import("//build/config/chromeos/ui_mode.gni") import("//build/config/features.gni") @@ -12,6 +16,7 @@ assert(enable_extensions) schema_sources_ = [ "accessibility_features.json", "activity_log_private.json", + "adblock_private.idl", "autofill_private.idl", "autotest_private.idl", "bookmark_manager_private.json", @@ -31,6 +36,7 @@ schema_sources_ = [ "downloads_internal.idl", "enterprise_hardware_platform.idl", "enterprise_reporting_private.idl", + "eyeo_filtering_private.idl", "font_settings.json", "gcm.json", "history.json", diff --git a/chrome/common/extensions/api/eyeo_filtering_private.idl b/chrome/common/extensions/api/eyeo_filtering_private.idl new file mode 100644 --- /dev/null +++ b/chrome/common/extensions/api/eyeo_filtering_private.idl @@ -0,0 +1,201 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . + +// eyeoFilteringPrivate API. +// This is a private API exposing control over resource filtering. +// +// Devs: when modifying this file, ensure you update +// tools/typescript/definitions/eyeo_filtering_private.d.ts as well. +// See DPD-1870 for more information. +namespace eyeoFilteringPrivate { + + dictionary Subscription { + // Subscription url + DOMString url; + // Current state of the subscription. Allowed values are: + // "Installed" + // "Preloaded" + // "Installing" + // See components/adblock/core/subscription/subscription.h for description. + DOMString installation_state; + // Subscription title (may be empty) + DOMString title; + // Subscription version (may be empty) + DOMString current_version; + // Time of last successful installation or update, in ISO 8601 format. + // May be passed directly to the Date constructor. + DOMString last_installation_time; + }; + + dictionary SessionStatsEntry { + // Subscription url or "adblock:custom" when custom filter was matched + DOMString url; + // Subscription hits + long count; + }; + + dictionary RequestInfo { + // URL of the request element which has been matched. + DOMString url; + // URLs of the parent frames. + DOMString[] parent_frame_urls; + // URL of subscription having the filter which matched for the request. + DOMString subscription; + // Configuration name containing subscription with matched filer. + DOMString configuration_name; + // A string representation of matched request's resource type. + // Possible values are: + // - "SCRIPT" + // - "IMAGE" + // - "STYLESHEET" + // - "OBJECT" + // - "SUBDOCUMENT" + // - "WEBSOCKET" + // - "WEBRTC" + // - "PING" + // - "XMLHTTPREQUEST" + // - "MEDIA" + // - "FONT" + // - "OTHER" (when none of the above matches) + // See: components/adblock/core/common/content_type.h + DOMString content_type; + // Tab id for which the event is fired. Defaults to -1, a numerical value of + // api::tabs::TAB_ID_NONE which means tab id cannot be obtained. + long tab_id; + // Window id for which the event is fired. Defaults to -1 which is a value + // of SessionID::InvalidValue() which means window id cannot be obtained. + long window_id; + }; + + callback CompletionCallback = void(); + callback ListCallback = void(DOMString[] result); + callback SessionStatsCallback = void(SessionStatsEntry[] result); + callback StateCallback = void(boolean result); + callback SubscriptionsCallback = void(Subscription[] result); + callback UrlCallback = void(DOMString result); + + // In order to use any configuration it needs to be created first + // in the backend, for example by using $(ref:createConfiguration) call. + // Names of all existing configurations can be fetched via + // (ref:getConfigurations) call. + interface Functions { + // Creates new filtering configuration. It is a no-op call if configuration + // already exists. + static void createConfiguration(DOMString config_name); + // Removes existing configuration, no-op when configuration does not exist. + // Use it only when |configuration| is no longer needed, otherwise prefer + // to disable |configuration| using (ref:setEnabled). + static void removeConfiguration(DOMString config_name); + // Returns a list of existing configurations. + [supportsPromises] static void getConfigurations(ListCallback callback); + // Allows to turn filtering configuration on or off. + [supportsPromises] static void setEnabled( + DOMString configuration, + boolean enabled, + optional CompletionCallback status); + // Returns whether filtering configuration is on. + [supportsPromises] static void isEnabled( + DOMString configuration, + StateCallback callback); + // Adds a domain requests should not be blocked on. + [supportsPromises] static void addAllowedDomain( + DOMString configuration, + DOMString domain, + optional CompletionCallback status); + // Removes a domain requests should not be blocked on. + [supportsPromises] static void removeAllowedDomain( + DOMString configuration, + DOMString domain, + optional CompletionCallback status); + // Returns a list of domains requests are not blocked on. + [supportsPromises] static void getAllowedDomains( + DOMString configuration, + ListCallback callback); + // Adds a custom filter. + [supportsPromises] static void addCustomFilter( + DOMString configuration, + DOMString filter, + optional CompletionCallback status); + // Removes a custom filter. + [supportsPromises] static void removeCustomFilter( + DOMString configuration, + DOMString filter, + optional CompletionCallback status); + // Returns a list of custom filters a user set. + [supportsPromises] static void getCustomFilters( + DOMString configuration, + ListCallback callback); + // Compiles a list of currently installed subscriptions. Filter lists that + // are still being installed and don't yet participate in filtering also + // appear on this list and can be discerned by their installation_state. + [supportsPromises] static void getFilterLists( + DOMString configuration, + SubscriptionsCallback callback); + // Subscribes to a filter list. If subscription was already done and not + // removed later by "unsubscribeFromFilterList()" call then this is a no-op + // call, otherwise it initiates a download. |url| must point to a https URL. + [supportsPromises] static void subscribeToFilterList( + DOMString configuration, + DOMString url, + optional CompletionCallback status); + // Removes a subscription to a filter list downloaded from |url| and deletes + // corresponding files from disk. Does nothing if the filter list was never + // subscribed to. + [supportsPromises] static void unsubscribeFromFilterList( + DOMString configuration, + DOMString url, + optional CompletionCallback status); + [supportsPromises] static void getSessionAllowedRequestsCount( + SessionStatsCallback callback); + // Returns number of blocked requests in a current session (runtime). + [supportsPromises] static void getSessionBlockedRequestsCount( + SessionStatsCallback callback); + // Returns Acceptable Ads url which can be passed to + // (ref:subscribeToFilterList) or (ref:unsubscribeFromFilterList). + [supportsPromises] static void getAcceptableAdsUrl(UrlCallback callback); + }; + + interface Events { + // Fired when an request is explicitly allowed by an exception rule. + static void onRequestAllowed(RequestInfo info); + + // Fired when an request is blocked. + static void onRequestBlocked(RequestInfo info); + + // Fired when a whole page is allowlisted in every filtering configuration. + static void onPageAllowed(RequestInfo info); + + // Fired when a popup is blocked. + static void onPopupBlocked(RequestInfo info); + + // Fired when a popup is allowlisted. + static void onPopupAllowed(RequestInfo info); + + // Fired when a subscription has been updated. + static void onSubscriptionUpdated(DOMString subscription_url); + + // Fired when configuration enable state changed + static void onEnabledStateChanged(DOMString config_name); + + // Fired when subscription added or removed. + static void onFilterListsChanged(DOMString config_name); + + // Fired when allowed domain list has been updated. + static void onAllowedDomainsChanged(DOMString config_name); + + // Fired when custom filter added or removed. + static void onCustomFiltersChanged(DOMString config_name); + }; +}; diff --git a/chrome/common/extensions/permissions/chrome_api_permissions.cc b/chrome/common/extensions/permissions/chrome_api_permissions.cc --- a/chrome/common/extensions/permissions/chrome_api_permissions.cc +++ b/chrome/common/extensions/permissions/chrome_api_permissions.cc @@ -1,6 +1,10 @@ // Copyright 2013 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the +// components/adblock/LICENSE file. #include "chrome/common/extensions/permissions/chrome_api_permissions.h" @@ -32,6 +36,8 @@ std::unique_ptr CreateAPIPermission( // ChromePermissionMessageProvider::GetPermissionMessages as well. constexpr APIPermissionInfo::InitInfo permissions_to_register[] = { // Register permissions for all extension types. + {APIPermissionID::kAdblockPrivate, "adblockPrivate", + APIPermissionInfo::kFlagCannotBeOptional}, {APIPermissionID::kBackground, "background", APIPermissionInfo::kFlagDoesNotRequireManagedSessionFullLoginWarning}, {APIPermissionID::kDeclarativeContent, "declarativeContent"}, @@ -82,6 +88,8 @@ constexpr APIPermissionInfo::InitInfo permissions_to_register[] = { {APIPermissionID::kEnterpriseNetworkingAttributes, "enterprise.networkingAttributes", APIPermissionInfo::kFlagDoesNotRequireManagedSessionFullLoginWarning}, + {APIPermissionID::kEyeoFilteringPrivate, "eyeoFilteringPrivate", + APIPermissionInfo::kFlagCannotBeOptional}, {APIPermissionID::kEnterprisePlatformKeys, "enterprise.platformKeys", APIPermissionInfo::kFlagDoesNotRequireManagedSessionFullLoginWarning}, {APIPermissionID::kFavicon, "favicon"}, diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc --- a/chrome/common/extensions/permissions/permission_set_unittest.cc +++ b/chrome/common/extensions/permissions/permission_set_unittest.cc @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// This source code is a part of eyeo Chromium SDK. +// Use of this source code is governed by the GPLv3 that can be found in the +// components/adblock/LICENSE file. + #include #include @@ -863,6 +867,10 @@ TEST(PermissionsTest, PermissionMessages) { skip.insert(APIPermissionID::kWmDesksPrivate); skip.insert(APIPermissionID::kSystemLog); + // Adblock API is also private (in separate block to minimize conflicts). + skip.insert(APIPermissionID::kAdblockPrivate); + skip.insert(APIPermissionID::kEyeoFilteringPrivate); + // Warned as part of host permissions. skip.insert(APIPermissionID::kDevtools); diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn @@ -3592,6 +3592,12 @@ if (!is_android) { ] } + ### Extension API module start + sources += [ + "../browser/extensions/api/adblock_private/adblock_private_apitest.cc", + "../browser/extensions/api/eyeo_filtering_private/eyeo_filtering_private_apitest.cc", + ] + ### Extension API module end if (is_chromeos_ash && enable_extensions) { deps += diff --git a/chrome/test/data/extensions/api_test/adblock_private/empty.js b/chrome/test/data/extensions/api_test/adblock_private/empty.js new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/adblock_private/empty.js @@ -0,0 +1,14 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . diff --git a/chrome/test/data/extensions/api_test/adblock_private/main.html b/chrome/test/data/extensions/api_test/adblock_private/main.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/adblock_private/main.html @@ -0,0 +1,29 @@ + + + + + + + Adblock private API test + + + +

chrome.adblockPrivate.* tests

+ + + diff --git a/chrome/test/data/extensions/api_test/adblock_private/manifest.json b/chrome/test/data/extensions/api_test/adblock_private/manifest.json new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/adblock_private/manifest.json @@ -0,0 +1,32 @@ +// 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 . +{ + "name": "adblockPrivate API test", + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyURY+BO7WO3B/dixoRSitosEKb1OOCsS1EF8dRoywUP+iQlHDJw2JL2A7d/E6JpoBQ/CUxX8lcHcsAs7zC31zb2iosBbfd5mCDd24bjLaIF/WNBRno6QYwbM/J7gCxn/aGFvAXdLnPhs2XFiP7iSQEY67NtTlah9EFGalB45UFUssrxFOXTFWT/gJmRIHqfCSUzHdPmFRJ1Sk6UpyZBPxp2MJAISbfTUhWIXa7WG+JxW95OEtNggfhYzX9wbCVSEU18RiMiMLdqNwHf7hYI30KiwrQhWcaB5kCnvJYEa43JggcE9xAaHV+1t2hSMyo5Xbz2YslI5UfDe8112hGVIUQIDAQAB", + "version": "0.1", + "incognito": "split", + "manifest_version": 2, + "background": { + "scripts": [ + "empty.js" + ] + }, + "description": "Test of chrome.adblockPrivate interface", + "permissions": [ + "adblockPrivate", + "eyeoFilteringPrivate", + "" + ] +} diff --git a/chrome/test/data/extensions/api_test/adblock_private/some-popup.html b/chrome/test/data/extensions/api_test/adblock_private/some-popup.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/adblock_private/some-popup.html @@ -0,0 +1,24 @@ + + + + Popup content + + +

I am a popup

+ + diff --git a/chrome/test/data/extensions/api_test/adblock_private/test.html b/chrome/test/data/extensions/api_test/adblock_private/test.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/adblock_private/test.html @@ -0,0 +1,25 @@ + + + + Test file + + + + popup link + + diff --git a/chrome/test/data/extensions/api_test/adblock_private/test.js b/chrome/test/data/extensions/api_test/adblock_private/test.js new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/adblock_private/test.js @@ -0,0 +1,538 @@ +// 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 . + +'use strict'; + +const urlParams = new URLSearchParams(window.location.search); + +// This class binds 1st argument for chrome.eyeoFilteringPrivate to 'adblock' +// configuration and redirects methods renamed in chrome.eyeoFilteringPrivate +// (eg. `onAdAllowed` => `onRequestAllowed`) which allows test code to +// seamlessly call the same methods on chrome.adblockPrivate and on +// chrome.eyeoFilteringPrivate. +class FilteringPrivateBoundWithAdblock { + constructor() { + this.delegate = chrome.eyeoFilteringPrivate; + this.configuration = 'adblock'; + this.getSessionAllowedAdsCount = + this.delegate.getSessionAllowedRequestsCount; + this.getSessionBlockedAdsCount = + this.delegate.getSessionBlockedRequestsCount; + this.onAdAllowed = this.delegate.onRequestAllowed; + this.onAdBlocked = this.delegate.onRequestBlocked; + this.onSubscriptionUpdated = this.delegate.onSubscriptionUpdated; + this.onAllowedDomainsUpdated = this.delegate.onAllowedDomainsUpdated; + const methodsToBind = [ + 'isEnabled', 'setEnabled', 'getAllowedDomains', 'addAllowedDomain', + 'removeAllowedDomain', 'getCustomFilters', 'addCustomFilter', + 'removeCustomFilter' + ]; + for (const method of methodsToBind) { + this[method] = function() { + const args = Array.from(arguments); + args.unshift(this.configuration); + this.delegate[method].apply(this.delegate, args); + } + } + const methodsToBindRenamed = new Map([ + ['installSubscription', 'subscribeToFilterList'], + ['uninstallSubscription', 'unsubscribeFromFilterList'], + ['getInstalledSubscriptions', 'getFilterLists'], + ]); + methodsToBindRenamed.forEach((value, key) => { + this[key] = + function() { + const args = Array.from(arguments); + args.unshift(this.configuration); + this.delegate[value].apply(this.delegate, args); + } + }); + } +}; + +// Set API object for tests, defaults to chrome.adblockPrivate +let apiObject = chrome.adblockPrivate; +if (urlParams.get('api') === 'eyeoFilteringPrivate') { + apiObject = new FilteringPrivateBoundWithAdblock; +} + +function findEnglishEasyList(item) { + console.log(item.title + ' ' + item.url + ' ' + item.languages); + return item.title.toLowerCase().includes('easylist') && + item.url.toLowerCase().includes('easylist') && + item.languages.includes('en'); +} + +function containsSubscription(subscriptions, url) { + for (const subscription of subscriptions) { + if (subscription.url === url) { + return true; + } + } + return false; +} + +function verifySessionStats(sessionStats, expectedSubscription) { + let index = -1; + for (let i = 0; i < sessionStats.length; i++) { + if (sessionStats[i].url === expectedSubscription) { + index = i; + break; + } + } + if (index == -1) { + chrome.test.fail('Failed: Missing entry in sessionStats'); + return; + } + if (sessionStats[index].count != 1) { + chrome.test.fail( + 'Failed: Expected a single subscription hit number, got: ' + + sessionStats[index].count); + return; + } +} + +function verifyEventData(event, expectedSubscription) { + if (event.tab_id < 1) { + chrome.test.fail('Failed: Wrong tab_id value'); + } + if (event.window_id < 1) { + chrome.test.fail('Failed: Wrong window_id value'); + } + if (event.content_type !== 'XMLHTTPREQUEST') { + chrome.test.fail('Failed: Wrong content_type value: ' + event.content_type); + } + if (event.subscription !== expectedSubscription) { + chrome.test.fail('Failed: Wrong subscription value: ' + event.subscription); + } +} + +function arrayEquals(a, b) { + if (a === b) + return true; + if (a === null || b === null) + return false; + if (a.length !== b.length) + return false; + for (var i = 0; i < a.length; i++) { + if (a[i] !== b[i]) + return false; + } + return true; +}; + +const availableTests = [ + function setEnabled_isEnabled() { + apiObject.setEnabled(false); + apiObject.isEnabled(function(enabled) { + if (enabled) + chrome.test.fail('Failed: ad blocking should NOT be enabled'); + }); + apiObject.setEnabled(true); + apiObject.isEnabled(function(enabled) { + if (enabled) + chrome.test.succeed(); + else + chrome.test.fail('Failed: ad blocking should be enabled'); + }); + }, + function setAAEnabled_isAAEnabled() { + chrome.adblockPrivate.setAcceptableAdsEnabled(false); + chrome.adblockPrivate.isAcceptableAdsEnabled(function(enabled) { + if (enabled) + chrome.test.fail('Failed: AA should NOT be enabled'); + }); + chrome.adblockPrivate.setAcceptableAdsEnabled(true); + chrome.adblockPrivate.getInstalledSubscriptions(function(selected) { + if (!containsSubscription( + selected, + 'https://easylist-downloads.adblockplus.org/exceptionrules.txt')) { + chrome.test.fail('Failed: AA subscription should be on the list'); + } + }); + chrome.adblockPrivate.isAcceptableAdsEnabled(function(enabled) { + if (enabled) + chrome.test.succeed(); + else + chrome.test.fail('Failed: AA should be enabled'); + }); + }, + function setAAEnabled_isAAEnabled_newAPI() { + const default_config = 'adblock'; + chrome.eyeoFilteringPrivate.getAcceptableAdsUrl(function(aa_url) { + chrome.eyeoFilteringPrivate.unsubscribeFromFilterList( + default_config, aa_url); + chrome.adblockPrivate.isAcceptableAdsEnabled(function(enabled) { + if (enabled) + chrome.test.fail('Failed: AA should NOT be enabled'); + }); + chrome.eyeoFilteringPrivate.getAcceptableAdsUrl(function(aa_url) { + chrome.eyeoFilteringPrivate.subscribeToFilterList( + default_config, aa_url); + chrome.adblockPrivate.isAcceptableAdsEnabled(function(enabled) { + if (enabled) + chrome.test.succeed(); + else + chrome.test.fail('Failed: AA should be enabled'); + }); + }); + }); + }, + function getBuiltInSubscriptions() { + chrome.adblockPrivate.getBuiltInSubscriptions(function(recommended) { + const found = recommended.find(findEnglishEasyList); + if (found) { + chrome.test.succeed(); + } else { + chrome.test.fail('Failed: invalid built-in subscriptions'); + } + }); + }, + // This test works because at the beginning getInstalledSubscriptions returns + // just default entries from recommended subscriptions. + function installedSubscriptionsDataSchema() { + const disabled = !!urlParams.get('disabled'); + if (disabled) { + apiObject.setEnabled(false); + } + apiObject.getInstalledSubscriptions(function(installed) { + for (const subscription of installed) { + if (!(subscription.installation_state == 'Unknown' && disabled || + subscription.installation_state != 'Unknown' && !disabled)) + chrome.test.fail( + 'Failed: Must contain valid "installation_state" property'); + if (!subscription.current_version && !disabled) + chrome.test.fail('Failed: Must contain "current_version" property'); + if (!subscription.last_installation_time && !disabled) + chrome.test.fail( + 'Failed: Must contain "last_installation_time" property'); + if (!subscription.title && !disabled) + chrome.test.fail('Failed: Must contain "title" property'); + if (!subscription.url) + chrome.test.fail('Failed: Must contain "url" property'); + chrome.test.succeed(); + } + }); + }, + function installSubscriptionInvalidURL() { + apiObject.installSubscription('http://', function() { + if (!chrome.runtime.lastError) + chrome.test.fail('Failed: invalid input accepted'); + else + chrome.test.succeed(); + }); + }, + function uninstallSubscriptionInvalidURL() { + apiObject.uninstallSubscription('http://', function() { + if (!chrome.runtime.lastError) + chrome.test.fail('Failed: invalid input accepted'); + else + chrome.test.succeed(); + }); + }, + function subscriptionsManagement() { + const kEasylist = urlParams.get('easylist'); + const kExceptionrules = urlParams.get('exceptions'); + const kABPFilters = urlParams.get('snippets'); + const kCustom = 'https://example.com/subscription.txt'; + + function containsDefaultSubscriptions(subscriptions) { + return containsSubscription(subscriptions, kEasylist) && + containsSubscription(subscriptions, kExceptionrules) && + containsSubscription(subscriptions, kABPFilters); + } + + if (urlParams.get('disabled')) { + apiObject.setEnabled(false); + } + + apiObject.getInstalledSubscriptions(function(installed) { + if (installed.length) { + if (!containsDefaultSubscriptions(installed)) { + chrome.test.fail('Failed: Should contain all default subscriptions'); + } + for (const subscription of installed) { + apiObject.uninstallSubscription(subscription.url); + } + } else { + chrome.test.fail('Failed: Should contain default subscriptions'); + } + apiObject.getInstalledSubscriptions(function(installed) { + if (installed.length) { + chrome.test.fail( + 'Failed: There shouldn\'t be any installed subscriptions'); + } + apiObject.installSubscription(kEasylist); + apiObject.installSubscription(kExceptionrules); + apiObject.installSubscription(kABPFilters); + apiObject.getInstalledSubscriptions(function(installed) { + if (installed.length) { + if (!containsDefaultSubscriptions(installed)) { + chrome.test.fail( + 'Failed: Should contain all default subscriptions'); + } + } else { + chrome.test.fail('Failed: Should contain default subscriptions'); + } + apiObject.installSubscription(kCustom); + apiObject.getInstalledSubscriptions(function(installed) { + if (!containsSubscription(installed, kCustom)) { + chrome.test.fail('Failed: Should contain custom subscription'); + } + apiObject.uninstallSubscription(kCustom); + apiObject.getInstalledSubscriptions(function(installed) { + if (containsSubscription(installed, kCustom)) { + chrome.test.fail( + 'Failed: Should not contain custom subscription'); + } else { + chrome.test.succeed(); + } + }); + }); + }); + }); + }); + }, + function allowedDomainsManagement() { + apiObject.getAllowedDomains(function(domains) { + if (domains.length) { + for (const domain of domains) + apiObject.removeAllowedDomain(domain); + } + + apiObject.getAllowedDomains(function(domains) { + if (domains.length) { + chrome.test.fail('Failed: There shouldn\'t be any allowed domains'); + return; + } + + apiObject.addAllowedDomain('foo.bar'); + apiObject.addAllowedDomain('bar.baz'); + apiObject.getAllowedDomains(function(domains) { + if (domains.length != 2) { + chrome.test.fail('Failed: There should be 2 allowed domains'); + return; + } + + if (domains.indexOf('foo.bar') == -1 || + domains.indexOf('bar.baz') == -1) { + chrome.test.fail('Failed: Didn\'t find expected allowed domains'); + return; + } + + apiObject.removeAllowedDomain('foo.bar'); + apiObject.removeAllowedDomain('bar.baz'); + + apiObject.getAllowedDomains(function(domains) { + if (domains.length) + chrome.test.fail('Failed: Still have allowed domains'); + else + chrome.test.succeed(); + }); + }); + }); + }); + }, + function customFiltersManagement() { + apiObject.getCustomFilters(function(filters) { + if (filters.length) { + for (const filter of filters) + apiObject.removeCustomFilter(filter); + } + + apiObject.getCustomFilters(function(filters) { + if (filters.length) { + chrome.test.fail('Failed: There shouldn\'t be any custom filters'); + return; + } + + apiObject.addCustomFilter('foo.bar'); + apiObject.addCustomFilter('bar.baz'); + apiObject.getCustomFilters(function(filters) { + if (filters.length != 2) { + chrome.test.fail('Failed: There should be 2 custom filters'); + return; + } + + if (filters.indexOf('foo.bar') == -1 || + filters.indexOf('bar.baz') == -1) { + chrome.test.fail('Failed: Didn\'t find expected custom filters'); + return; + } + + apiObject.removeCustomFilter('foo.bar'); + apiObject.removeCustomFilter('bar.baz'); + + apiObject.getCustomFilters(function(filters) { + if (filters.length) + chrome.test.fail('Failed: Still have custom filters'); + else + chrome.test.succeed(); + }); + }); + }); + }); + }, + function adBlockedEvents() { + const expectedSubscription = urlParams.get('subscription_url'); + apiObject.onSubscriptionUpdated.addListener(function(subscription) { + if (subscription !== expectedSubscription) + chrome.test.fail('Failed: Unexpected subscription: ' + subscription); + + apiObject.onAdBlocked.addListener(function(e) { + verifyEventData(e, expectedSubscription); + if (e.url.includes('test1.png')) { + chrome.test.succeed(); + } + }); + const request = new XMLHttpRequest(); + const handler = function() {}; + request.onload = handler; + request.onerror = handler; + request.open('GET', 'https://example.com/test1.png', true); + request.send(); + }); + apiObject.installSubscription(expectedSubscription); + }, + function adAllowedEvents() { + const expectedSubscription = urlParams.get('subscription_url'); + apiObject.onSubscriptionUpdated.addListener(function(subscription) { + if (subscription !== expectedSubscription) + chrome.test.fail('Failed: Unexpected subscription: ' + subscription); + + apiObject.onAdAllowed.addListener(function(e) { + verifyEventData(e, expectedSubscription); + if (e.url.includes('test2.png')) { + chrome.test.succeed(); + } + }); + const request = new XMLHttpRequest(); + const handler = function() {}; + request.onload = handler; + request.onerror = handler; + request.open('GET', 'https://example.com/test2.png', true); + request.send(); + }); + apiObject.installSubscription(expectedSubscription); + }, + function sessionStats() { + const expectedSubscription = urlParams.get('subscription_url'); + // Verify that subscription was downloaded and update event triggered, + // then verify correct number of a session blocked and allowed ads count + // for this subscription. + apiObject.onSubscriptionUpdated.addListener(function(subscription) { + if (subscription !== expectedSubscription) + chrome.test.fail('Failed: Unexpected subscription: ' + subscription); + + const handler = function() { + apiObject.getSessionBlockedAdsCount(function(sessionStats) { + verifySessionStats(sessionStats, expectedSubscription); + const handler = function() { + apiObject.getSessionAllowedAdsCount(function(sessionStats) { + verifySessionStats(sessionStats, expectedSubscription); + chrome.test.succeed(); + }); + }; + const request = new XMLHttpRequest(); + request.onload = handler; + request.onerror = handler; + request.open('GET', 'https://example.com/test4.png', true); + request.send(); + }); + }; + const request = new XMLHttpRequest(); + request.onload = handler; + request.onerror = handler; + request.open('GET', 'https://example.com/test3.png', true); + request.send(); + }); + apiObject.installSubscription(expectedSubscription); + }, + function allowedDomainsEvent() { + const domain = 'domain.com'; + let data = [domain]; + let attempts = 2; + chrome.adblockPrivate.onAllowedDomainsChanged.addListener(function() { + chrome.adblockPrivate.getAllowedDomains(function(domains) { + if (!arrayEquals(data, domains)) { + chrome.test.fail('Unexpected domain list'); + } + if (--attempts == 0) { + chrome.test.succeed(); + } + }); + }); + chrome.adblockPrivate.addAllowedDomain(domain); + data = []; + chrome.adblockPrivate.removeAllowedDomain(domain); + }, + function enabledStateEvent() { + let state = false; + let attempts = 2; + chrome.adblockPrivate.onEnabledStateChanged.addListener(function() { + chrome.adblockPrivate.isEnabled(function(enabled) { + if (enabled !== state) { + chrome.test.fail('Unexpected enabled state'); + } + if (--attempts == 0) { + chrome.test.succeed(); + } + }); + }); + chrome.adblockPrivate.setEnabled(false); + state = true; + chrome.adblockPrivate.setEnabled(true); + }, + function filterListsEvent() { + const kCustom = 'https://example.com/subscription.txt'; + let data = [kCustom]; + let attempts = 2; + chrome.adblockPrivate.onFilterListsChanged.addListener(function() { + chrome.adblockPrivate.getInstalledSubscriptions(function(custom) { + if (!(data.length + 3, custom.length)) { + chrome.test.fail('Unexpected subscription list'); + } + if (--attempts == 0) { + chrome.test.succeed(); + } + data = []; + chrome.adblockPrivate.uninstallSubscription(kCustom); + }); + }); + chrome.adblockPrivate.installSubscription(kCustom); + }, + function customFiltersEvent() { + const filter = 'foo.bar'; + let data = [filter]; + let attempts = 2; + chrome.adblockPrivate.onCustomFiltersChanged.addListener(function() { + chrome.adblockPrivate.getCustomFilters(function(filters) { + if (!arrayEquals(data, filters)) { + chrome.test.fail('Unexpected custom filter list'); + } + if (--attempts == 0) { + chrome.test.succeed(); + } + }); + }); + chrome.adblockPrivate.addCustomFilter(filter); + data = []; + chrome.adblockPrivate.removeCustomFilter(filter); + }, +]; + +chrome.test.runTests(availableTests.filter(function(op) { + return op.name == urlParams.get('subtest'); +})); diff --git a/chrome/test/data/extensions/api_test/eyeo_filtering_private/empty.js b/chrome/test/data/extensions/api_test/eyeo_filtering_private/empty.js new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/eyeo_filtering_private/empty.js @@ -0,0 +1,14 @@ +// This file is part of eyeo Chromium SDK, +// Copyright (C) 2006-present eyeo GmbH +// +// eyeo Chromium SDK is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 3 as +// published by the Free Software Foundation. +// +// eyeo Chromium SDK is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with eyeo Chromium SDK. If not, see . diff --git a/chrome/test/data/extensions/api_test/eyeo_filtering_private/main.html b/chrome/test/data/extensions/api_test/eyeo_filtering_private/main.html new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/eyeo_filtering_private/main.html @@ -0,0 +1,29 @@ + + + + + + + Eyeo filtering private API test + + + +

chrome.eyeoFilteringPrivate.* tests

+ + + diff --git a/chrome/test/data/extensions/api_test/eyeo_filtering_private/manifest.json b/chrome/test/data/extensions/api_test/eyeo_filtering_private/manifest.json new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/eyeo_filtering_private/manifest.json @@ -0,0 +1,31 @@ +// 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 . +{ + "name": "eyeoFilteringPrivate API test", + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkRxyv6tzqVnC+2gR3QxaN32fynw6GBZd9CXH8huHopx5xZ0fE1gScXnnjna7YW2sc6dhZZv326lRwmsJRwJN+RZpxBQbTD4CuCiqfUo+Xdyigh91QqyScLLRmg3SPsBBF9X/M50LO/6MD2eETiWbQQRy1TXNz52lt6NtjXKmS2lVZzR/jnGyAA96vMOmxeNIJmSYHFOHlSIphAJr/Erd0v1ZcBjJnZxqSrKZwUTHHc/FxcN1YqJkU/6O6gjMLNo3Uv33bqRAYmGUq+TTftwLg2hzEE1OllThcF9VVmQ3HZ5eTqqw/XP/tiQ/vUBflKer2mSVk708VBNpktao44kAtQIDAQAB", + "version": "0.1", + "incognito": "split", + "manifest_version": 3, + "background": { + "service_worker": "empty.js" + }, + "description": "Test of chrome.eyeoFilteringPrivate interface", + "host_permissions": [ + "" + ], + "permissions": [ + "eyeoFilteringPrivate" + ] +} diff --git a/chrome/test/data/extensions/api_test/eyeo_filtering_private/test.js b/chrome/test/data/extensions/api_test/eyeo_filtering_private/test.js new file mode 100644 --- /dev/null +++ b/chrome/test/data/extensions/api_test/eyeo_filtering_private/test.js @@ -0,0 +1,460 @@ +// 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 . + +'use strict'; + +const custom_config = 'custom'; + +async function pollUntil(predicate, pollEveryMs) { + return new Promise(r => { + const id = setInterval(() => { + let ret; + if (ret = predicate()) { + clearInterval(id); + r(ret); + } + }, pollEveryMs); + }); +} + +function containsSubscription(subscriptions, url) { + for (const subscription of subscriptions) { + if (subscription.url === url) { + return true; + } + } + return false; +} + +function arrayEquals(a, b) { + if (a === b) + return true; + if (a === null || b === null) + return false; + if (a.length !== b.length) + return false; + for (var i = 0; i < a.length; i++) { + if (a[i] !== b[i]) + return false; + } + return true; +}; + +const availableTests = [ + function createRemoveAndGetConfigurations() { + chrome.eyeoFilteringPrivate.getConfigurations(function(configs) { + if (configs.includes(custom_config)) { + chrome.test.fail('Failed: There should NOT be a custom configuration'); + } + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.getConfigurations(function(configs) { + if (!configs.includes(custom_config)) { + chrome.test.fail('Failed: There should be a custom configuration'); + } + chrome.eyeoFilteringPrivate.removeConfiguration(custom_config); + chrome.eyeoFilteringPrivate.getConfigurations(function(configs) { + if (configs.includes(custom_config)) { + chrome.test.fail('Failed: There should NOT be a custom configuration'); + } + chrome.test.succeed(); + }); + }); + }); + }, + function createRemoveAndGetConfigurationsWithPromises() { + chrome.eyeoFilteringPrivate.getConfigurations().then(configs => { + if (configs.includes(custom_config)) { + chrome.test.fail('Failed: There should NOT be a custom configuration'); + } + }); + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.getConfigurations().then(configs => { + if (!configs.includes(custom_config)) { + chrome.test.fail('Failed: There should be a custom configuration'); + } + chrome.eyeoFilteringPrivate.removeConfiguration(custom_config); + chrome.eyeoFilteringPrivate.getConfigurations().then(configs => { + if (configs.includes(custom_config)) { + chrome.test.fail('Failed: There should NOT be a custom configuration'); + } + chrome.test.succeed(); + }); + }); + }, + function enableAndDisableConfiguration() { + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.isEnabled(custom_config, function(enabled) { + if (!enabled) { + chrome.test.fail('Failed: Configuration should be enabled'); + } + }); + chrome.eyeoFilteringPrivate.setEnabled(custom_config, false); + chrome.eyeoFilteringPrivate.isEnabled(custom_config, function(enabled) { + if (enabled) { + chrome.test.fail('Failed: Configuration should NOT be enabled'); + } + }); + chrome.eyeoFilteringPrivate.setEnabled(custom_config, true); + chrome.eyeoFilteringPrivate.isEnabled(custom_config, function(enabled) { + if (!enabled) { + chrome.test.fail('Failed: Configuration should be enabled'); + } + chrome.test.succeed(); + }); + }, + function enableAndDisableConfigurationWithPromises() { + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.isEnabled(custom_config).then(enabled => { + if (!enabled) { + chrome.test.fail('Failed: Configuration should be enabled'); + } + }); + chrome.eyeoFilteringPrivate.setEnabled(custom_config, false); + chrome.eyeoFilteringPrivate.isEnabled(custom_config) + .then(enabled => { + if (enabled) { + chrome.test.fail('Failed: Configuration should NOT be enabled'); + } + }) + .catch(error => chrome.test.fail(error)); + chrome.eyeoFilteringPrivate.setEnabled(custom_config, true); + chrome.eyeoFilteringPrivate.isEnabled(custom_config).then(enabled => { + if (!enabled) { + chrome.test.fail('Failed: Configuration should be enabled'); + } + chrome.test.succeed(); + }); + }, + function addAllowedDomainToCustomConfiguration() { + const domain = 'domain.com'; + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.addAllowedDomain(custom_config, domain); + chrome.eyeoFilteringPrivate.getAllowedDomains( + custom_config, function(domains) { + if (domains.length != 1) { + chrome.test.fail('Failed: There should be a custom domain'); + } + if (domains.indexOf(domain) == -1) { + chrome.test.fail('Failed: Didn\'t find expected custom domain'); + } + }); + chrome.eyeoFilteringPrivate.removeAllowedDomain(custom_config, domain); + chrome.eyeoFilteringPrivate.getAllowedDomains( + custom_config, function(domains) { + if (domains.length) { + chrome.test.fail('Failed: Still have custom domain(s)'); + } + chrome.test.succeed(); + }); + }, + async function addAllowedDomainToCustomConfigurationWithPromises() { + const domain = 'domain.com'; + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + await chrome.eyeoFilteringPrivate.addAllowedDomain(custom_config, domain); + chrome.eyeoFilteringPrivate.getAllowedDomains(custom_config) + .then(domains => { + if (domains.length != 1) { + chrome.test.fail('Failed: There should be a custom domain'); + } + if (domains.indexOf(domain) == -1) { + chrome.test.fail('Failed: Didn\'t find expected custom domain'); + } + }); + await chrome.eyeoFilteringPrivate.removeAllowedDomain( + custom_config, domain); + chrome.eyeoFilteringPrivate.getAllowedDomains(custom_config) + .then(domains => { + if (domains.length) { + chrome.test.fail('Failed: Still have custom domain(s)'); + } + chrome.test.succeed(); + }); + }, + function addCustomFilterToCustomConfiguration() { + const filter = '||foo.bar'; + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.addCustomFilter(custom_config, filter); + chrome.eyeoFilteringPrivate.getCustomFilters( + custom_config, function(filters) { + if (filters.length != 1) { + chrome.test.fail('Failed: There should be a custom filter'); + } + if (filters.indexOf(filter) == -1) { + chrome.test.fail('Failed: Didn\'t find expected custom filter'); + } + }); + chrome.eyeoFilteringPrivate.removeCustomFilter(custom_config, filter); + chrome.eyeoFilteringPrivate.getCustomFilters( + custom_config, function(filters) { + if (filters.length) { + chrome.test.fail('Failed: Still have custom filter(s)'); + } + chrome.test.succeed(); + }); + }, + async function addCustomFilterToCustomConfigurationWithPromises() { + const filter = '||foo.bar'; + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + await chrome.eyeoFilteringPrivate.addCustomFilter(custom_config, filter); + chrome.eyeoFilteringPrivate.getCustomFilters(custom_config) + .then(filters => { + if (filters.length != 1) { + chrome.test.fail('Failed: There should be a custom filter'); + } + if (filters.indexOf(domain) == -1) { + chrome.test.fail('Failed: Didn\'t find expected custom filter'); + } + }); + await chrome.eyeoFilteringPrivate.removeCustomFilter(custom_config, filter); + chrome.eyeoFilteringPrivate.getCustomFilters(custom_config) + .then(filters => { + if (filters.length) { + chrome.test.fail('Failed: Still have custom filter(s)'); + } + chrome.test.succeed(); + }); + }, + function subscribeToFilterListInCustomConfiguration() { + const subscription = 'https://example.com/subscription.txt'; + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.subscribeToFilterList( + custom_config, subscription, function() { + chrome.eyeoFilteringPrivate.getFilterLists( + custom_config, function(subscriptions) { + if (subscriptions.length != 1) { + chrome.test.fail( + 'Failed: There should be a custom subscription'); + return; + } + if (!containsSubscription(subscriptions, subscription)) { + chrome.test.fail( + 'Failed: Didn\'t find expected custom subscription'); + return; + } + chrome.eyeoFilteringPrivate.unsubscribeFromFilterList( + custom_config, subscription, function() { + chrome.eyeoFilteringPrivate.getFilterLists( + custom_config, function(subscriptions) { + if (subscriptions.length) { + chrome.test.fail( + 'Failed: Still have custom subscription(s)'); + } + chrome.test.succeed(); + }); + }); + }); + }); + }, + async function subscribeToFilterListInCustomConfigurationWithPromises() { + const subscription = 'https://example.com/subscription.txt'; + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + await chrome.eyeoFilteringPrivate.subscribeToFilterList( + custom_config, subscription); + chrome.eyeoFilteringPrivate.getFilterLists(custom_config) + .then(subscriptions => { + if (subscriptions.length != 1) { + chrome.test.fail('Failed: There should be a custom subscription'); + return; + } + if (!containsSubscription(subscriptions, subscription)) { + chrome.test.fail( + 'Failed: Didn\'t find expected custom subscription'); + return; + } + }); + await chrome.eyeoFilteringPrivate.unsubscribeFromFilterList( + custom_config, subscription); + chrome.eyeoFilteringPrivate.getFilterLists(custom_config) + .then(subscriptions => { + if (subscriptions.length) { + chrome.test.fail('Failed: Still have custom subscription(s)'); + } + chrome.test.succeed(); + }); + }, + async function missingConfiguration() { + const input = 'https://dummy.com'; + const expectedError = 'Configuration with name \'custom\' does not exist!'; + const setters = [ + 'subscribeToFilterList', 'unsubscribeFromFilterList', 'addAllowedDomain', + 'removeAllowedDomain', 'addCustomFilter', 'removeCustomFilter' + ]; + const getters = [ + 'isEnabled', 'getFilterLists', 'getAllowedDomains', 'getCustomFilters' + ]; + const allMethodsCount = 1 + setters.length + getters.length; + let counter = 0; + chrome.eyeoFilteringPrivate.setEnabled(custom_config, false, function() { + if (!chrome.runtime.lastError) { + chrome.test.fail('Failed: missing configuration accepted'); + } + chrome.test.assertEq(expectedError, chrome.runtime.lastError.message); + ++counter; + }); + for (const method of setters) { + chrome.eyeoFilteringPrivate[method](custom_config, input, function() { + if (!chrome.runtime.lastError) { + chrome.test.fail('Failed: missing configuration accepted'); + } + chrome.test.assertEq(expectedError, chrome.runtime.lastError.message); + ++counter; + }); + } + for (const method of getters) { + chrome.eyeoFilteringPrivate[method](custom_config, function(result) { + if (!chrome.runtime.lastError) { + chrome.test.fail('Failed: missing configuration accepted'); + } + chrome.test.assertEq(expectedError, chrome.runtime.lastError.message); + ++counter; + }); + } + await pollUntil(() => counter === allMethodsCount, 100); + chrome.test.succeed(); + }, + async function missingConfigurationWithPromises() { + const input = 'https://dummy.com'; + const expectedError = + 'Error: Configuration with name \'custom\' does not exist!' + const setters = [ + 'subscribeToFilterList', 'unsubscribeFromFilterList', 'addAllowedDomain', + 'removeAllowedDomain', 'addCustomFilter', 'removeCustomFilter' + ]; + const getters = [ + 'isEnabled', 'getFilterLists', 'getAllowedDomains', 'getCustomFilters' + ]; + const allMethodsCount = 1 + setters.length + getters.length; + let counter = 0; + const errorHandler = function(error) { + chrome.test.assertEq(expectedError, error.toString()); + ++counter; + }; + await chrome.eyeoFilteringPrivate.setEnabled(custom_config, false) + .catch(error => errorHandler(error)); + for (const method of setters) { + await chrome.eyeoFilteringPrivate[method](custom_config, input) + .catch(error => errorHandler(error)); + } + for (const method of getters) { + await chrome.eyeoFilteringPrivate[method](custom_config) + .catch(error => errorHandler(error)); + } + if (counter == allMethodsCount) { + chrome.test.succeed(); + } else { + chrome.test.fail('Failed: expected missing configuration for every call'); + } + }, + function allowedDomainsEvent() { + const domain = 'domain.com'; + let data = [domain]; + let attempts = 2; + chrome.eyeoFilteringPrivate.onAllowedDomainsChanged.addListener(function( + config_name) { + if (config_name != custom_config) { + chrome.test.fail('Failed: Wrong config name'); + } + chrome.eyeoFilteringPrivate.getAllowedDomains( + custom_config, function(domains) { + if (!arrayEquals(data, domains)) { + chrome.test.fail('Unexpected domain list'); + } + if (--attempts == 0) { + chrome.test.succeed(); + } + }); + }); + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.addAllowedDomain(custom_config, domain); + data = []; + chrome.eyeoFilteringPrivate.removeAllowedDomain(custom_config, domain); + }, + function enabledStateEvent() { + let state = false; + let attempts = 2; + chrome.eyeoFilteringPrivate.onEnabledStateChanged.addListener(function( + config_name) { + if (config_name != custom_config) { + chrome.test.fail('Failed: Wrong config name'); + } + chrome.eyeoFilteringPrivate.isEnabled(custom_config, function(enabled) { + if (enabled !== state) { + chrome.test.fail('Unexpected enabled state'); + } + if (--attempts == 0) { + chrome.test.succeed(); + } + }); + }); + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.setEnabled(custom_config, false); + state = true; + chrome.eyeoFilteringPrivate.setEnabled(custom_config, true); + }, + function filterListsEvent() { + const domain = 'http://domain.com/'; + let data = [domain]; + let attempts = 2; + chrome.eyeoFilteringPrivate.onFilterListsChanged.addListener(function( + config_name) { + if (config_name != custom_config) { + chrome.test.fail('Failed: Wrong config name'); + } + chrome.eyeoFilteringPrivate.getFilterLists( + custom_config, function(custom) { + if (!arrayEquals(data, custom)) { + chrome.test.fail('Unexpected subscription list'); + } + if (--attempts == 0) { + chrome.test.succeed(); + } + }); + }); + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.subscribeToFilterList(custom_config, domain); + data = []; + chrome.eyeoFilteringPrivate.unsubscribeFromFilterList( + custom_config, domain); + }, + function customFiltersEvent() { + const filter = 'foo.bar'; + let data = [filter]; + let attempts = 2; + chrome.eyeoFilteringPrivate.onCustomFiltersChanged.addListener(function( + config_name) { + if (config_name != custom_config) { + chrome.test.fail('Failed: Wrong config name'); + } + chrome.eyeoFilteringPrivate.getCustomFilters( + custom_config, function(filters) { + if (!arrayEquals(data, filters)) { + chrome.test.fail('Unexpected custom filter list'); + } + if (--attempts == 0) { + chrome.test.succeed(); + } + }); + }); + chrome.eyeoFilteringPrivate.createConfiguration(custom_config); + chrome.eyeoFilteringPrivate.addCustomFilter(custom_config, filter); + data = []; + chrome.eyeoFilteringPrivate.removeCustomFilter(custom_config, filter); + }, +]; + +const urlParams = new URLSearchParams(window.location.search); +chrome.test.runTests(availableTests.filter(function(op) { + return op.name == urlParams.get('subtest'); +})); diff --git a/extensions/browser/extension_event_histogram_value.h b/extensions/browser/extension_event_histogram_value.h --- a/extensions/browser/extension_event_histogram_value.h +++ b/extensions/browser/extension_event_histogram_value.h @@ -1,6 +1,10 @@ // 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. #ifndef EXTENSIONS_BROWSER_EXTENSION_EVENT_HISTOGRAM_VALUE_H_ #define EXTENSIONS_BROWSER_EXTENSION_EVENT_HISTOGRAM_VALUE_H_ @@ -40,7 +44,8 @@ enum HistogramValue { APP_WINDOW_ON_MAXIMIZED = 19, APP_WINDOW_ON_MINIMIZED = 20, APP_WINDOW_ON_RESTORED = 21, - DELETED_AUDIO_MODEM_ON_RECEIVED = 22, + EYEO_EVENT = 22, // Reusing a DELETED_AUDIO_MODEM_ON_RECEIVED to avoid merge + // conflicts. DELETED_AUDIO_MODEM_ON_TRANSMIT_FAIL = 23, DELETED_AUDIO_ON_DEVICE_CHANGED = 24, AUDIO_ON_DEVICES_CHANGED = 25, diff --git a/extensions/common/mojom/api_permission_id.mojom b/extensions/common/mojom/api_permission_id.mojom --- a/extensions/common/mojom/api_permission_id.mojom +++ b/extensions/common/mojom/api_permission_id.mojom @@ -1,6 +1,10 @@ // Copyright 2021 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. module extensions.mojom; @@ -279,6 +283,8 @@ enum APIPermissionID { kAccessibilityServicePrivate = 252, kUserScripts = 253, kChromeOSBluetoothPeripheralsInfo = 254, + kAdblockPrivate = 999, + kEyeoFilteringPrivate = 1000, // Add new entries at the end of the enum and be sure to update the // "ExtensionPermission3" enum in tools/metrics/histograms/enums.xml diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml @@ -17,6 +17,9 @@ https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histogra Please pretty-print and validate your edits by running the pretty_print.py and validate_format.py scripts in the same directory as this file before uploading your change for review. These are checked by presubmit scripts. + +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. --> @@ -35304,7 +35307,7 @@ Called by update_extension_histograms.py.--> - + @@ -38452,6 +38455,8 @@ Called by update_extension_permission.py.--> + + diff --git a/tools/typescript/definitions/adblock_private.d.ts b/tools/typescript/definitions/adblock_private.d.ts new file mode 100644 --- /dev/null +++ b/tools/typescript/definitions/adblock_private.d.ts @@ -0,0 +1,172 @@ +// 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 . + +/** @fileoverview Type definitions for chrome.adblockPrivate API. */ + +import {ChromeEvent} from './chrome_event.js'; + +declare global { + export namespace chrome { + export namespace adblockPrivate { + + export interface BuiltInSubscription { + url: string; + title: string; + languages: string[]; + } + + export interface Subscription { + url: string; + installation_state: string; + title: string; + current_version: string; + last_installation_time: string; + } + + export interface SessionStatsEntry { + url: string; + count: number; + } + + export interface AdInfo { + url: string; + parent_frame_urls: string[]; + subscription: string; + configuration_name: string; + content_type: string; + tab_id: number; + window_id: number; + } + + export const onAdAllowed: ChromeEvent<( + info: AdInfo, + ) => void>; + + export const onAdBlocked: ChromeEvent<( + info: AdInfo, + ) => void>; + + export const onPageAllowed: ChromeEvent<( + info: AdInfo, + ) => void>; + + export const onPopupBlocked: ChromeEvent<( + info: AdInfo, + ) => void>; + + export const onPopupAllowed: ChromeEvent<( + info: AdInfo, + ) => void>; + + export const onSubscriptionUpdated: ChromeEvent<( + subscription_url: string, + ) => void>; + + export const onEnabledStateChanged: ChromeEvent<( + ) => void>; + + export const onFilterListsChanged: ChromeEvent<( + ) => void>; + + export const onAllowedDomainsChanged: ChromeEvent<( + ) => void>; + + export const onCustomFiltersChanged: ChromeEvent<( + ) => void>; + + export function setEnabled( + enabled: boolean, + ): void; + + export function isEnabled( + callback: ( + result: boolean, + ) => void, + ): void; + + export function setAcceptableAdsEnabled( + enabled: boolean, + ): void; + + export function isAcceptableAdsEnabled( + callback: ( + result: boolean, + ) => void, + ): void; + + export function getBuiltInSubscriptions( + callback: ( + result: BuiltInSubscription[], + ) => void, + ): void; + + export function addAllowedDomain( + domain: string, + ): void; + + export function removeAllowedDomain( + domain: string, + ): void; + + export function getAllowedDomains( + callback: ( + result: string[], + ) => void, + ): void; + + export function addCustomFilter( + filter: string, + ): void; + + export function removeCustomFilter( + filter: string, + ): void; + + export function getCustomFilters( + callback: ( + result: string[], + ) => void, + ): void; + + export function getSessionAllowedAdsCount( + callback: ( + result: SessionStatsEntry[], + ) => void, + ): void; + + export function getSessionBlockedAdsCount( + callback: ( + result: SessionStatsEntry[], + ) => void, + ): void; + + export function getInstalledSubscriptions( + callback: ( + result: Subscription[], + ) => void, + ): void; + + export function installSubscription( + url: string, + feedback?: () => void, + ): void; + + export function uninstallSubscription( + url: string, + feedback?: () => void, + ): void; + } + } +} diff --git a/tools/typescript/definitions/eyeo_filtering_private.d.ts b/tools/typescript/definitions/eyeo_filtering_private.d.ts new file mode 100644 --- /dev/null +++ b/tools/typescript/definitions/eyeo_filtering_private.d.ts @@ -0,0 +1,249 @@ +// 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 . + +/** @fileoverview Type definitions for chrome.eyeoFilteringPrivate API. */ + +import {ChromeEvent} from './chrome_event.js'; + +declare global { + export namespace chrome { + export namespace eyeoFilteringPrivate { + + export interface Subscription { + url: string; + installation_state: string; + title: string; + current_version: string; + last_installation_time: string; + } + + export interface SessionStatsEntry { + url: string; + count: number; + } + + export interface RequestInfo { + url: string; + parent_frame_urls: string[]; + subscription: string; + configuration_name: string; + content_type: string; + tab_id: number; + window_id: number; + } + + export const onRequestAllowed: ChromeEvent<( + info: RequestInfo, + ) => void>; + + export const onRequestBlocked: ChromeEvent<( + info: RequestInfo, + ) => void>; + + export const onPageAllowed: ChromeEvent<( + info: RequestInfo, + ) => void>; + + export const onPopupBlocked: ChromeEvent<( + info: RequestInfo, + ) => void>; + + export const onPopupAllowed: ChromeEvent<( + info: RequestInfo, + ) => void>; + + export const onSubscriptionUpdated: ChromeEvent<( + subscription_url: string, + ) => void>; + + export const onEnabledStateChanged: ChromeEvent<( + config_name: string, + ) => void>; + + export const onFilterListsChanged: ChromeEvent<( + config_name: string, + ) => void>; + + export const onAllowedDomainsChanged: ChromeEvent<( + config_name: string, + ) => void>; + + export const onCustomFiltersChanged: ChromeEvent<( + config_name: string, + ) => void>; + + export function createConfiguration( + config_name: string, + ): void; + + export function removeConfiguration( + config_name: string, + ): void; + + export function getConfigurations(): Promise; + + export function getConfigurations( + callback?: ( + result: string[], + ) => void, + ): void; + + export function setEnabled( + configuration: string, + enabled: boolean, + ): Promise; + + export function setEnabled( + configuration: string, + enabled: boolean, + status?: () => void, + ): void; + + export function isEnabled( + configuration: string, + ): Promise; + + export function isEnabled( + configuration: string, + callback?: ( + result: boolean, + ) => void, + ): void; + + export function addAllowedDomain( + configuration: string, + domain: string, + ): Promise; + + export function addAllowedDomain( + configuration: string, + domain: string, + status?: () => void, + ): void; + + export function removeAllowedDomain( + configuration: string, + domain: string, + ): Promise; + + export function removeAllowedDomain( + configuration: string, + domain: string, + status?: () => void, + ): void; + + export function getAllowedDomains( + configuration: string, + ): Promise; + + export function getAllowedDomains( + configuration: string, + callback?: ( + result: string[], + ) => void, + ): void; + + export function addCustomFilter( + configuration: string, + filter: string, + ): Promise; + + export function addCustomFilter( + configuration: string, + filter: string, + status?: () => void, + ): void; + + export function removeCustomFilter( + configuration: string, + filter: string, + ): Promise; + + export function removeCustomFilter( + configuration: string, + filter: string, + status?: () => void, + ): void; + + export function getCustomFilters( + configuration: string, + ): Promise; + + export function getCustomFilters( + configuration: string, + callback?: ( + result: string[], + ) => void, + ): void; + + export function getFilterLists( + configuration: string, + ): Promise; + + export function getFilterLists( + configuration: string, + callback?: ( + result: Subscription[], + ) => void, + ): void; + + export function subscribeToFilterList( + configuration: string, + url: string, + ): Promise; + + export function subscribeToFilterList( + configuration: string, + url: string, + status?: () => void, + ): void; + + export function unsubscribeFromFilterList( + configuration: string, + url: string, + ): Promise; + + export function unsubscribeFromFilterList( + configuration: string, + url: string, + status?: () => void, + ): void; + + export function getSessionAllowedRequestsCount(): Promise; + + export function getSessionAllowedRequestsCount( + callback?: ( + result: SessionStatsEntry[], + ) => void, + ): void; + + export function getSessionBlockedRequestsCount(): Promise; + + export function getSessionBlockedRequestsCount( + callback?: ( + result: SessionStatsEntry[], + ) => void, + ): void; + + export function getAcceptableAdsUrl(): Promise; + + export function getAcceptableAdsUrl( + callback?: ( + result: string, + ) => void, + ): void; + } + } +} -- 2.25.1