From: chromium-sdk Date: Thu, 12 Oct 2023 14:46:06 +0200 Subject: eyeo Browser Ad filtering Solution: Android API Module Based on Chromium 118.0.5993.48 Pre-requisites: eyeo Browser Ad filtering Solution: Base Module --- chrome/android/BUILD.gn | 10 + chrome/browser/BUILD.gn | 14 + .../android/adblock/adblock_jni_factory.cc | 72 +++ .../android/adblock/adblock_jni_factory.h | 50 ++ ...iltering_configuration_bindings_factory.cc | 70 +++ ...filtering_configuration_bindings_factory.h | 51 ++ .../adblock/java_bindings_getters_impl.cc | 88 +++ ...lassification_notifier_bindings_factory.cc | 71 +++ ...classification_notifier_bindings_factory.h | 52 ++ ...hrome_browser_main_extra_parts_profiles.cc | 10 + components/adblock/android/BUILD.gn | 99 +++ components/adblock/android/adblock_jni.cc | 176 ++++++ components/adblock/android/adblock_jni.h | 52 ++ .../filtering_configuration_bindings.cc | 295 +++++++++ .../filtering_configuration_bindings.h | 68 +++ .../adblock/AdblockContentType.java | 61 ++ .../components/adblock/AdblockController.java | 203 +++++++ .../components/adblock/AdblockCounters.java | 101 ++++ .../adblock/FilteringConfiguration.java | 348 +++++++++++ .../ResourceClassificationNotifier.java | 187 ++++++ .../adblock/android/java_bindings_getters.h | 41 ++ .../adblock/AdblockControllerTestBase.java | 63 ++ .../adblock/TestAdBlockedObserver.java | 176 ++++++ .../TestPagesCircumventionTestBase.java | 64 ++ .../adblock/TestPagesCspTestBase.java | 108 ++++ .../TestPagesElemhideEmuInvTestBase.java | 135 +++++ .../adblock/TestPagesElemhideEmuTestBase.java | 183 ++++++ .../adblock/TestPagesElemhideTestBase.java | 204 +++++++ .../adblock/TestPagesExceptionTestBase.java | 174 ++++++ .../adblock/TestPagesFilterTestBase.java | 254 ++++++++ .../TestPagesHeaderFilterTestBase.java | 125 ++++ .../adblock/TestPagesHelperBase.java | 180 ++++++ .../adblock/TestPagesRewriteTestBase.java | 153 +++++ .../adblock/TestPagesSiteKeyTestBase.java | 58 ++ .../adblock/TestPagesSnippetsTestBase.java | 562 ++++++++++++++++++ .../adblock/TestPagesWebsocketTestBase.java | 56 ++ .../adblock/TestVerificationUtils.java | 163 +++++ ...source_classification_notifier_bindings.cc | 164 +++++ ...esource_classification_notifier_bindings.h | 69 +++ 39 files changed, 5010 insertions(+) create mode 100644 chrome/browser/android/adblock/adblock_jni_factory.cc create mode 100644 chrome/browser/android/adblock/adblock_jni_factory.h create mode 100644 chrome/browser/android/adblock/filtering_configuration_bindings_factory.cc create mode 100644 chrome/browser/android/adblock/filtering_configuration_bindings_factory.h create mode 100644 chrome/browser/android/adblock/java_bindings_getters_impl.cc create mode 100644 chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.cc create mode 100644 chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.h create mode 100644 components/adblock/android/BUILD.gn create mode 100644 components/adblock/android/adblock_jni.cc create mode 100644 components/adblock/android/adblock_jni.h create mode 100644 components/adblock/android/filtering_configuration_bindings.cc create mode 100644 components/adblock/android/filtering_configuration_bindings.h create mode 100644 components/adblock/android/java/src/org/chromium/components/adblock/AdblockContentType.java create mode 100644 components/adblock/android/java/src/org/chromium/components/adblock/AdblockController.java create mode 100644 components/adblock/android/java/src/org/chromium/components/adblock/AdblockCounters.java create mode 100644 components/adblock/android/java/src/org/chromium/components/adblock/FilteringConfiguration.java create mode 100644 components/adblock/android/java/src/org/chromium/components/adblock/ResourceClassificationNotifier.java create mode 100644 components/adblock/android/java_bindings_getters.h create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/AdblockControllerTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestAdBlockedObserver.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesCircumventionTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesCspTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuInvTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesExceptionTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesFilterTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesHeaderFilterTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesHelperBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesRewriteTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesSiteKeyTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesSnippetsTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesWebsocketTestBase.java create mode 100644 components/adblock/android/javatests/src/org/chromium/components/adblock/TestVerificationUtils.java create mode 100644 components/adblock/android/resource_classification_notifier_bindings.cc create mode 100644 components/adblock/android/resource_classification_notifier_bindings.h diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -3438,6 +3438,17 @@ static_library("browser") { "webauthn/android/webauthn_request_delegate_android.h", ] + ### Android API module start + sources += [ + "android/adblock/adblock_jni_factory.cc", + "android/adblock/adblock_jni_factory.h", + "android/adblock/filtering_configuration_bindings_factory.cc", + "android/adblock/filtering_configuration_bindings_factory.h", + "android/adblock/java_bindings_getters_impl.cc", + "android/adblock/resource_classification_notifier_bindings_factory.cc", + "android/adblock/resource_classification_notifier_bindings_factory.h", + ] + ### Android API module end public_deps += [ "//chrome/android/features/dev_ui:buildflags", @@ -3627,6 +3638,9 @@ static_library("browser") { "//url:gurl_android", ] + ###Android API module start + deps += [ "//components/adblock/android:java_bindings" ] + ###Android API module end if (safe_browsing_mode == 2) { sources += [ diff --git a/chrome/browser/android/adblock/adblock_jni_factory.cc b/chrome/browser/android/adblock/adblock_jni_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/android/adblock/adblock_jni_factory.cc @@ -0,0 +1,72 @@ +/* + * 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/android/adblock/adblock_jni_factory.h" + +#include + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/android/adblock_jni.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +// static +AdblockJNI* AdblockJNIFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +AdblockJNIFactory* AdblockJNIFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +AdblockJNIFactory::AdblockJNIFactory() + : BrowserContextKeyedServiceFactory( + "AdblockJNI", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(SubscriptionServiceFactory::GetInstance()); +} + +AdblockJNIFactory::~AdblockJNIFactory() = default; + +KeyedService* AdblockJNIFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new AdblockJNI( + SubscriptionServiceFactory::GetForBrowserContext(context)); +} + +content::BrowserContext* AdblockJNIFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +bool AdblockJNIFactory::ServiceIsCreatedWithBrowserContext() const { + // This avoids manual instantiation in chrome_browser_main.cc + return true; +} + +bool AdblockJNIFactory::ServiceIsNULLWhileTesting() const { + return true; +} + +} // namespace adblock diff --git a/chrome/browser/android/adblock/adblock_jni_factory.h b/chrome/browser/android/adblock/adblock_jni_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/android/adblock/adblock_jni_factory.h @@ -0,0 +1,50 @@ +/* + * 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_ANDROID_ADBLOCK_ADBLOCK_JNI_FACTORY_H_ +#define CHROME_BROWSER_ANDROID_ADBLOCK_ADBLOCK_JNI_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class AdblockJNI; +class AdblockJNIFactory : public BrowserContextKeyedServiceFactory { + public: + public: + static AdblockJNI* GetForBrowserContext(content::BrowserContext* context); + static AdblockJNIFactory* GetInstance(); + + private: + friend class base::NoDestructor; + AdblockJNIFactory(); + ~AdblockJNIFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + bool ServiceIsNULLWhileTesting() const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ANDROID_ADBLOCK_ADBLOCK_JNI_FACTORY_H_ diff --git a/chrome/browser/android/adblock/filtering_configuration_bindings_factory.cc b/chrome/browser/android/adblock/filtering_configuration_bindings_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/android/adblock/filtering_configuration_bindings_factory.cc @@ -0,0 +1,70 @@ +/* + * 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/android/adblock/filtering_configuration_bindings_factory.h" + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "chrome/browser/profiles/profile.h" +#include "components/adblock/android/filtering_configuration_bindings.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace adblock { + +// static +FilteringConfigurationBindings* +FilteringConfigurationBindingsFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +FilteringConfigurationBindingsFactory* +FilteringConfigurationBindingsFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +FilteringConfigurationBindingsFactory::FilteringConfigurationBindingsFactory() + : BrowserContextKeyedServiceFactory( + "FilteringConfigurationBindings", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(SubscriptionServiceFactory::GetInstance()); +} + +FilteringConfigurationBindingsFactory:: + ~FilteringConfigurationBindingsFactory() = default; + +KeyedService* FilteringConfigurationBindingsFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new FilteringConfigurationBindings( + SubscriptionServiceFactory::GetForBrowserContext(context), + Profile::FromBrowserContext(context)->GetOriginalProfile()->GetPrefs()); +} + +content::BrowserContext* +FilteringConfigurationBindingsFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +bool FilteringConfigurationBindingsFactory::ServiceIsCreatedWithBrowserContext() + const { + return true; +} + +} // namespace adblock diff --git a/chrome/browser/android/adblock/filtering_configuration_bindings_factory.h b/chrome/browser/android/adblock/filtering_configuration_bindings_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/android/adblock/filtering_configuration_bindings_factory.h @@ -0,0 +1,51 @@ +/* + * 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_ANDROID_ADBLOCK_FILTERING_CONFIGURATION_BINDINGS_FACTORY_H_ +#define CHROME_BROWSER_ANDROID_ADBLOCK_FILTERING_CONFIGURATION_BINDINGS_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class FilteringConfigurationBindings; +class FilteringConfigurationBindingsFactory + : public BrowserContextKeyedServiceFactory { + public: + public: + static FilteringConfigurationBindings* GetForBrowserContext( + content::BrowserContext* context); + static FilteringConfigurationBindingsFactory* GetInstance(); + + private: + friend class base::NoDestructor; + FilteringConfigurationBindingsFactory(); + ~FilteringConfigurationBindingsFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ANDROID_ADBLOCK_FILTERING_CONFIGURATION_BINDINGS_FACTORY_H_ diff --git a/chrome/browser/android/adblock/java_bindings_getters_impl.cc b/chrome/browser/android/adblock/java_bindings_getters_impl.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/android/adblock/java_bindings_getters_impl.cc @@ -0,0 +1,88 @@ +/* + * 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 "components/adblock/android/java_bindings_getters.h" + +#include "chrome/browser/adblock/subscription_service_factory.h" +#include "chrome/browser/android/adblock/adblock_jni_factory.h" +#include "chrome/browser/android/adblock/filtering_configuration_bindings_factory.h" +#include "chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.h" +#include "chrome/browser/android/tab_android.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "components/adblock/android/filtering_configuration_bindings.h" +#include "components/adblock/android/resource_classification_notifier_bindings.h" +#include "content/public/browser/web_contents.h" + +namespace adblock { +namespace { +constexpr int kNoTabId = -1; +} + +SubscriptionService* GetSubscriptionService() { + if (!g_browser_process || !g_browser_process->profile_manager()) { + return nullptr; + } + return SubscriptionServiceFactory::GetForBrowserContext( + g_browser_process->profile_manager()->GetLastUsedProfile()); +} + +AdblockJNI* GetJNI() { + if (!g_browser_process || !g_browser_process->profile_manager()) { + return nullptr; + } + return AdblockJNIFactory::GetForBrowserContext( + g_browser_process->profile_manager()->GetLastUsedProfile()); +} + +adblock::FilteringConfigurationBindings& GetFilteringConfigurationBindings() { + auto* bindings = + adblock::FilteringConfigurationBindingsFactory::GetForBrowserContext( + g_browser_process->profile_manager()->GetLastUsedProfile()); + DCHECK(bindings) << "FilteringConfigurationBindings should be non-null even " + "in tests, to keep the code simple"; + return *bindings; +} + +adblock::ResourceClassificationNotifierBindings& +GetResourceClassificationNotifierBindings() { + auto* bindings = adblock::ResourceClassificationNotifierBindingsFactory:: + GetForBrowserContext( + g_browser_process->profile_manager()->GetLastUsedProfile()); + DCHECK(bindings) + << "ResourceClassificationNotifierBindings should be non-null even " + "in tests, to keep the code simple"; + return *bindings; +} + +int GetTabId(content::RenderFrameHost* render_frame_host) { + auto* web_contents = + content::WebContents::FromRenderFrameHost(render_frame_host); + if (!web_contents) { + return kNoTabId; + } + + auto* tab = TabAndroid::FromWebContents(web_contents); + if (!tab) { + return kNoTabId; + } + + return tab->GetAndroidId(); +} + +} // namespace adblock diff --git a/chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.cc b/chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.cc @@ -0,0 +1,71 @@ +/* + * 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/android/adblock/resource_classification_notifier_bindings_factory.h" + +#include "chrome/browser/adblock/resource_classification_runner_factory.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "components/adblock/android/resource_classification_notifier_bindings.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace adblock { + +// static +ResourceClassificationNotifierBindings* +ResourceClassificationNotifierBindingsFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} +// static +ResourceClassificationNotifierBindingsFactory* +ResourceClassificationNotifierBindingsFactory::GetInstance() { + static base::NoDestructor + instance; + return instance.get(); +} + +ResourceClassificationNotifierBindingsFactory:: + ResourceClassificationNotifierBindingsFactory() + : BrowserContextKeyedServiceFactory( + "ResourceClassificationNotifierBindings", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ResourceClassificationRunnerFactory::GetInstance()); +} + +ResourceClassificationNotifierBindingsFactory:: + ~ResourceClassificationNotifierBindingsFactory() = default; + +KeyedService* +ResourceClassificationNotifierBindingsFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new ResourceClassificationNotifierBindings( + ResourceClassificationRunnerFactory::GetForBrowserContext(context)); +} + +content::BrowserContext* +ResourceClassificationNotifierBindingsFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +bool ResourceClassificationNotifierBindingsFactory:: + ServiceIsCreatedWithBrowserContext() const { + return true; +} + +} // namespace adblock diff --git a/chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.h b/chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.h new file mode 100644 --- /dev/null +++ b/chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.h @@ -0,0 +1,52 @@ +/* + * 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_ANDROID_ADBLOCK_RESOURCE_CLASSIFICATION_NOTIFIER_BINDINGS_FACTORY_H_ +#define CHROME_BROWSER_ANDROID_ADBLOCK_RESOURCE_CLASSIFICATION_NOTIFIER_BINDINGS_FACTORY_H_ + +#include "base/no_destructor.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "content/public/browser/browser_context.h" + +namespace adblock { + +class ResourceClassificationNotifierBindings; +class ResourceClassificationNotifierBindingsFactory + : public BrowserContextKeyedServiceFactory { + public: + public: + static ResourceClassificationNotifierBindings* GetForBrowserContext( + content::BrowserContext* context); + static ResourceClassificationNotifierBindingsFactory* GetInstance(); + + private: + friend class base::NoDestructor< + ResourceClassificationNotifierBindingsFactory>; + ResourceClassificationNotifierBindingsFactory(); + ~ResourceClassificationNotifierBindingsFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; +}; + +} // namespace adblock + +#endif // CHROME_BROWSER_ANDROID_ADBLOCK_RESOURCE_CLASSIFICATION_NOTIFIER_BINDINGS_FACTORY_H_ diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc --- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc +++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc @@ -263,6 +263,11 @@ #include "components/commerce/core/commerce_feature_list.h" #include "components/commerce/core/proto/merchant_signal_db_content.pb.h" +// Android API module start +#include "chrome/browser/android/adblock/adblock_jni_factory.h" +#include "chrome/browser/android/adblock/filtering_configuration_bindings_factory.h" +#include "chrome/browser/android/adblock/resource_classification_notifier_bindings_factory.h" +// Android API module end #else #include "chrome/browser/accessibility/live_caption/live_caption_controller_factory.h" @@ -762,6 +767,11 @@ void ChromeBrowserMainExtraPartsProfiles:: #if BUILDFLAG(IS_ANDROID) FastCheckoutCapabilitiesFetcherFactory::GetInstance(); + // Android API module start + adblock::AdblockJNIFactory::GetInstance(); + adblock::FilteringConfigurationBindingsFactory::GetInstance(); + adblock::ResourceClassificationNotifierBindingsFactory::GetInstance(); + // Android API module end #endif FaviconServiceFactory::GetInstance(); diff --git a/components/adblock/android/BUILD.gn b/components/adblock/android/BUILD.gn new file mode 100644 --- /dev/null +++ b/components/adblock/android/BUILD.gn @@ -0,0 +1,99 @@ +# 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 . + + +import("//build/config/android/rules.gni") +import("//build/config/locales.gni") +import("//tools/grit/grit_rule.gni") + +source_set("java_bindings") { + sources = [ + "adblock_jni.cc", + "adblock_jni.h", + "filtering_configuration_bindings.cc", + "filtering_configuration_bindings.h", + "java_bindings_getters.h", + "resource_classification_notifier_bindings.cc", + "resource_classification_notifier_bindings.h", + ] + deps = [ + ":jni_headers", + "//base", + "//components/keyed_service/content:content", + ] + + public_deps = [ + "//components/adblock/content:browser", + "//components/adblock/core/configuration", + "//components/adblock/core/subscription", + "//components/prefs", + ] +} + +generate_jni("jni_headers") { + sources = [ + "java/src/org/chromium/components/adblock/AdblockController.java", + "java/src/org/chromium/components/adblock/FilteringConfiguration.java", + "java/src/org/chromium/components/adblock/ResourceClassificationNotifier.java", + ] +} + +android_library("adblock_controller_java") { + sources = [ + "java/src/org/chromium/components/adblock/AdblockContentType.java", + "java/src/org/chromium/components/adblock/AdblockController.java", + "java/src/org/chromium/components/adblock/AdblockCounters.java", + "java/src/org/chromium/components/adblock/FilteringConfiguration.java", + "java/src/org/chromium/components/adblock/ResourceClassificationNotifier.java", + ] + + srcjar_deps = [ ":jni_headers" ] + deps = [ + "//base:base_java", + "//base:jni_java", + "//third_party/androidx:androidx_annotation_annotation_java", + ] + + resources_package = "org.chromium.components.adblock.controller" +} + + +android_library("adblock_java_tests_base") { + testonly = true + sources = [ + "javatests/src/org/chromium/components/adblock/AdblockControllerTestBase.java", + "javatests/src/org/chromium/components/adblock/TestAdBlockedObserver.java", + "javatests/src/org/chromium/components/adblock/TestPagesCircumventionTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesCspTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuInvTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesElemhideTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesExceptionTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesFilterTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesHeaderFilterTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesHelperBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesRewriteTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesSiteKeyTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesSnippetsTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesWebsocketTestBase.java", + "javatests/src/org/chromium/components/adblock/TestVerificationUtils.java", + ] + deps = [ + ":adblock_controller_java", + "//base:base_java", + "//base:base_java_test_support", + "//content/public/android:content_full_java", + "//content/public/test/android:content_java_test_support", + "//third_party/androidx:androidx_test_runner_java", + "//third_party/junit:junit", + ] +} diff --git a/components/adblock/android/adblock_jni.cc b/components/adblock/android/adblock_jni.cc new file mode 100644 --- /dev/null +++ b/components/adblock/android/adblock_jni.cc @@ -0,0 +1,176 @@ +/* + * 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 "components/adblock/android/adblock_jni.h" + +#include +#include +#include + +#include "base/android/jni_android.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "base/android/jni_weak_ref.h" +#include "base/logging.h" +#include "components/adblock/android/java_bindings_getters.h" +#include "components/adblock/android/jni_headers/AdblockController_jni.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "content/public/browser/browser_thread.h" + +using base::android::AttachCurrentThread; +using base::android::CheckException; +using base::android::ConvertJavaStringToUTF8; +using base::android::ConvertUTF8ToJavaString; +using base::android::GetClass; +using base::android::JavaParamRef; +using base::android::JavaRef; +using base::android::MethodID; +using base::android::ScopedJavaGlobalRef; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaArrayOfObjects; +using base::android::ToJavaArrayOfStrings; + +namespace adblock { + +namespace { + +ScopedJavaLocalRef ToJava(JNIEnv* env, + ScopedJavaLocalRef& url_class, + jmethodID& url_constructor, + const std::string& url, + const std::string& title, + const std::string& version, + const std::vector& languages) { + ScopedJavaLocalRef url_param( + env, env->NewObject(url_class.obj(), url_constructor, + ConvertUTF8ToJavaString(env, url).obj())); + CheckException(env); + return Java_Subscription_Constructor(env, url_param, + ConvertUTF8ToJavaString(env, title), + ConvertUTF8ToJavaString(env, version), + ToJavaArrayOfStrings(env, languages)); +} + +std::vector> CSubscriptionsToJObjects( + JNIEnv* env, + const std::vector>& subscriptions) { + ScopedJavaLocalRef url_class = GetClass(env, "java/net/URL"); + jmethodID url_constructor = MethodID::Get( + env, url_class.obj(), "", "(Ljava/lang/String;)V"); + std::vector> jobjects; + jobjects.reserve(subscriptions.size()); + for (auto& sub : subscriptions) { + jobjects.push_back(ToJava( + env, url_class, url_constructor, sub->GetSourceUrl().spec(), + sub->GetTitle(), sub->GetCurrentVersion(), std::vector{})); + } + return jobjects; +} + +std::vector> CSubscriptionsToJObjects( + JNIEnv* env, + std::vector& subscriptions) { + ScopedJavaLocalRef url_class = GetClass(env, "java/net/URL"); + jmethodID url_constructor = MethodID::Get( + env, url_class.obj(), "", "(Ljava/lang/String;)V"); + std::vector> jobjects; + jobjects.reserve(subscriptions.size()); + for (auto& sub : subscriptions) { + if (sub.ui_visibility == SubscriptionUiVisibility::Visible) { + // The checks here are when one makes f.e. adblock:custom visible + DCHECK(sub.url.is_valid()); + if (sub.url.is_valid()) { + jobjects.push_back(ToJava(env, url_class, url_constructor, + sub.url.spec(), sub.title, "", + sub.languages)); + } + } + } + return jobjects; +} + +} // namespace + +AdblockJNI::AdblockJNI(SubscriptionService* subscription_service) + : subscription_service_(subscription_service) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (subscription_service_) { + subscription_service_->AddObserver(this); + } +} + +AdblockJNI::~AdblockJNI() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (subscription_service_) { + subscription_service_->RemoveObserver(this); + } +} + +void AdblockJNI::Bind(JavaObjectWeakGlobalRef weak_java_controller) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + weak_java_controller_ = weak_java_controller; +} + +void AdblockJNI::OnSubscriptionInstalled(const GURL& url) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef obj = weak_java_controller_.get(env); + if (obj.is_null()) { + return; + } + + ScopedJavaLocalRef j_url = ConvertUTF8ToJavaString(env, url.spec()); + Java_AdblockController_subscriptionUpdatedCallback(env, obj, j_url); +} + +} // namespace adblock + +static void JNI_AdblockController_Bind( + JNIEnv* env, + const base::android::JavaParamRef& caller) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!adblock::GetSubscriptionService()) { + return; + } + JavaObjectWeakGlobalRef weak_controller_ref(env, caller); + adblock::GetJNI()->Bind(weak_controller_ref); +} + +static base::android::ScopedJavaLocalRef +JNI_AdblockController_GetInstalledSubscriptions(JNIEnv* env) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + auto* subscription_service = adblock::GetSubscriptionService(); + if (!subscription_service) { + return ToJavaArrayOfObjects(env, + std::vector>{}); + } + + return ToJavaArrayOfObjects( + env, + adblock::CSubscriptionsToJObjects( + env, subscription_service->GetCurrentSubscriptions( + subscription_service->GetAdblockFilteringConfiguration()))); +} + +static base::android::ScopedJavaLocalRef +JNI_AdblockController_GetRecommendedSubscriptions(JNIEnv* env) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + auto list = adblock::config::GetKnownSubscriptions(); + return ToJavaArrayOfObjects(env, + adblock::CSubscriptionsToJObjects(env, list)); +} diff --git a/components/adblock/android/adblock_jni.h b/components/adblock/android/adblock_jni.h new file mode 100644 --- /dev/null +++ b/components/adblock/android/adblock_jni.h @@ -0,0 +1,52 @@ +/* + * 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 COMPONENTS_ADBLOCK_ANDROID_ADBLOCK_JNI_H_ +#define COMPONENTS_ADBLOCK_ANDROID_ADBLOCK_JNI_H_ + +#include + +#include "base/android/jni_weak_ref.h" +#include "base/memory/raw_ptr.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/keyed_service/core/keyed_service.h" + +namespace content { +class BrowserContext; +} + +namespace adblock { + +class AdblockJNI : public SubscriptionService::SubscriptionObserver, + public KeyedService { + public: + explicit AdblockJNI(SubscriptionService* subscription_service); + ~AdblockJNI() override; + + void Bind(JavaObjectWeakGlobalRef weak_java_controller); + // SubscriptionService::SubscriptionObserver + void OnSubscriptionInstalled(const GURL& subscription_url) override; + + private: + SEQUENCE_CHECKER(sequence_checker_); + raw_ptr subscription_service_; + JavaObjectWeakGlobalRef weak_java_controller_; +}; + +} // namespace adblock + +#endif // COMPONENTS_ADBLOCK_ANDROID_ADBLOCK_JNI_H_ diff --git a/components/adblock/android/filtering_configuration_bindings.cc b/components/adblock/android/filtering_configuration_bindings.cc new file mode 100644 --- /dev/null +++ b/components/adblock/android/filtering_configuration_bindings.cc @@ -0,0 +1,295 @@ +/* + * 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 "components/adblock/android/filtering_configuration_bindings.h" + +#include +#include + +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "base/ranges/algorithm.h" +#include "components/adblock/android/java_bindings_getters.h" +#include "components/adblock/android/jni_headers/FilteringConfiguration_jni.h" +#include "components/adblock/core/configuration/filtering_configuration.h" +#include "components/adblock/core/configuration/persistent_filtering_configuration.h" +#include "components/adblock/core/subscription/subscription_service.h" + +namespace adblock { + +FilteringConfigurationBindings::FilteringConfigurationBindings( + SubscriptionService* subscription_service, + PrefService* pref_service) + : subscription_service_(subscription_service), + pref_service_(pref_service) {} + +FilteringConfigurationBindings::~FilteringConfigurationBindings() { + for (auto& pair : bound_counterparts_) { + pair.first->RemoveObserver(this); + } +} + +void FilteringConfigurationBindings::Bind( + const std::string& configuration_name, + JavaObjectWeakGlobalRef filtering_configuration_java) { + auto* existing_configuration = + GetInstalledConfigurationWithName(configuration_name); + if (existing_configuration) { + auto existing_binding = bound_counterparts_.find(existing_configuration); + if (existing_binding == bound_counterparts_.end()) { + // There is no Java-side counterpart bound to this + // FilteringConfiguration. + existing_configuration->AddObserver(this); + } + bound_counterparts_[existing_configuration] = + std::move(filtering_configuration_java); + } else { + // There is no already-installed FilteringConfiguration with this name. + // Create one and bind to it. + auto new_filtering_configuration = + std::make_unique(pref_service_, + configuration_name); + new_filtering_configuration->AddObserver(this); + bound_counterparts_[new_filtering_configuration.get()] = + std::move(filtering_configuration_java); + subscription_service_->InstallFilteringConfiguration( + std::move(new_filtering_configuration)); + } +} + +void FilteringConfigurationBindings::RemoveConfiguration( + const std::string& configuration_name) { + auto* existing_configuration = + GetInstalledConfigurationWithName(configuration_name); + if (existing_configuration) { + existing_configuration->RemoveObserver(this); + bound_counterparts_.erase(existing_configuration); + subscription_service_->UninstallFilteringConfiguration(configuration_name); + } +} + +std::vector +FilteringConfigurationBindings::GetConfigurations() { + return subscription_service_->GetInstalledFilteringConfigurations(); +} + +FilteringConfiguration* +FilteringConfigurationBindings::GetInstalledConfigurationWithName( + const std::string& configuration_name) { + const auto installed_configurations = GetConfigurations(); + auto existing_configuration_it = + base::ranges::find(installed_configurations, configuration_name, + &FilteringConfiguration::GetName); + return existing_configuration_it != installed_configurations.end() + ? *existing_configuration_it + : nullptr; +} + +void FilteringConfigurationBindings::OnEnabledStateChanged( + FilteringConfiguration* config) { + Notify(config, Java_FilteringConfiguration_enabledStateChanged); +} + +void FilteringConfigurationBindings::OnFilterListsChanged( + FilteringConfiguration* config) { + Notify(config, Java_FilteringConfiguration_filterListsChanged); +} + +void FilteringConfigurationBindings::OnAllowedDomainsChanged( + FilteringConfiguration* config) { + Notify(config, Java_FilteringConfiguration_allowedDomainsChanged); +} + +void FilteringConfigurationBindings::OnCustomFiltersChanged( + FilteringConfiguration* config) { + Notify(config, Java_FilteringConfiguration_customFiltersChanged); +} + +void FilteringConfigurationBindings::Notify( + FilteringConfiguration* config, + FilteringConfigurationBindings::JavaEventListener event_listener_function) { + auto bound_weak_ref = bound_counterparts_.find(config); + DCHECK(bound_weak_ref != bound_counterparts_.end()) + << "This should never receive notifications from unobserved " + "FilteringConfigurations"; + JNIEnv* env = base::android::AttachCurrentThread(); + auto java_counterpart = bound_weak_ref->second.get(env); + if (!java_counterpart.is_null()) { + event_listener_function(env, java_counterpart); + } +} + +} // namespace adblock + +// Throws when called with a name of a non existing configuration. +adblock::FilteringConfiguration* GetConfigurationWithName( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name) { + auto& bindings = adblock::GetFilteringConfigurationBindings(); + auto* configuration = bindings.GetInstalledConfigurationWithName( + base::android::ConvertJavaStringToUTF8(configuration_name)); + if (!configuration) { + env->ThrowNew(env->FindClass("java/lang/IllegalStateException"), + "Configuration does not exist!"); + return nullptr; + } + return configuration; +} + +static void JNI_FilteringConfiguration_Bind( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name, + const base::android::JavaParamRef& caller) { + auto& bindings = adblock::GetFilteringConfigurationBindings(); + JavaObjectWeakGlobalRef weak_configuration_ref(env, caller); + auto cpp_name = base::android::ConvertJavaStringToUTF8(configuration_name); + bindings.Bind(cpp_name, weak_configuration_ref); +} + +static void JNI_FilteringConfiguration_RemoveConfiguration( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name) { + auto& bindings = adblock::GetFilteringConfigurationBindings(); + bindings.RemoveConfiguration( + base::android::ConvertJavaStringToUTF8(configuration_name)); +} + +static base::android::ScopedJavaLocalRef +JNI_FilteringConfiguration_GetConfigurations(JNIEnv* env) { + auto& bindings = adblock::GetFilteringConfigurationBindings(); + std::vector configurations; + base::ranges::transform( + bindings.GetConfigurations(), std::back_inserter(configurations), + [](adblock::FilteringConfiguration* fc) { return fc->GetName(); }); + return base::android::ToJavaArrayOfStrings(env, configurations); +} + +static jboolean JNI_FilteringConfiguration_IsEnabled( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + return (configuration && configuration->IsEnabled()) ? JNI_TRUE : JNI_FALSE; +} + +static void JNI_FilteringConfiguration_SetEnabled( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name, + jboolean j_enabled) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + if (configuration) { + configuration->SetEnabled(j_enabled == JNI_TRUE); + } +} + +static void JNI_FilteringConfiguration_AddFilterList( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name, + const base::android::JavaParamRef& url) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + if (configuration) { + configuration->AddFilterList( + GURL{base::android::ConvertJavaStringToUTF8(url)}); + } +} + +static void JNI_FilteringConfiguration_RemoveFilterList( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name, + const base::android::JavaParamRef& url) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + if (configuration) { + configuration->RemoveFilterList( + GURL{base::android::ConvertJavaStringToUTF8(url)}); + } +} + +static base::android::ScopedJavaLocalRef +JNI_FilteringConfiguration_GetFilterLists( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name) { + // For simplicity, convert GURL to std::string, pass to Java, and convert from + // String to URL. Strings are easier to pass through JNI. + std::vector urls; + auto* configuration = GetConfigurationWithName(env, configuration_name); + if (configuration) { + base::ranges::transform(configuration->GetFilterLists(), + std::back_inserter(urls), &GURL::spec); + } + return base::android::ToJavaArrayOfStrings(env, urls); +} + +static void JNI_FilteringConfiguration_AddAllowedDomain( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name, + const base::android::JavaParamRef& allowed_domain) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + if (configuration) { + configuration->AddAllowedDomain( + base::android::ConvertJavaStringToUTF8(allowed_domain)); + } +} + +static void JNI_FilteringConfiguration_RemoveAllowedDomain( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name, + const base::android::JavaParamRef& allowed_domain) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + if (configuration) { + configuration->RemoveAllowedDomain(ConvertJavaStringToUTF8(allowed_domain)); + } +} + +static base::android::ScopedJavaLocalRef +JNI_FilteringConfiguration_GetAllowedDomains( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + return base::android::ToJavaArrayOfStrings( + env, configuration ? configuration->GetAllowedDomains() + : std::vector{}); +} + +static void JNI_FilteringConfiguration_AddCustomFilter( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name, + const base::android::JavaParamRef& custom_filter) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + if (configuration) { + configuration->AddCustomFilter(ConvertJavaStringToUTF8(custom_filter)); + } +} + +static void JNI_FilteringConfiguration_RemoveCustomFilter( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name, + const base::android::JavaParamRef& custom_filter) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + if (configuration) { + configuration->RemoveCustomFilter( + base::android::ConvertJavaStringToUTF8(custom_filter)); + } +} + +static base::android::ScopedJavaLocalRef +JNI_FilteringConfiguration_GetCustomFilters( + JNIEnv* env, + const base::android::JavaParamRef& configuration_name) { + auto* configuration = GetConfigurationWithName(env, configuration_name); + return base::android::ToJavaArrayOfStrings( + env, configuration ? configuration->GetCustomFilters() + : std::vector{}); +} diff --git a/components/adblock/android/filtering_configuration_bindings.h b/components/adblock/android/filtering_configuration_bindings.h new file mode 100644 --- /dev/null +++ b/components/adblock/android/filtering_configuration_bindings.h @@ -0,0 +1,68 @@ +/* + * 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 COMPONENTS_ADBLOCK_ANDROID_FILTERING_CONFIGURATION_BINDINGS_H_ +#define COMPONENTS_ADBLOCK_ANDROID_FILTERING_CONFIGURATION_BINDINGS_H_ + +#include +#include +#include +#include "base/android/jni_weak_ref.h" +#include "base/memory/raw_ptr.h" +#include "base/sequence_checker.h" +#include "components/adblock/core/configuration/filtering_configuration.h" +#include "components/adblock/core/subscription/subscription_service.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/prefs/pref_service.h" + +namespace adblock { + +class FilteringConfigurationBindings : public KeyedService, + public FilteringConfiguration::Observer { + public: + explicit FilteringConfigurationBindings( + SubscriptionService* subscription_service, + PrefService* pref_service); + ~FilteringConfigurationBindings() override; + void Bind(const std::string& configuration_name, + JavaObjectWeakGlobalRef filtering_configuration_java); + void RemoveConfiguration(const std::string& configuration_name); + std::vector GetConfigurations(); + FilteringConfiguration* GetInstalledConfigurationWithName( + const std::string& name); + + // FilteringConfiguration::Observer: + void OnEnabledStateChanged(FilteringConfiguration* config) override; + void OnFilterListsChanged(FilteringConfiguration* config) override; + void OnAllowedDomainsChanged(FilteringConfiguration* config) override; + void OnCustomFiltersChanged(FilteringConfiguration* config) override; + + private: + using JavaEventListener = void(JNIEnv* env, + const base::android::JavaRef& obj); + void Notify(FilteringConfiguration* config, + JavaEventListener event_listener_function); + SEQUENCE_CHECKER(sequence_checker_); + raw_ptr subscription_service_; + raw_ptr pref_service_; + std::map + bound_counterparts_; +}; + +} // namespace adblock + +#endif // COMPONENTS_ADBLOCK_ANDROID_FILTERING_CONFIGURATION_BINDINGS_H_ diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/AdblockContentType.java b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockContentType.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockContentType.java @@ -0,0 +1,61 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import java.util.HashMap; +import java.util.Map; + +public enum AdblockContentType { + // Note. This has to be kept in sync with c++ enum so some values + // are skipped + CONTENT_TYPE_OTHER(1 << 0), + CONTENT_TYPE_SCRIPT(1 << 1), + CONTENT_TYPE_IMAGE(1 << 2), + CONTENT_TYPE_STYLESHEET(1 << 3), + CONTENT_TYPE_OBJECT(1 << 4), + CONTENT_TYPE_SUBDOCUMENT(1 << 5), + CONTENT_TYPE_WEBSOCKET(1 << 7), + CONTENT_TYPE_WEBRTC(1 << 8), + CONTENT_TYPE_PING(1 << 10), + CONTENT_TYPE_XMLHTTPREQUEST(1 << 11), + CONTENT_TYPE_MEDIA(1 << 14), + CONTENT_TYPE_FONT(1 << 15); + + private final int contentType; + + private AdblockContentType(int contentType) { + this.contentType = contentType; + } + + private static final Map intToContentTypeMap = + new HashMap(); + + static { + for (AdblockContentType type : AdblockContentType.values()) { + intToContentTypeMap.put(type.contentType, type); + } + } + + public static AdblockContentType fromInt(int i) { + return intToContentTypeMap.get(i); + } + + public int getValue() { + return contentType; + } +} diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/AdblockController.java b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockController.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockController.java @@ -0,0 +1,203 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import android.content.Context; +import android.util.Log; +import android.webkit.URLUtil; + +import androidx.annotation.UiThread; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.NativeMethods; +import org.chromium.components.adblock.controller.R; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @brief Main access point for java UI code to control ad filtering. + * It calls its native counter part also AdblockController. + * It lives in UI thread on the browser process. + */ +public final class AdblockController extends FilteringConfiguration { + private static final String TAG = AdblockController.class.getSimpleName(); + + private static AdblockController sInstance; + + private URL mAcceptableAds; + + private AdblockController() { + super("adblock"); + try { + mAcceptableAds = + new URL("https://easylist-downloads.adblockplus.org/exceptionrules.txt"); + } catch (java.net.MalformedURLException e) { + mAcceptableAds = null; + } + } + + /** + * @return The singleton object. + */ + public static AdblockController getInstance() { + ThreadUtils.assertOnUiThread(); + if (sInstance == null) { + sInstance = new AdblockController(); + AdblockControllerJni.get().bind(sInstance); + } + return sInstance; + } + + public static class Subscription { + private URL mUrl; + private String mTitle; + private String mVersion = ""; + private String[] mLanguages = {}; + + public Subscription(final URL url, final String title, final String version) { + this.mUrl = url; + this.mTitle = title; + this.mVersion = version; + } + + @CalledByNative("Subscription") + public Subscription( + final URL url, final String title, final String version, final String[] languages) { + this.mUrl = url; + this.mTitle = title; + this.mVersion = version; + this.mLanguages = languages; + } + + public String title() { + return mTitle; + } + + public URL url() { + return mUrl; + } + + public String version() { + return mVersion; + } + + public String[] languages() { + return mLanguages; + } + + @Override + public boolean equals(final Object object) { + if (object == null) return false; + if (getClass() != object.getClass()) return false; + + Subscription other = (Subscription) object; + return url().equals(other.url()); + } + } + + @UiThread + public void setAcceptableAdsEnabled(boolean enabled) { + if (enabled) + addFilterList(mAcceptableAds); + else + removeFilterList(mAcceptableAds); + } + + @UiThread + public boolean isAcceptableAdsEnabled() { + return getFilterLists().contains(mAcceptableAds); + } + + @UiThread + public List getRecommendedSubscriptions() { + return (List) (List) Arrays.asList( + AdblockControllerJni.get().getRecommendedSubscriptions()); + } + + @UiThread + public void installSubscription(final URL url) { + addFilterList(url); + } + + @UiThread + public void uninstallSubscription(final URL url) { + removeFilterList(url); + } + + @UiThread + public List getInstalledSubscriptions() { + return (List) (List) Arrays.asList( + AdblockControllerJni.get().getInstalledSubscriptions()); + } + + // TODO(mpawlowski) temporary pass-through, to enable gradual deprecation. + public interface AdBlockedObserver extends ResourceClassificationNotifier.AdBlockedObserver {} + // TODO(mpawlowski) deprecate and remove, use ResourceClassificationNotifier directly. + @UiThread + public void addOnAdBlockedObserver( + final ResourceClassificationNotifier.AdBlockedObserver observer) { + ResourceClassificationNotifier.getInstance().addOnAdBlockedObserver(observer); + } + + // TODO(mpawlowski) deprecate and remove, use ResourceClassifier directly. + @UiThread + public void removeOnAdBlockedObserver( + final ResourceClassificationNotifier.AdBlockedObserver observer) { + ResourceClassificationNotifier.getInstance().removeOnAdBlockedObserver(observer); + } + + private List transform(String[] urls) { + if (urls == null) return null; + + List result = new ArrayList(); + for (String url : urls) { + try { + result.add(new URL(URLUtil.guessUrl(url))); + } catch (MalformedURLException e) { + Log.e(TAG, "Error parsing url: " + url); + } + } + + return result; + } + + @CalledByNative + private void subscriptionUpdatedCallback(final String url) { + ThreadUtils.assertOnUiThread(); + try { + URL subscriptionUrl = new URL(URLUtil.guessUrl(url)); + for (final SubscriptionUpdateObserver observer : mSubscriptionUpdateObservers) { + observer.onSubscriptionDownloaded(subscriptionUrl); + } + } catch (MalformedURLException e) { + Log.e(TAG, "Error parsing subscription url: " + url); + } + } + + @NativeMethods + interface Natives { + void bind(AdblockController caller); + Object[] getInstalledSubscriptions(); + Object[] getRecommendedSubscriptions(); + } +} diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/AdblockCounters.java b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockCounters.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockCounters.java @@ -0,0 +1,101 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AdblockCounters { + /** + * Immutable data-class containing an auxiliary information about resource event. + */ + public static class ResourceInfo { + private final String mRequestUrl; + private final List mParentFrameUrls; + private final String mSubscriptionUrl; + private final String mConfigurationName; + private final AdblockContentType mAdblockContentType; + private final int mTabId; + + ResourceInfo(final String requestUrl, final List parentFrameUrls, + final String subscriptionUrl, final String configurationName, + final int adblockContentType, final int tabId) { + this.mRequestUrl = requestUrl; + this.mParentFrameUrls = parentFrameUrls; + this.mSubscriptionUrl = subscriptionUrl; + this.mConfigurationName = configurationName; + this.mAdblockContentType = AdblockContentType.fromInt(adblockContentType); + this.mTabId = tabId; + } + + /** + * @return The request which was blocked or allowed. + */ + public String getRequestUrl() { + return mRequestUrl; + } + + /** + * @return The parent frames of the mRequestUrl. + */ + public List getParentFrameUrls() { + return mParentFrameUrls; + } + + /** + * @return Subscription url for filter done blocking or allowing decision, + * empty string otherwise. + */ + public String getSubscription() { + return mSubscriptionUrl; + } + + /** + * @return Configuration name containing subscription with matched filer for + * blocking or allowing decision, empty string otherwise. + */ + public String getConfigurationName() { + return mConfigurationName; + } + + /** + * @return The Adblock content type. See enum ContentType in + * components/adblock/types.h + */ + public AdblockContentType getAdblockContentType() { + return mAdblockContentType; + } + + /** + * @return The current tab id for the mRequestUrl. -1 means no tab id, likely tab closed + * before event arrived. Numbers start from 0. + */ + public int getTabId() { + return mTabId; + } + + @Override + public String toString() { + return "mRequestUrl: " + mRequestUrl + ", mParentFrameUrls: " + + mParentFrameUrls.toString() + ", mSubscriptionUrl:" + mSubscriptionUrl + + ", mConfigurationName:" + mConfigurationName + ", mAdblockContentType: " + + mAdblockContentType.getValue() + ", mTabId: " + mTabId; + } + } +} diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/FilteringConfiguration.java b/components/adblock/android/java/src/org/chromium/components/adblock/FilteringConfiguration.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/java/src/org/chromium/components/adblock/FilteringConfiguration.java @@ -0,0 +1,348 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import android.content.Context; +import android.content.res.Resources; +import android.util.Log; +import android.webkit.URLUtil; + +import androidx.annotation.UiThread; + +import org.chromium.base.ContextUtils; +import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.NativeMethods; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @brief Represents an independent configuration of filters, filter lists, + * allowed domains and other settings that influence resource filtering and + * content blocking. + * Multiple Filtering Configurations can co-exist and be controlled separately. + * A network resource is blocked if any enabled Filtering Configuration + * determines it should be, through its filters. + * Elements on websites are hidden according to a superset of element-hiding + * selectors from all enabled Filtering Configurations. + * Lives on UI thread, not thread-safe. + */ +public class FilteringConfiguration { + private static final String TAG = FilteringConfiguration.class.getSimpleName(); + + private final Set mConfigurationChangeObservers = new HashSet<>(); + protected final Set mSubscriptionUpdateObservers = new HashSet<>(); + private final String mName; + private final static Set mConfigurations = new HashSet<>(); + + public interface ConfigurationChangeObserver { + /** + * Triggered when the FilteringConfiguration becomes disabled or enabled. + */ + @UiThread + void onEnabledStateChanged(); + + /** + * Triggered when the collection of installed filter lists changes. + */ + @UiThread + void onFilterListsChanged(); + + /** + * Triggered when the set of allowed domain changes. + */ + @UiThread + void onAllowedDomainsChanged(); + + /** + * Triggered when the set of custom filters changes. + */ + @UiThread + void onCustomFiltersChanged(); + } + + public interface SubscriptionUpdateObserver { + @UiThread + void onSubscriptionDownloaded(final URL url); + } + + @UiThread + public void addObserver(final ConfigurationChangeObserver observer) { + mConfigurationChangeObservers.add(observer); + } + + @UiThread + public void removeObserver(final ConfigurationChangeObserver observer) { + mConfigurationChangeObservers.remove(observer); + } + + @UiThread + public void addSubscriptionUpdateObserver(final SubscriptionUpdateObserver observer) { + mSubscriptionUpdateObservers.add(observer); + } + + @UiThread + public void removeSubscriptionUpdateObserver(final SubscriptionUpdateObserver observer) { + mSubscriptionUpdateObservers.remove(observer); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void setEnabled(boolean enabled) throws IllegalStateException { + FilteringConfigurationJni.get().setEnabled(mName, enabled); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public boolean isEnabled() throws IllegalStateException { + return FilteringConfigurationJni.get().isEnabled(mName); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void addFilterList(final URL url) throws IllegalStateException { + FilteringConfigurationJni.get().addFilterList(mName, url.toString()); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void removeFilterList(final URL url) throws IllegalStateException { + FilteringConfigurationJni.get().removeFilterList(mName, url.toString()); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getFilterLists() throws IllegalStateException { + List filterListsStr = + Arrays.asList(FilteringConfigurationJni.get().getFilterLists(mName)); + List filterLists = new ArrayList(); + for (String url : filterListsStr) { + try { + filterLists.add(new URL(url)); + } catch (MalformedURLException e) { + Log.e(TAG, "Received invalid subscription URL from C++: " + url); + } + } + return filterLists; + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void addAllowedDomain(final String domain) throws IllegalStateException { + String sanitizedDomain = sanitizeSite(domain); + if (sanitizedDomain == null) return; + FilteringConfigurationJni.get().addAllowedDomain(mName, sanitizedDomain); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + + @UiThread + public void removeAllowedDomain(final String domain) throws IllegalStateException { + String sanitizedDomain = sanitizeSite(domain); + if (sanitizedDomain == null) return; + FilteringConfigurationJni.get().removeAllowedDomain(mName, sanitizedDomain); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getAllowedDomains() throws IllegalStateException { + List allowedDomains = + Arrays.asList(FilteringConfigurationJni.get().getAllowedDomains(mName)); + Collections.sort(allowedDomains); + return allowedDomains; + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void addCustomFilter(final String filter) throws IllegalStateException { + FilteringConfigurationJni.get().addCustomFilter(mName, filter); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void removeCustomFilter(final String filter) throws IllegalStateException { + FilteringConfigurationJni.get().removeCustomFilter(mName, filter); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getCustomFilters() throws IllegalStateException { + return Arrays.asList(FilteringConfigurationJni.get().getCustomFilters(mName)); + } + + @UiThread + public static FilteringConfiguration createConfiguration(final String configuration_name) { + FilteringConfiguration configuration = findConfigurationByName(configuration_name); + if (configuration == null) { + configuration = new FilteringConfiguration(configuration_name); + mConfigurations.add(configuration); + } + return configuration; + } + + @UiThread + public static void removeConfiguration(final String configuration_name) { + final FilteringConfiguration configuration = findConfigurationByName(configuration_name); + if (configuration != null) { + mConfigurations.remove(configuration); + } + // Even if on Java side there is no such a FilteringConfiguration object, it can exist + // on native side. + FilteringConfigurationJni.get().removeConfiguration(configuration_name); + } + + @UiThread + public static List getConfigurations() { + // Get all existing (on C++ side) configurations, if there is no matching Java + // instance present in mConfigurations set then create one and add. + final String[] existing_configurations_names = + FilteringConfigurationJni.get().getConfigurations(); + final List configurations_to_return = + new ArrayList(); + // If mConfigurations contains configurations which are not present on the list returned + // from FilteringConfigurationJni.get().getConfigurations() then we need to remove them + // as it means that a configuration has been removed on native side (not via Java API). + mConfigurations.removeIf(filteringConfiguration -> { + return !Arrays.asList(existing_configurations_names) + .contains(filteringConfiguration.mName); + }); + for (final String configuration_name : existing_configurations_names) { + FilteringConfiguration configuration = findConfigurationByName(configuration_name); + if (configuration == null) { + configuration = new FilteringConfiguration(configuration_name); + mConfigurations.add(configuration); + } + configurations_to_return.add(configuration); + } + Collections.sort(configurations_to_return, new Comparator() { + @Override + public int compare( + final FilteringConfiguration object1, final FilteringConfiguration object2) { + return object1.mName.compareTo(object2.mName); + } + }); + return configurations_to_return; + } + + // TODO(kzlomek): Make it private once we remove AdblockController (DPD-2120). + protected FilteringConfiguration(final String configuration_name) { + mName = configuration_name; + FilteringConfigurationJni.get().bind(mName, this); + } + + @UiThread + private static FilteringConfiguration findConfigurationByName(final String configuration_name) { + for (final FilteringConfiguration configuration : mConfigurations) { + if (configuration.mName.equals(configuration_name)) { + return configuration; + } + } + return null; + } + + private String sanitizeSite(String site) { + // |site| is raw user input. We expect it to be either a domain or a URL. + try { + URL candidate = new URL(URLUtil.guessUrl(site)); + return candidate.getHost(); + } catch (java.net.MalformedURLException e) { + } + // Could not parse |site| as URL or domain. + return null; + } + + @CalledByNative + private void enabledStateChanged() { + ThreadUtils.assertOnUiThread(); + for (final ConfigurationChangeObserver observer : mConfigurationChangeObservers) { + observer.onEnabledStateChanged(); + } + } + + @CalledByNative + private void filterListsChanged() { + ThreadUtils.assertOnUiThread(); + for (final ConfigurationChangeObserver observer : mConfigurationChangeObservers) { + observer.onFilterListsChanged(); + } + } + + @CalledByNative + private void allowedDomainsChanged() { + ThreadUtils.assertOnUiThread(); + for (final ConfigurationChangeObserver observer : mConfigurationChangeObservers) { + observer.onAllowedDomainsChanged(); + } + } + + @CalledByNative + private void customFiltersChanged() { + ThreadUtils.assertOnUiThread(); + for (final ConfigurationChangeObserver observer : mConfigurationChangeObservers) { + observer.onCustomFiltersChanged(); + } + } + + @NativeMethods + interface Natives { + void bind(String configuration_name, FilteringConfiguration caller); + void removeConfiguration(String configuration_name); + String[] getConfigurations(); + boolean isEnabled(String configuration_name); + void setEnabled(String configuration_name, boolean enabled); + void addFilterList(String configuration_name, String url); + void removeFilterList(String configuration_name, String url); + String[] getFilterLists(String configuration_name); + void addAllowedDomain(String configuration_name, String domain); + void removeAllowedDomain(String configuration_name, String domain); + String[] getAllowedDomains(String configuration_name); + void addCustomFilter(String configuration_name, String filter); + void removeCustomFilter(String configuration_name, String filter); + String[] getCustomFilters(String configuration_name); + } +} diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/ResourceClassificationNotifier.java b/components/adblock/android/java/src/org/chromium/components/adblock/ResourceClassificationNotifier.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/java/src/org/chromium/components/adblock/ResourceClassificationNotifier.java @@ -0,0 +1,187 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import android.content.Context; +import android.content.res.Resources; +import android.util.Log; +import android.webkit.URLUtil; + +import androidx.annotation.UiThread; + +import org.chromium.base.ContextUtils; +import org.chromium.base.ThreadUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.NativeMethods; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @brief Allows observing notifications from ongoing resource classification. + * Classification combines filters from all FilteringConfigurations. + * Lives on UI thread, not thread-safe. + */ +public final class ResourceClassificationNotifier { + private static final String TAG = ResourceClassificationNotifier.class.getSimpleName(); + + private static ResourceClassificationNotifier sInstance; + private final Set mOnAdBlockedObservers = new HashSet<>(); + // TODO(mpawlowski) in the future, we can consider adding filter hit + // notifications here as well (DPD-1233) + + public interface AdBlockedObserver { + /** + * "Ad allowed" event for a request which would be blocked but there + * was an allowlisting filter found. + * + * It should not block the UI thread for too long. + * @param info contains auxiliary information about the resource. + */ + @UiThread + void onAdAllowed(AdblockCounters.ResourceInfo info); + + /** + * "Ad blocked" event for a request which was blocked. + * + * It should not block the UI thread for too long. + * @param info contains auxiliary information about the resource. + */ + @UiThread + void onAdBlocked(AdblockCounters.ResourceInfo info); + + /** + * "Page allowed" event for an allowlisted domain (page). + * + * It should not block the UI thread for too long. + * @param info contains auxiliary information about the resource. + */ + @UiThread + void onPageAllowed(AdblockCounters.ResourceInfo info); + + /** + * "Popup allowed" event for a popup which would be blocked but there + * was an allowlisting filter found. + * + * It should not block the UI thread for too long. + * @param info contains auxiliary information about the resource. + */ + @UiThread + void onPopupAllowed(AdblockCounters.ResourceInfo info); + + /** + * "Popup blocked" event for a popup which was blocked. + * + * It should not block the UI thread for too long. + * @param info contains auxiliary information about the resource. + */ + @UiThread + void onPopupBlocked(AdblockCounters.ResourceInfo info); + } + + private ResourceClassificationNotifier() { + ResourceClassificationNotifierJni.get().bind(this); + } + + public static ResourceClassificationNotifier getInstance() { + ThreadUtils.assertOnUiThread(); + if (sInstance == null) { + sInstance = new ResourceClassificationNotifier(); + } + return sInstance; + } + + @UiThread + public void addOnAdBlockedObserver(final AdBlockedObserver observer) { + mOnAdBlockedObservers.add(observer); + } + + @UiThread + public void removeOnAdBlockedObserver(final AdBlockedObserver observer) { + mOnAdBlockedObservers.remove(observer); + } + + @CalledByNative + private void adMatchedCallback(final String requestUrl, boolean wasBlocked, + final String[] parentFrameUrls, final String subscriptionUrl, + final String configurationName, final int contentType, final int tabId) { + ThreadUtils.assertOnUiThread(); + final List parentsList = Arrays.asList(parentFrameUrls); + final AdblockCounters.ResourceInfo resourceInfo = new AdblockCounters.ResourceInfo( + requestUrl, parentsList, subscriptionUrl, configurationName, contentType, tabId); + Log.d(TAG, + "eyeo: adMatchedCallback() notifies " + mOnAdBlockedObservers.size() + + " listeners about " + resourceInfo.toString() + + (wasBlocked ? " getting blocked" : " being allowed")); + for (final AdBlockedObserver observer : mOnAdBlockedObservers) { + if (wasBlocked) { + observer.onAdBlocked(resourceInfo); + } else { + observer.onAdAllowed(resourceInfo); + } + } + } + + @CalledByNative + private void pageAllowedCallback(final String requestUrl, final String subscriptionUrl, + final String configurationName, final int tabId) { + ThreadUtils.assertOnUiThread(); + final AdblockCounters.ResourceInfo resourceInfo = + new AdblockCounters.ResourceInfo(requestUrl, new ArrayList(), subscriptionUrl, + configurationName, AdblockContentType.CONTENT_TYPE_OTHER.getValue(), tabId); + Log.d(TAG, + "eyeo: pageAllowedCallback() notifies " + mOnAdBlockedObservers.size() + + " listeners about " + resourceInfo.toString()); + for (final AdBlockedObserver observer : mOnAdBlockedObservers) { + observer.onPageAllowed(resourceInfo); + } + } + + @CalledByNative + private void popupMatchedCallback(final String requestUrl, boolean wasBlocked, + final String openerUrl, final String subscription, final String configurationName, + final int tabId) { + ThreadUtils.assertOnUiThread(); + final List parentsList = Arrays.asList(openerUrl); + final AdblockCounters.ResourceInfo resourceInfo = + new AdblockCounters.ResourceInfo(requestUrl, parentsList, subscription, + configurationName, AdblockContentType.CONTENT_TYPE_OTHER.getValue(), tabId); + Log.d(TAG, + "eyeo: popupMatchedCallback() notifies " + mOnAdBlockedObservers.size() + + " listeners about " + resourceInfo.toString() + + (wasBlocked ? " getting blocked" : " being allowed")); + for (final AdBlockedObserver observer : mOnAdBlockedObservers) { + if (wasBlocked) { + observer.onPopupBlocked(resourceInfo); + } else { + observer.onPopupAllowed(resourceInfo); + } + } + } + + @NativeMethods + interface Natives { + void bind(ResourceClassificationNotifier caller); + } +} diff --git a/components/adblock/android/java_bindings_getters.h b/components/adblock/android/java_bindings_getters.h new file mode 100644 --- /dev/null +++ b/components/adblock/android/java_bindings_getters.h @@ -0,0 +1,41 @@ +/* + * 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 COMPONENTS_ADBLOCK_ANDROID_JAVA_BINDINGS_GETTERS_H_ +#define COMPONENTS_ADBLOCK_ANDROID_JAVA_BINDINGS_GETTERS_H_ + +#include "components/adblock/android/adblock_jni.h" +#include "components/adblock/android/filtering_configuration_bindings.h" +#include "components/adblock/android/resource_classification_notifier_bindings.h" +#include "components/adblock/core/subscription/subscription_service.h" + +namespace adblock { + +SubscriptionService* GetSubscriptionService(); + +AdblockJNI* GetJNI(); + +FilteringConfigurationBindings& GetFilteringConfigurationBindings(); + +ResourceClassificationNotifierBindings& +GetResourceClassificationNotifierBindings(); + +int GetTabId(content::RenderFrameHost* render_frame_host); + +} // namespace adblock + +#endif // COMPONENTS_ADBLOCK_ANDROID_JAVA_BINDINGS_GETTERS_H_ diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/AdblockControllerTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/AdblockControllerTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/AdblockControllerTestBase.java @@ -0,0 +1,63 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Test; + +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.base.test.util.Feature; +import org.chromium.base.test.util.IntegrationTest; +import org.chromium.components.adblock.AdblockController; +import org.chromium.content_public.browser.test.util.TestThreadUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public abstract class AdblockControllerTestBase { + private final CallbackHelper mHelper = new CallbackHelper(); + + @Test + @IntegrationTest + @LargeTest + @Feature({"adblock"}) + public void addingAllowedDomains() throws TimeoutException { + final List allowedDomains = new ArrayList<>(); + TestThreadUtils.runOnUiThreadBlocking(() -> { + AdblockController.getInstance().addAllowedDomain("foobar.com"); + AdblockController.getInstance().addAllowedDomain("domain.com/path/to/page.html"); + AdblockController.getInstance().addAllowedDomain("domain.com/duplicate.html"); + AdblockController.getInstance().addAllowedDomain("https://scheme.com/path.html"); + AdblockController.getInstance().addAllowedDomain("gibberish"); + allowedDomains.addAll(AdblockController.getInstance().getAllowedDomains()); + mHelper.notifyCalled(); + }); + mHelper.waitForCallback(0, 1, 10, TimeUnit.SECONDS); + // We expect to see a sorted collection of domains (not URLs) without duplicates. + ArrayList expectedAllowedDomains = new ArrayList(); + expectedAllowedDomains.add("domain.com"); + expectedAllowedDomains.add("foobar.com"); + expectedAllowedDomains.add("scheme.com"); + expectedAllowedDomains.add("www.gibberish.com"); + Assert.assertEquals(expectedAllowedDomains, allowedDomains); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestAdBlockedObserver.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestAdBlockedObserver.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestAdBlockedObserver.java @@ -0,0 +1,176 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import org.junit.Assert; + +import org.chromium.components.adblock.AdblockContentType; +import org.chromium.components.adblock.AdblockCounters; +import org.chromium.components.adblock.ResourceClassificationNotifier; + +import java.net.URL; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; + +public class TestAdBlockedObserver implements ResourceClassificationNotifier.AdBlockedObserver { + @Override + public void onAdAllowed(AdblockCounters.ResourceInfo info) { + allowedInfos.add(info); + Assert.assertEquals(info.getSubscription(), getExpectedSubscriptionUrl()); + CheckAndCountDownLatch(Decision.ALLOWED, info.getRequestUrl().split("\\?")[0]); + } + + @Override + public void onAdBlocked(AdblockCounters.ResourceInfo info) { + blockedInfos.add(info); + Assert.assertEquals(info.getSubscription(), getExpectedSubscriptionUrl()); + CheckAndCountDownLatch(Decision.BLOCKED, info.getRequestUrl().split("\\?")[0]); + } + + @Override + public void onPageAllowed(AdblockCounters.ResourceInfo info) { + allowedPageInfos.add(info); + Assert.assertEquals(info.getSubscription(), getExpectedSubscriptionUrl()); + } + + @Override + public void onPopupAllowed(AdblockCounters.ResourceInfo info) { + allowedPopupsInfos.add(info); + Assert.assertEquals(info.getSubscription(), getExpectedSubscriptionUrl()); + } + + @Override + public void onPopupBlocked(AdblockCounters.ResourceInfo info) { + blockedPopupsInfos.add(info); + Assert.assertEquals(info.getSubscription(), getExpectedSubscriptionUrl()); + } + + public boolean isBlocked(String url) { + for (AdblockCounters.ResourceInfo info : blockedInfos) { + if (info.getRequestUrl().contains(url)) return true; + } + + return false; + } + + public boolean isPopupBlocked(String url) { + for (AdblockCounters.ResourceInfo info : blockedPopupsInfos) { + if (info.getRequestUrl().contains(url)) return true; + } + + return false; + } + + public int numBlockedByType(AdblockContentType type) { + int result = 0; + for (AdblockCounters.ResourceInfo info : blockedInfos) { + if (info.getAdblockContentType() == type) ++result; + } + return result; + } + + public int numBlockedPopups() { + return blockedPopupsInfos.size(); + } + + public boolean isAllowed(String url) { + for (AdblockCounters.ResourceInfo info : allowedInfos) { + if (info.getRequestUrl().contains(url)) return true; + } + + return false; + } + + public boolean isPageAllowed(String url) { + for (AdblockCounters.ResourceInfo info : allowedPageInfos) { + if (info.getRequestUrl().contains(url)) return true; + } + + return false; + } + + public boolean isPopupAllowed(String url) { + for (AdblockCounters.ResourceInfo info : allowedPopupsInfos) { + if (info.getRequestUrl().contains(url)) return true; + } + + return false; + } + + public int numAllowedByType(AdblockContentType type) { + int result = 0; + for (AdblockCounters.ResourceInfo info : allowedInfos) { + if (info.getAdblockContentType() == type) ++result; + } + return result; + } + + public int numAllowedPopups() { + return allowedPopupsInfos.size(); + } + + public void setExpectedSubscriptionUrl(URL url) { + mTestSubscriptionUrl = url; + } + + private String getExpectedSubscriptionUrl() { + if (mTestSubscriptionUrl != null) return mTestSubscriptionUrl.toString(); + return "adblock:custom"; + } + + private enum Decision { ALLOWED, BLOCKED } + + // We either countDown() our latch for every filtered resource if there are no + // specific expectations set (expectedAllowed == null && expectedBlocked == null), + // or we countDown() only when all expectations have been met so when: + // (expectedAllowed.isNullOrEmpty() && expectedBlocked.isNullOrEmpty()). + private void CheckAndCountDownLatch(final Decision decision, final String url) { + if (countDownLatch != null) { + if (expectedBlocked == null && expectedAllowed == null) { + countDownLatch.countDown(); + } else { + if (decision == Decision.BLOCKED) { + if (expectedBlocked != null) { + expectedBlocked.remove(url); + } + } else { + if (expectedAllowed != null) { + expectedAllowed.remove(url); + } + } + boolean expectationsMet = (expectedAllowed == null || expectedAllowed.isEmpty()) + && (expectedBlocked == null || expectedBlocked.isEmpty()); + if (expectationsMet) { + countDownLatch.countDown(); + } + } + } + } + + private URL mTestSubscriptionUrl; + public List blockedInfos = new CopyOnWriteArrayList<>(); + public List allowedInfos = new CopyOnWriteArrayList<>(); + public List allowedPageInfos = new CopyOnWriteArrayList<>(); + public List blockedPopupsInfos = new CopyOnWriteArrayList<>(); + public List allowedPopupsInfos = new CopyOnWriteArrayList<>(); + CountDownLatch countDownLatch; + Set expectedAllowed; + Set expectedBlocked; +}; diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesCircumventionTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesCircumventionTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesCircumventionTestBase.java @@ -0,0 +1,64 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Test; + +import org.chromium.base.test.util.Feature; +import org.chromium.content_public.browser.test.util.JavaScriptUtils; + +public abstract class TestPagesCircumventionTestBase { + private TestPagesHelperBase mHelper; + + public void setUp(TestPagesHelperBase helper) { + mHelper = helper; + mHelper.addFilterList(TestPagesHelperBase.TESTPAGES_SUBSCRIPTION); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyCircumventionInlineStyleNotImportant() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.CIRCUMVENTION_TESTPAGES_TESTCASES_ROOT + + "inline-style-important"); + TestVerificationUtils.verifyHiddenCount(mHelper, 2, "div"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyCircumventionAnonymousFrameDocumentWrite() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.CIRCUMVENTION_TESTPAGES_TESTCASES_ROOT + + "anoniframe-documentwrite"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "span[data-expectedresult='fail']"); + String numHidden = JavaScriptUtils.executeJavaScriptAndWaitForResult( + mHelper.getWebContents(), + "var hiddenCount = 0;" + + "var elements = document.getElementById(\"write\").contentWindow" + + ".document.getElementsByTagName(\"span\");" + + "for (let i = 0; i < elements.length; ++i) {" + + " if (window.getComputedStyle(elements[i]).display == \"none\") " + + "++hiddenCount;" + + "}" + + "hiddenCount;"); + Assert.assertEquals("1", numHidden); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesCspTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesCspTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesCspTestBase.java @@ -0,0 +1,108 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Test; + +import org.chromium.base.test.util.Feature; + +public abstract class TestPagesCspTestBase { + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testCspAllSites() throws Exception { + mHelper.addCustomFilter("*$csp=script-src 'none'"); + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "csp_all"); + TestVerificationUtils.verifyDisplayedCount(mHelper, 0, "img[id='all-sites-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testCspSpecificSite() throws Exception { + mHelper.addCustomFilter(String.format("||%s^$csp=script-src https://%s/lib/utils.js", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "csp_specific"); + TestVerificationUtils.verifyDisplayedCount(mHelper, 0, "img[id='specific-site-fail-1']", + TestVerificationUtils.IncludeSubframes.NO); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testCspSpecificSiteFrameSrc() throws Exception { + mHelper.addCustomFilter( + String.format("||%s^$csp=frame-src 'self'", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "csp_specific"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div[id='sub-frame-error']", TestVerificationUtils.IncludeSubframes.NO); + TestVerificationUtils.verifyDisplayedCount(mHelper, 0, "div[id='sub-frame-error-details']", + TestVerificationUtils.IncludeSubframes.NO); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testCspException() throws Exception { + // Blocking filter: + mHelper.addCustomFilter(String.format("||%s^$csp=script-src https://%s/lib/utils.js", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + // Resource loaded by JS was blocked + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "csp"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div[id='unblock-javascript'] > img"); + + // Allowing filter: + mHelper.addCustomFilter(String.format("@@||%s^$csp", TestPagesHelperBase.TESTPAGES_DOMAIN)); + // Resource loaded by JS was allowed + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "csp"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div[id='unblock-javascript'] > img"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testCspGenericBlockException() throws Exception { + // Blocking filter: + mHelper.addCustomFilter(String.format("||%s^$csp=script-src https://%s/lib/utils.js", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + // Resource loaded by JS was blocked + mHelper.loadUrl( + TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "csp_genericblock"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div[id='genericblock-javascript'] > img"); + + // Allowing filter: + mHelper.addCustomFilter( + String.format("@@||%s^$genericblock", TestPagesHelperBase.TESTPAGES_DOMAIN)); + // Resource loaded by JS was allowed + mHelper.loadUrl( + TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "csp_genericblock"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div[id='genericblock-javascript'] > img"); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuInvTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuInvTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuInvTestBase.java @@ -0,0 +1,135 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Test; + +import org.chromium.base.test.util.Feature; + +public abstract class TestPagesElemhideEmuInvTestBase { + public static final String ELEMENT_HIDING_EMULATION_TESTPAGES_URL = + TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + + "element-hiding-emulation-inversion"; + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuNotAbpProperties() throws Exception { + mHelper.addCustomFilter( + String.format("%s#?#.ehei-properties:not(:-abp-properties(width: 238px))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='basic-not-abp-properties-usage-fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuNotAbpHas() throws Exception { + mHelper.addCustomFilter( + String.format("%s#?#.ehei-has:not(:-abp-has(span.ehei-has-not-hide))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='basic-not-abp-has-usage-fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuNotAbpContains() throws Exception { + mHelper.addCustomFilter( + String.format("%s#?#.ehei-contains:not(span:-abp-contains(example-content))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "span[id='basic-not-abp-contains-usage-fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuNotChained() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#?#.ehei-chained-parent:not(:-abp-has(> div:-abp-properties(width: 198px)))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='chained-extended-selectors-with-not-selector-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuNotCaseIsensitive() throws Exception { + mHelper.addCustomFilter(String.format("%s#?#.ehei-case:not(:-abp-properties(WiDtH: 209px))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='case-insensitive-extended-selectors-with-not-selector-fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuNotWildcard() throws Exception { + mHelper.addCustomFilter(String.format("%s#?#.ehei-wildcard:not(:-abp-properties(cursor:*))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, + "div[id='wildcard-in-extended-selector-combined-with-not-selector-fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuNotRegexAbpProperties() throws Exception { + mHelper.addCustomFilter( + String.format("%s#?#.ehei-regex:not(:-abp-properties(/width: 11[1-5]px;/))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='regular-expression-in-not-abp-properties-fail-1']"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='regular-expression-in-not-abp-properties-fail-2']"); + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, ".ehei-regex3"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuNotRegexAbpContains() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#?#.ehei-contains-regex:not(span:-abp-contains(/example-contentregex\\d/))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "span[id='regular-expression-in-not-abp-contains-fail-1']"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "span[id='regular-expression-in-not-abp-contains-fail-2']"); + TestVerificationUtils.verifyDisplayedCount(mHelper, 2, ".ehei-contains-regex"); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideEmuTestBase.java @@ -0,0 +1,183 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Test; + +import org.chromium.base.test.util.Feature; + +public abstract class TestPagesElemhideEmuTestBase { + public static final String ELEMENT_HIDING_EMULATION_TESTPAGES_URL = + TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "element-hiding-emulation"; + public static final String ELEMENT_HIDING_EMULATION_EXCEPTIONS_TESTPAGES_URL = + TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "element-hiding"; + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersBasicAbpProperties() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#?#div:-abp-properties(width: 213px)", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='basic-abp-properties-usage-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersBasicAbpHas() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#?#div:-abp-has(>div>span.ehe-abp-has)", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[id='basic-abp-has-usage-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersBasicHas() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#?#div:has(>div>span.ehe-has)", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + // "Basic :has() usage" are duplicated on testpage. + TestVerificationUtils.verifyHiddenCount(mHelper, 2, "div[id='basic-has-usage-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersBasicAbpContains() throws Exception { + mHelper.addCustomFilter(String.format("%s#?#span:-abp-contains(ehe-contains-target)", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "span[id='basic-abp-contains-usage-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersBasicXpath() throws Exception { + mHelper.addCustomFilter( + String.format("%s#?#span:xpath(//*[@id=\"basic-xpath-usage-fail\"])", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "span[id='basic-xpath-usage-fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersBasicHasText() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#?#span:has-text(ehe-has-text)", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "span[id='basic-has-text-usage-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersChainedExtendedSelectors() throws Exception { + mHelper.addCustomFilter( + String.format("%s#?#div:-abp-has(> div:-abp-properties(width: 214px))", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='chained-extended-selectors-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersCaseInsensitiveExtendedSelectors() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#?#div:-abp-properties(WiDtH: 215px)", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='case-insensitive-extended-selectors-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersWildcardInExtendedSelector() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#?#div:-abp-properties(cursor:*)", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='wildcard-in-extended-selector-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersRegularExpressionInAbpProperties() throws Exception { + mHelper.addCustomFilter(String.format("%s#?#div:-abp-properties(/width: 12[1-5]px;/)", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='regular-expression-in-abp-properties-fail-1']"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='regular-expression-in-abp-properties-fail-2']"); + // "Not a target" div is not hidden, does not match regular expression. + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div[class='testcase-examplecontent ehe-regex3']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersRegularExpressionInAbpContains() throws Exception { + mHelper.addCustomFilter( + String.format("%s#?#div > div:-abp-contains(/ehe-containsregex\\d/)", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='regular-expression-in-abp-contains-fail-1']"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='regular-expression-in-abp-contains-fail-2']"); + } + + // Exceptions: + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideEmuFiltersException() throws Exception { + // Add a blocking filter, verify element hidden. + mHelper.addCustomFilter( + String.format("%s##.testcase-ehe", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_EXCEPTIONS_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[id='exception-usage-pass-1']"); + + // Add exception filter, verify element no longer hidden. + mHelper.addCustomFilter( + String.format("%s#@#.testcase-ehe", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EMULATION_EXCEPTIONS_TESTPAGES_URL); + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, "div[id='exception-usage-pass-1']"); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesElemhideTestBase.java @@ -0,0 +1,204 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Test; + +import org.chromium.base.test.util.Feature; + +public abstract class TestPagesElemhideTestBase { + public static final String ELEMENT_HIDING_TESTPAGES_URL = + TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "element-hiding"; + public static final String ELEMENT_HIDING_EXCEPTIONS_TESTPAGES_URL = + TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "elemhide"; + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersIdSelector() throws Exception { + mHelper.addCustomFilter(String.format("%s###eh-id", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[id='eh-id']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersIdSelectorDoubleCurlyBraces() throws Exception { + mHelper.addCustomFilter( + String.format("%s##div[id='{{eh-id}}']", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[id='{{eh-id}}']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersClassSelector() throws Exception { + mHelper.addCustomFilter( + String.format("%s##.eh-class", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[class='eh-class']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersDescendantSelector() throws Exception { + mHelper.addCustomFilter(String.format( + "%s##.testcase-area > .eh-descendant", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[class='eh-descendant']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersSiblingSelector() throws Exception { + mHelper.addCustomFilter(String.format("%s##.testcase-examplecontent + .eh-sibling", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[class='eh-sibling']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersAttributeSelector1() throws Exception { + mHelper.addCustomFilter(String.format( + "%s##div[height=\"100\"][width=\"100\"]", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='attribute-selector-1-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersAttributeSelector2() throws Exception { + mHelper.addCustomFilter(String.format("%s##div[href=\"http://testcase-attribute.com/\"]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='attribute-selector-2-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersAttributeSelector3() throws Exception { + mHelper.addCustomFilter(String.format( + "%s##div[style=\"width: 200px;\"]", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='attribute-selector-3-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersStartsWithSelector1() throws Exception { + mHelper.addCustomFilter(String.format("%s##div[href^=\"http://testcase-startswith.com/\"]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='starts-with-selector-1-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersStartsWithSelector2() throws Exception { + mHelper.addCustomFilter(String.format( + "%s##div[style^=\"width: 201px;\"]", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='starts-with-selector-2-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersEndsWithSelector1() throws Exception { + mHelper.addCustomFilter(String.format( + "%s##div[style$=\"width: 202px;\"]", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div[id='ends-with-selector-1-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersContains() throws Exception { + mHelper.addCustomFilter(String.format( + "%s##div[style*=\"width: 203px;\"]", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[id='contains-fail-1']"); + } + + // Exceptions: + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersBasicException() throws Exception { + mHelper.addCustomFilter( + String.format("%s##.ex-elemhide", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.addCustomFilter(String.format( + "||%s/testfiles/elemhide/basic/*", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EXCEPTIONS_TESTPAGES_URL); + // No exceptions added yet, both objects should be blocked. + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "img[id='basic-usage-fail-1']"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[id='basic-usage-pass-1']"); + // Add exception filter and reload. + mHelper.addCustomFilter(String.format( + "@@%s/en/exceptions/elemhide$elemhide", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EXCEPTIONS_TESTPAGES_URL); + // Image should remain blocked, div should be unblocked. + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "img[id='basic-usage-fail-1']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div[id='basic-usage-area'] > div[id='basic-usage-pass-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testElemHideFiltersIframeException() throws Exception { + mHelper.addCustomFilter( + String.format("%s##.targ-elemhide", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.addCustomFilter(String.format( + "||%s/testfiles/elemhide/iframe/*.png", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EXCEPTIONS_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "img[id='iframe-fail-1']"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[id='iframe-pass-1']"); + + // Add exception filter and reload. + mHelper.addCustomFilter(String.format( + "@@%s/en/exceptions/elemhide$elemhide", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(ELEMENT_HIDING_EXCEPTIONS_TESTPAGES_URL); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "img[id='iframe-fail-1']"); + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, "div[id='iframe-pass-1']"); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesExceptionTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesExceptionTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesExceptionTestBase.java @@ -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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Test; + +import org.chromium.base.test.util.Feature; +import org.chromium.components.adblock.AdblockContentType; +import org.chromium.content_public.browser.test.util.JavaScriptUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public abstract class TestPagesExceptionTestBase { + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + mHelper.addFilterList(TestPagesHelperBase.TESTPAGES_SUBSCRIPTION); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyImageExceptions() throws Exception { + final String subdomainImage = String.format( + "https://allowed.subdomain.%s/testfiles/image_exception/subdomain.png", + TestPagesHelperBase.TESTPAGES_DOMAIN); + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "image"); + Assert.assertEquals(2, mHelper.numAllowed()); + Assert.assertEquals(2, mHelper.numAllowedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + Assert.assertTrue(mHelper.isAllowed(subdomainImage)); + Assert.assertTrue(mHelper.isAllowed( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "image_exception/image.png")); + TestVerificationUtils.verifyDisplayedCount(mHelper, 2, "img"); + String numImages = JavaScriptUtils.executeJavaScriptAndWaitForResult( + mHelper.getWebContents(), "document.getElementsByTagName(\"img\").length;"); + Assert.assertEquals("2", numImages); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifySubdocumentException() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "subdocument"); + Assert.assertEquals(1, mHelper.numAllowed()); + Assert.assertTrue(mHelper.isAllowed(TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + + "subdocument_exception/subdocument.html")); + TestVerificationUtils.verifyGreenBackground(mHelper, "exception-target"); + String numFrames = JavaScriptUtils.executeJavaScriptAndWaitForResult( + mHelper.getWebContents(), "window.frames.length;"); + Assert.assertEquals("1", numFrames); + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, "iframe"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyScriptException() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "script"); + Assert.assertEquals(1, mHelper.numAllowed()); + Assert.assertTrue(mHelper.isAllowed( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "script_exception/script.js")); + TestVerificationUtils.verifyGreenBackground(mHelper, "script-target"); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "script_exception/image.png")); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "img[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyStylesheetException() throws Exception { + final String allowedUrl = + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "stylesheet_exception/stylesheet.cs"; + final CountDownLatch countDownLatch = + mHelper.setOnAdMatchedExpectations(null, new HashSet<>(List.of(allowedUrl))); + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "stylesheet"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(1, mHelper.numAllowed()); + Assert.assertTrue(mHelper.isAllowed(allowedUrl)); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "stylesheet_exception/image.png")); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "img"); + TestVerificationUtils.verifyGreenBackground(mHelper, "exception-target"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyXHRException() throws Exception { + final String allowedUrl = + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "xmlhttprequest_exception/text.txt"; + final CountDownLatch countDownLatch = + mHelper.setOnAdMatchedExpectations(null, new HashSet<>(List.of(allowedUrl))); + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "xmlhttprequest"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(1, mHelper.numAllowed()); + Assert.assertTrue(mHelper.isAllowed(allowedUrl)); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyGenericBlockException() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "genericblock"); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "genericblock/specific.png")); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, "img[data-expectedresult='pass']"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "img[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyGenericHideException() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "generichide"); + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, "div[data-expectedresult='pass']"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyDocumentException() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "document"); + Assert.assertTrue(mHelper.isAllowed( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "document/image.png")); + Assert.assertTrue(mHelper.isPageAllowed( + TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "document")); + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, "div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyWebSocketException() throws Exception { + final String wssUrl = + String.format("wss://%s/websocket", TestPagesHelperBase.TESTPAGES_DOMAIN); + final CountDownLatch countDownLatch = + mHelper.setOnAdMatchedExpectations(null, new HashSet<>(List.of(wssUrl))); + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "websocket"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(1, mHelper.numAllowedByType(AdblockContentType.CONTENT_TYPE_WEBSOCKET)); + Assert.assertTrue(mHelper.isAllowed(wssUrl)); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesFilterTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesFilterTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesFilterTestBase.java @@ -0,0 +1,254 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Test; + +import org.chromium.base.test.util.DisabledTest; +import org.chromium.base.test.util.Feature; +import org.chromium.components.adblock.AdblockContentType; +import org.chromium.content_public.browser.test.util.DOMUtils; +import org.chromium.content_public.browser.test.util.JavaScriptUtils; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public abstract class TestPagesFilterTestBase { + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + mHelper.addFilterList(TestPagesHelperBase.TESTPAGES_SUBSCRIPTION); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testBlockingFilters() throws Exception { + final CountDownLatch countDownLatch = mHelper.setOnAdMatchedExpectations( + new HashSet<>(Arrays.asList( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "blocking/full-path.png", + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + + "blocking/partial-path/partial-path.png", + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + + "blocking/wildcard/1/wildcard.png", + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + + "blocking/wildcard/2/wildcard.png", + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "blocking/dynamic.png", + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "blocking/subdomain.png")), + null); + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "blocking"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(6, mHelper.numBlocked()); + Assert.assertEquals(6, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + TestVerificationUtils.verifyHiddenCount(mHelper, 6, "img[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyScriptFilters() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "script"); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_SCRIPT)); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "script/script.js")); + + String childCount = + JavaScriptUtils.executeJavaScriptAndWaitForResult(mHelper.getWebContents(), + "document.getElementById(\"script-target\").childElementCount"); + Assert.assertEquals("1", childCount); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyImageFilters() throws Exception { + final CountDownLatch countDownLatch = mHelper.setOnAdMatchedExpectations( + new HashSet<>(Arrays.asList( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "image/static/static.png", + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + + "image/dynamic/dynamic.png")), + null); + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "image"); + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(2, mHelper.numBlocked()); + Assert.assertEquals(2, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "image/static/static.png")); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "image/dynamic/dynamic.png")); + TestVerificationUtils.verifyHiddenCount(mHelper, 2, "img[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyStylesheetFilters() throws Exception { + final String blockedUrl = + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "stylesheet/stylesheet.cs"; + final CountDownLatch countDownLatch = + mHelper.setOnAdMatchedExpectations(new HashSet<>(List.of(blockedUrl)), null); + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "stylesheet"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals( + 1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_STYLESHEET)); + Assert.assertTrue(mHelper.isBlocked(blockedUrl)); + String value = DOMUtils.getNodeContents(mHelper.getWebContents(), "stylesheet-target"); + Assert.assertEquals("Passed. Stylesheet was blocked.", value); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyXHRFilters() throws Exception { + final String blockedUrl = + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "xmlhttprequest/text.txt"; + final CountDownLatch countDownLatch = + mHelper.setOnAdMatchedExpectations(new HashSet<>(List.of(blockedUrl)), null); + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "xmlhttprequest"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals( + 1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_XMLHTTPREQUEST)); + Assert.assertTrue(mHelper.isBlocked(blockedUrl)); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifySubdocumentFilters() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "subdocument"); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals( + 1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_SUBDOCUMENT)); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "subdocument/subdocument.html")); + // Do not search for iframe within the site's iframes. + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "iframe[data-expectedresult='fail']", + TestVerificationUtils.IncludeSubframes.NO); + } + + @Test + @LargeTest + @Feature({"adblock"}) + @DisabledTest(message = "Please enable again when rewrite filters will be supported") + public void testVerifyRewrite() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "rewrite"); + Assert.assertEquals(3, mHelper.numBlocked()); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_SCRIPT)); + Assert.assertEquals(2, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_MEDIA)); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "rewrite/audio.mp3")); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "rewrite/video.mp4")); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "rewrite/script.js")); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyMatchCaseFilter() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "match-case"); + Assert.assertEquals(2, mHelper.numBlocked()); + Assert.assertEquals(2, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + TestVerificationUtils.verifyHiddenCount(mHelper, 2, "img[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyThirdPartyFilter() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "third-party"); + Assert.assertEquals(2, mHelper.numBlocked()); + Assert.assertEquals(2, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + TestVerificationUtils.verifyHiddenCount(mHelper, 2, "img[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount(mHelper, 2, "img[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyOtherFilter() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "other"); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_OTHER)); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "other/image.png")); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyDomainFilter() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "domain"); + Assert.assertEquals(2, mHelper.numBlocked()); + Assert.assertEquals(2, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "domain/static/target/image.png")); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "domain/dynamic/image.png")); + TestVerificationUtils.verifyHiddenCount(mHelper, 2, "img[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyPingFilter() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "ping"); + // Ping action not yet triggered + Assert.assertEquals(0, mHelper.numBlocked()); + final CountDownLatch countDownLatch = new CountDownLatch(1); + mHelper.setOnAdMatchedLatch(countDownLatch); + // Trigger ping action + JavaScriptUtils.executeJavaScriptAndWaitForResult(mHelper.getWebContents(), + "document.getElementById(\"script-ping-trigger\").click()"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(1, mHelper.numBlocked()); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyPingFilterException() throws Exception { + mHelper.loadUrl(TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "ping"); + // Ping action not yet triggered + Assert.assertEquals(0, mHelper.numAllowed()); + final CountDownLatch countDownLatch = new CountDownLatch(1); + mHelper.setOnAdMatchedLatch(countDownLatch); + // Trigger ping action + JavaScriptUtils.executeJavaScriptAndWaitForResult(mHelper.getWebContents(), + "document.getElementsByClassName(\"testcase-trigger\")[0].click()"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(1, mHelper.numAllowed()); + Assert.assertEquals(0, mHelper.numBlocked()); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesHeaderFilterTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesHeaderFilterTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesHeaderFilterTestBase.java @@ -0,0 +1,125 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Test; + +import org.chromium.base.test.util.Feature; +import org.chromium.components.adblock.AdblockContentType; + +public abstract class TestPagesHeaderFilterTestBase { + public static final String HEADER_TESTPAGES_URL = + TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "header"; + public static final String HEADER_EXCEPTIONS_TESTPAGES_URL = + TestPagesHelperBase.EXCEPTION_TESTPAGES_TESTCASES_ROOT + "header"; + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHeaderFilterScript() throws Exception { + mHelper.addCustomFilter( + String.format("||%s/testfiles/header/$header=content-type=application/javascript", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(HEADER_TESTPAGES_URL); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_SCRIPT)); + Assert.assertTrue(mHelper.isBlocked(String.format( + "https://%s/testfiles/header/script.js", TestPagesHelperBase.TESTPAGES_DOMAIN))); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#functionproperty-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHeaderFilterImage() throws Exception { + mHelper.addCustomFilter( + String.format("||%s/testfiles/header/image.png$header=content-type=image/png", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(HEADER_TESTPAGES_URL); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + Assert.assertTrue(mHelper.isBlocked(String.format( + "https://%s/testfiles/header/image.png", TestPagesHelperBase.TESTPAGES_DOMAIN))); + TestVerificationUtils.verifyDisplayedCount(mHelper, 0, "img[id='image-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHeaderFilterImageAndComma() throws Exception { + mHelper.addCustomFilter(String.format("||%s/testfiles/header/image2.png$header=date=\\x2c", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(HEADER_TESTPAGES_URL); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + Assert.assertTrue(mHelper.isBlocked(String.format( + "https://%s/testfiles/header/image2.png", TestPagesHelperBase.TESTPAGES_DOMAIN))); + TestVerificationUtils.verifyDisplayedCount(mHelper, 0, "img[id='comma-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHeaderFilterStylesheet() throws Exception { + mHelper.addCustomFilter(String.format("||%s/testfiles/header/$header=content-type=text/css", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(HEADER_TESTPAGES_URL); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals( + 1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_STYLESHEET)); + Assert.assertTrue( + mHelper.isBlocked(String.format("https://%s/testfiles/header/stylesheet.css", + TestPagesHelperBase.TESTPAGES_DOMAIN))); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHeaderFilterException() throws Exception { + // Add blocking filter, expect blocked image + mHelper.addCustomFilter( + String.format("||%s/testfiles/header_exception/$header=content-type=image/png", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(HEADER_EXCEPTIONS_TESTPAGES_URL); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "img[id='image-header-exception-pass-1']"); + + // Add exception filter, expect image allowed + mHelper.addCustomFilter(String.format( + "@@%s/testfiles/header_exception/$header", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(HEADER_EXCEPTIONS_TESTPAGES_URL); + Assert.assertEquals(1, mHelper.numAllowed()); + Assert.assertEquals(1, mHelper.numAllowedByType(AdblockContentType.CONTENT_TYPE_IMAGE)); + Assert.assertTrue( + mHelper.isAllowed(String.format("https://%s/testfiles/header_exception/image.png", + TestPagesHelperBase.TESTPAGES_DOMAIN))); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "img[id='image-header-exception-pass-1']"); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesHelperBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesHelperBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesHelperBase.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import org.junit.Assert; + +import org.chromium.base.Log; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.components.adblock.AdblockContentType; +import org.chromium.components.adblock.AdblockController; +import org.chromium.content_public.browser.WebContents; +import org.chromium.content_public.browser.test.util.TestThreadUtils; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public abstract class TestPagesHelperBase { + public static final String TESTPAGES_DOMAIN = "abptestpages.org"; + public static final String TESTPAGES_BASE_URL = "https://" + TESTPAGES_DOMAIN; + public static final String TESTPAGES_TESTCASES_ROOT = TESTPAGES_BASE_URL + "/en/"; + public static final String FILTER_TESTPAGES_TESTCASES_ROOT = + TESTPAGES_TESTCASES_ROOT + "filters/"; + public static final String EXCEPTION_TESTPAGES_TESTCASES_ROOT = + TESTPAGES_TESTCASES_ROOT + "exceptions/"; + public static final String CIRCUMVENTION_TESTPAGES_TESTCASES_ROOT = + TESTPAGES_TESTCASES_ROOT + "circumvention/"; + public static final String SITEKEY_TESTPAGES_TESTCASES_ROOT = + EXCEPTION_TESTPAGES_TESTCASES_ROOT + "sitekey"; + public static final String SNIPPETS_TESTPAGES_TESTCASES_ROOT = + TESTPAGES_TESTCASES_ROOT + "snippets/"; + public static final String TESTPAGES_RESOURCES_ROOT = TESTPAGES_BASE_URL + "/testfiles/"; + public static final String TESTPAGES_SUBSCRIPTION = + TESTPAGES_TESTCASES_ROOT + "/abp-testcase-subscription.txt"; + public static final int TEST_TIMEOUT_SEC = 30; + + private URL mTestSubscriptionUrl; + private final CallbackHelper mHelper = new CallbackHelper(); + private final TestAdBlockedObserver mObserver = new TestAdBlockedObserver(); + private final TestSubscriptionUpdatedObserver mSubscriptionUpdateObserver = + new TestSubscriptionUpdatedObserver(); + + private class TestSubscriptionUpdatedObserver + implements AdblockController.SubscriptionUpdateObserver { + @Override + public void onSubscriptionDownloaded(final URL url) { + if (mTestSubscriptionUrl == null) return; + if (url.toString().contains(mTestSubscriptionUrl.toString())) { + Log.d("TestSubscriptionUpdatedObserver", + "Notify subscription updated: " + url.toString()); + mHelper.notifyCalled(); + } + } + } + + public void setUp() { + TestThreadUtils.runOnUiThreadBlocking(() -> { + AdblockController.getInstance().addOnAdBlockedObserver(mObserver); + AdblockController.getInstance().addSubscriptionUpdateObserver( + mSubscriptionUpdateObserver); + }); + } + + public void addFilterList(final String filterListUrl) { + try { + mTestSubscriptionUrl = new URL(filterListUrl); + mObserver.setExpectedSubscriptionUrl(mTestSubscriptionUrl); + } catch (MalformedURLException ignored) { + } + Assert.assertNotNull("Test subscription url", mTestSubscriptionUrl); + TestThreadUtils.runOnUiThreadBlocking(() -> { + AdblockController.getInstance().installSubscription(mTestSubscriptionUrl); + }); + try { + mHelper.waitForCallback(0, 1, TEST_TIMEOUT_SEC, TimeUnit.SECONDS); + } catch (TimeoutException e) { + Assert.assertEquals( + "Test subscription was properly added", "Failed to add test subscription"); + } + } + + public void addCustomFilter(final String filter) { + TestThreadUtils.runOnUiThreadBlocking( + () -> { AdblockController.getInstance().addCustomFilter(filter); }); + } + + public void tearDown() { + TestThreadUtils.runOnUiThreadBlocking(() -> { + AdblockController.getInstance().removeSubscriptionUpdateObserver( + mSubscriptionUpdateObserver); + AdblockController.getInstance().removeOnAdBlockedObserver(mObserver); + }); + } + + // Note: Use either setOnAdMatchedLatch XOR setOnAdMatchedExpectations + public void setOnAdMatchedLatch(final CountDownLatch countDownLatch) { + Assert.assertTrue( + mObserver.countDownLatch == null || mObserver.countDownLatch.getCount() == 0); + mObserver.countDownLatch = countDownLatch; + } + + // Note: Use either setOnAdMatchedLatch XOR setOnAdMatchedExpectations + public CountDownLatch setOnAdMatchedExpectations( + final Set onBlocked, final Set onAllowed) { + Assert.assertTrue( + mObserver.countDownLatch == null || mObserver.countDownLatch.getCount() == 0); + mObserver.countDownLatch = new CountDownLatch(1); + mObserver.expectedBlocked = onBlocked; + mObserver.expectedAllowed = onAllowed; + return mObserver.countDownLatch; + } + + public boolean isBlocked(final String url) { + return mObserver.isBlocked(url); + } + + public boolean isPopupBlocked(final String url) { + return mObserver.isPopupBlocked(url); + } + + public int numBlockedByType(final AdblockContentType type) { + return mObserver.numBlockedByType(type); + } + + public int numBlockedPopups() { + return mObserver.numBlockedPopups(); + } + + public int numAllowedByType(final AdblockContentType type) { + return mObserver.numAllowedByType(type); + } + + public int numAllowedPopups() { + return mObserver.numAllowedPopups(); + } + + public boolean isAllowed(final String url) { + return mObserver.isAllowed(url); + } + + public boolean isPageAllowed(final String url) { + return mObserver.isPageAllowed(url); + } + + public boolean isPopupAllowed(final String url) { + return mObserver.isPopupAllowed(url); + } + + public abstract void loadUrl(final String url) throws Exception; + + public abstract void loadUrlWaitForContent(final String url) throws Exception; + + public abstract WebContents getWebContents(); + + public int numBlocked() { + return mObserver.blockedInfos.size(); + } + + public int numAllowed() { + return mObserver.allowedInfos.size(); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesRewriteTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesRewriteTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesRewriteTestBase.java @@ -0,0 +1,153 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Test; + +import org.chromium.base.test.util.Feature; +import org.chromium.content_public.browser.test.util.JavaScriptUtils; + +public abstract class TestPagesRewriteTestBase { + public static final String REWRITE_TEST_URL = + TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "rewrite"; + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewriteScript() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/*.js$rewrite=abp-resource:blank-js,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + TestVerificationUtils.verifyDisplayedCount(mHelper, 0, "div[id='script-fail-1']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewriteStylesheet() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/*.css$rewrite=abp-resource:blank-css,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + TestVerificationUtils.verifyGreenBackground(mHelper, "stylesheet-target"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewriteSubdocument() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/*.html$rewrite=abp-resource:blank-html,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + TestVerificationUtils.verifySelfTestPass(mHelper, "subdocument-target"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewriteText() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/*.txt$rewrite=abp-resource:blank-text,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + TestVerificationUtils.verifySelfTestPass(mHelper, "text-status"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewriteGif() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/1x1.gif$rewrite=abp-resource:1x1-transparent-gif,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + TestVerificationUtils.verifySelfTestPass(mHelper, "1x1-target"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewrite2x2Png() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/2x2.png$rewrite=abp-resource:2x2-transparent-png,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + TestVerificationUtils.verifySelfTestPass(mHelper, "2x2-target"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewrite3x2Png() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/3x2.png$rewrite=abp-resource:3x2-transparent-png,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + TestVerificationUtils.verifySelfTestPass(mHelper, "3x2-target"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewrite32x32Png() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/32x32.png$rewrite=abp-resource:32x32-transparent-png," + + "domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + TestVerificationUtils.verifySelfTestPass(mHelper, "32x32-target"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewriteAudio() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/*.mp3$rewrite=abp-resource:blank-mp3,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + String value = JavaScriptUtils.executeJavaScriptAndWaitForResult(mHelper.getWebContents(), + "document.getElementById('audio-area').lastChild.getAttribute" + + "('data-expectedresult')"); + Assert.assertEquals("\"pass\"", value); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testRewriteVideo() throws Exception { + mHelper.addCustomFilter(String.format( + "||%s/testfiles/rewrite/*.mp4$rewrite=abp-resource:blank-mp4,domain=%s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrl(REWRITE_TEST_URL); + String value = JavaScriptUtils.executeJavaScriptAndWaitForResult(mHelper.getWebContents(), + "document.getElementById('video-area').lastChild.getAttribute" + + "('data-expectedresult')"); + Assert.assertEquals("\"pass\"", value); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesSiteKeyTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesSiteKeyTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesSiteKeyTestBase.java @@ -0,0 +1,58 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Test; + +import org.chromium.base.test.util.Feature; + +public abstract class TestPagesSiteKeyTestBase { + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifySitekeyException() throws Exception { + mHelper.addCustomFilter( + String.format("%s#@#[data-adblockkey]", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.addCustomFilter( + String.format("%s##.testcase-sitekey-eh", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.addCustomFilter(String.format( + "||%s/testfiles/sitekey/outofframe.png", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.addCustomFilter(String.format( + "||%s/testfiles/sitekey/inframe.png", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.addCustomFilter( + "@@$document,sitekey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANGtTstne7e8MbmDHDiMFkGbcuBgXmiVesGOG3gtYeM1EkrzVhBjGUvKXYE4GLFwqty3v5MuWWbvItUWBTYoVVsCAwEAAQ"); + mHelper.loadUrl(TestPagesHelperBase.SITEKEY_TESTPAGES_TESTCASES_ROOT); + Assert.assertEquals(1, mHelper.numBlocked()); + Assert.assertTrue(mHelper.isBlocked( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "sitekey/outofframe.png")); + Assert.assertEquals(1, mHelper.numAllowed()); + Assert.assertTrue(mHelper.isAllowed( + TestPagesHelperBase.TESTPAGES_RESOURCES_ROOT + "sitekey/inframe.png")); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "img"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div"); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesSnippetsTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesSnippetsTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesSnippetsTestBase.java @@ -0,0 +1,562 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Test; + +import org.chromium.base.test.util.Feature; + +public abstract class TestPagesSnippetsTestBase { + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortCurrentInlineScriptBasic() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#abort-current-inline-script console.group", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-current-inline-script"); + // All "Abort" snippets cancel creation of the target div, so it won't be hidden - it will + // not exist in DOM. Therefore we verify it's not displayed instead of verifying it's + // hidden. + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#basic-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortCurrentInlineScriptSearch() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#abort-current-inline-script console.info acis-search", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-current-inline-script"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#search-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortCurrentInlineScriptRegex() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#abort-current-inline-script console.warn '/acis-regex[1-2]/'", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-current-inline-script"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#regex-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnPropertyReadBasic() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#abort-on-property-read aoprb", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "abort-on-property-read"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#basic-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnPropertyReadSubProperty() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#abort-on-property-read aopr.sp", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "abort-on-property-read"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#subproperty-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnPropertyReadFunctionProperty() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#abort-on-property-read aoprf.fp", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "abort-on-property-read"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#functionproperty-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnPropertyWriteBasic() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#abort-on-property-write window.aopwb", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "abort-on-property-write"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#basic-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnPropertyWriteSubProperty() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#abort-on-property-write window.aopwsp", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "abort-on-property-write"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#subproperty-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnPropertyWriteFunctionProperty() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#abort-on-property-write aopwf.fp", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "abort-on-property-write"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#functionproperty-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnIframePropertyReadBasic() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#abort-on-iframe-property-read aoiprb", TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-on-iframe-property-read"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#basic-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnIframePropertyReadSubProperty() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#abort-on-iframe-property-read aoipr.sp", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-on-iframe-property-read"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#subproperty-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnIframePropertyReadMultipleProperties() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#abort-on-iframe-property-read aoipr1 aoipr2", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-on-iframe-property-read"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#multipleproperties-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnIframePropertyWriteBasic() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#abort-on-iframe-property-write aoipwb", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-on-iframe-property-write"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#basic-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnIframePropertyWriteSubProperty() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#abort-on-iframe-property-write aoipw.sp", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-on-iframe-property-write"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#subproperty-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testAbortOnIframePropertyWriteMultipleProperties() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#abort-on-iframe-property-write aoipw1 aoipw2", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "abort-on-iframe-property-write"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#multipleproperties-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsStatic() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-contains 'hic-basic-static' p[id]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-contains"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "p#hic-static-id"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsDynamic() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-contains 'hic-basic-dynamic' p[id]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-contains"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "p#hic-dynamic-id"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsSearch() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-contains 'hic-search' p[id] .target", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-contains"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div#search2-target > p.target"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#search1-target > p[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsRegex() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-contains /hic-regex-[2-3]/ p[id]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-contains"); + // "hic-regex-1" does not match regex, should remain displayed. + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, "p#hic-regex-1"); + + // "hic-regex-2" and "hic-regex-2" do match regex, should be hidden. + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "p#hic-regex-2"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "p#hic-regex-3"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsFrame() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-contains hidden span#frame-target", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-contains"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "span#frame-target"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsAndMatchesStyleStatic() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#hide-if-contains-and-matches-style hicamss div[id] span.label /./ 'display:" + + " inline;'", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "hide-if-contains-and-matches-style"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#static-usage-area > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#static-usage-area > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsAndMatchesStyleDynamic() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#hide-if-contains-and-matches-style hicamsd div[id] span.label /./ 'display:" + + " inline;'", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "hide-if-contains-and-matches-style"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#dynamic-target > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#dynamic-target > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsImage() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-contains-image " + + "/^89504e470d0a1a0a0000000d4948445200000064000000640802/ " + + "div[shouldhide] div", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-contains-image"); + TestVerificationUtils.verifyHiddenCount(mHelper, 2, "div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsVisibleTextBasicUsage() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#hide-if-contains-visible-text Sponsored-hicvt-basic '#parent-basic > " + + ".article' '#parent-basic > .article .label'", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "hide-if-contains-visible-text"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#parent-basic > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfContainsVisibleTextContentUsage() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#hide-if-contains-visible-text Sponsored-hicvt-content '#parent-content > " + + ".article' '#parent-content > .article .label'", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "hide-if-contains-visible-text"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#parent-content > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfHasAndMatchesStyleBasicUsage() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#hide-if-has-and-matches-style a[href=\"#basic-target-ad\"] div[id] span" + + ".label /./ 'display: inline;'", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "hide-if-has-and-matches-style"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#basic-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfHasAndMatchesStyleLegitElements() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#hide-if-has-and-matches-style a[href=\"#comments-target-ad\"] div[id] span" + + ".label ';' /\\\\bdisplay:\\ inline\\;/", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "hide-if-has-and-matches-style"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#comments-target > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfLabeledBy() throws Exception { + mHelper.addCustomFilter(String.format( + "%s#$#hide-if-labelled-by 'Label' '#hilb-target [aria-labelledby]' '#hilb-target'", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-labelled-by"); + TestVerificationUtils.verifyHiddenCount(mHelper, 1, "div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount(mHelper, 1, "div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfMatchesXPathBasicStaticUsage() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-matches-xpath //*[@id=\"isnfnv\"]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-matches-xpath"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#basic-static-usage-area > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#basic-static-usage-area > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfMatchesXPath3BasicStaticUsage() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-matches-xpath3 //*[@id=\"isnfnv\"]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-matches-xpath"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#basic-static-usage-area > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#basic-static-usage-area > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfMatchesXPathClassUsage() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#hide-if-matches-xpath //*[@class=\"to-be-hidden\"]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-matches-xpath"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#class-usage-area > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#class-usage-area > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfMatchesXPath3ClassUsage() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#hide-if-matches-xpath3 //*[@class=\"to-be-hidden\"]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-matches-xpath"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#class-usage-area > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#class-usage-area > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfMatchesXPathIdStartsWith() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#hide-if-matches-xpath //div[starts-with(@id,\"fail\")]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-matches-xpath"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#hide-if-id-starts-with-area > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#hide-if-id-starts-with-area > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfMatchesXPath3IdStartsWith() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#hide-if-matches-xpath3 //div[starts-with(@id,\"fail\")]", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-matches-xpath"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#hide-if-id-starts-with-area > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#hide-if-id-starts-with-area > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfShadowContainsBasicUsage() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#hide-if-shadow-contains 'hisc-basic' p", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-shadow-contains"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#basic-target > p[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testHideIfShadowContainsRegexUsage() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#hide-if-shadow-contains '/hisc-regex[1-2]/' div", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "hide-if-shadow-contains"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 2, "div#regex-target > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#regex-target > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testJsonPrune() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#json-prune 'data-expectedresult jsonprune aria-label'", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "json-prune?delay=100"); + // The object does not get hidden, it no longer exists in the DOM. + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#testcase-area > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testOverridePropertyRead() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#override-property-read overridePropertyRead.fp false", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent( + TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + "override-property-read"); + TestVerificationUtils.verifyHiddenCount( + mHelper, 1, "div#basic-target > div[data-expectedresult='fail']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testStripFetchQueryParameterBasicUsage() throws Exception { + mHelper.addCustomFilter(String.format("%s#$#strip-fetch-query-parameter basicBlocked %s", + TestPagesHelperBase.TESTPAGES_DOMAIN, TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "strip-fetch-query-parameter"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 0, "div#basic-target > div[data-expectedresult='fail']"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 1, "div#basic-target > div[data-expectedresult='pass']"); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testStripFetchQueryParameterOtherUsage() throws Exception { + mHelper.addCustomFilter( + String.format("%s#$#strip-fetch-query-parameter otherAllowed2 other-domain", + TestPagesHelperBase.TESTPAGES_DOMAIN)); + mHelper.loadUrlWaitForContent(TestPagesHelperBase.SNIPPETS_TESTPAGES_TESTCASES_ROOT + + "strip-fetch-query-parameter"); + TestVerificationUtils.verifyDisplayedCount( + mHelper, 2, "div#other-target > div[data-expectedresult='pass']"); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesWebsocketTestBase.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesWebsocketTestBase.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestPagesWebsocketTestBase.java @@ -0,0 +1,56 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import androidx.test.filters.LargeTest; + +import org.junit.Assert; +import org.junit.Test; + +import org.chromium.base.test.util.Feature; +import org.chromium.components.adblock.AdblockContentType; + +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +public abstract class TestPagesWebsocketTestBase { + private TestPagesHelperBase mHelper; + + protected void setUp(TestPagesHelperBase helper) { + mHelper = helper; + mHelper.addCustomFilter( + String.format("$websocket,domain=%s", TestPagesHelperBase.TESTPAGES_DOMAIN)); + } + + @Test + @LargeTest + @Feature({"adblock"}) + public void testVerifyWebsocketFilter() throws Exception { + final String wssUrl = + String.format("wss://%s/websocket", TestPagesHelperBase.TESTPAGES_DOMAIN); + final CountDownLatch countDownLatch = + mHelper.setOnAdMatchedExpectations(new HashSet<>(List.of(wssUrl)), null); + mHelper.loadUrl(TestPagesHelperBase.FILTER_TESTPAGES_TESTCASES_ROOT + "websocket"); + // Wait with 10 seconds max timeout + countDownLatch.await(10, TimeUnit.SECONDS); + Assert.assertEquals(1, mHelper.numBlockedByType(AdblockContentType.CONTENT_TYPE_WEBSOCKET)); + Assert.assertTrue(mHelper.isBlocked(wssUrl)); + } +} diff --git a/components/adblock/android/javatests/src/org/chromium/components/adblock/TestVerificationUtils.java b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestVerificationUtils.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/javatests/src/org/chromium/components/adblock/TestVerificationUtils.java @@ -0,0 +1,163 @@ +/* + * 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 . + */ + +package org.chromium.components.adblock; + +import org.junit.Assert; + +import org.chromium.content_public.browser.test.util.JavaScriptUtils; + +import java.util.Locale; +import java.util.concurrent.TimeoutException; + +public class TestVerificationUtils { + public enum IncludeSubframes { + YES, + NO, + } + + private static final String STATUS_OK = "\"OK\""; + + private static final String MATCHES_HIDDEN_FUNCTION = "let matches = function(element) {" + + " return window.getComputedStyle(element).display == \"none\";" + + "}"; + + private static final String MATCHES_DISPLAYED_FUNCTION = "let matches = function(element) {" + + " return window.getComputedStyle(element).display != \"none\";" + + "}"; + + private static final String COUNT_ELEMENT_FUNCTION = + "let countElements = function(selector, includeSubframes) {" + + " let count = 0;" + + " for (let element of document.querySelectorAll(selector)) {" + + " if (matches(element))" + + " ++count;" + + " }" + + " if (includeSubframes) {" + + " for (let frame of document.querySelectorAll(\"iframe\")) {" + + " for (let element of frame.contentWindow.document.body" + + ".querySelectorAll(selector)) {" + + " if (matches(element))" + + " ++count;" + + " }" + + " }" + + " }" + + " return count;" + + "}"; + + private static final String WAIT_FOR_COUNT_FUNCTION_WRAPPER = "(function () {" + + "%s\n" // matches() definition placeholder + + "%s\n" // countElements() definition placeholder + + "%s\n" // WAIT_FUNCTION placeholder which calls countElements() as a predicate + + "}());"; + + // Poll every 100 ms until condition is met or 4 seconds timeout occurs + // Internal timeout of JavaScriptUtils.runJavascriptWithAsyncResult() is 5 seconds + // so our wait timeout needs to be shorter. + private static final String WAIT_FUNCTION = "function waitWithTimeout() {" + + " return new Promise(resolve => {" + + " let repeat = 40;" + + " const id = setInterval(() => {" + + " --repeat;" + + " if (%s) {" // predicate placeholder + + " clearInterval(id);" + + " resolve('OK');" + + " } else if (repeat == 0) {" + + " clearInterval(id);" + + " resolve('Timeout');" + + " }" + + " }, 100);" + + " });" + + "};" + + "waitWithTimeout().then((result) => { domAutomationController.send(result); });"; + + private static void verifyMatchesCount(final TestPagesHelperBase helper, final int num, + final String matchesFunction, final String selector, IncludeSubframes includeSubframes) + throws TimeoutException { + final String boolIncludeSubframes = + includeSubframes == IncludeSubframes.YES ? "true" : "false"; + final String predicate = String.format(Locale.getDefault(), + "countElements(\"%s\", %s) == %d", selector, boolIncludeSubframes, num); + final String waitFunction = String.format(WAIT_FUNCTION, predicate); + final String js = String.format(WAIT_FOR_COUNT_FUNCTION_WRAPPER, matchesFunction, + COUNT_ELEMENT_FUNCTION, waitFunction); + final String result = + JavaScriptUtils.runJavascriptWithAsyncResult(helper.getWebContents(), js); + Assert.assertEquals(STATUS_OK, result); + } + + public static void verifyHiddenCount(final TestPagesHelperBase helper, final int num, + final String selector) throws TimeoutException { + verifyHiddenCount(helper, num, selector, IncludeSubframes.YES); + } + + public static void verifyHiddenCount(final TestPagesHelperBase helper, final int num, + final String selector, final IncludeSubframes includeSubframes) + throws TimeoutException { + verifyMatchesCount(helper, num, MATCHES_HIDDEN_FUNCTION, selector, includeSubframes); + } + + public static void verifyDisplayedCount(final TestPagesHelperBase helper, final int num, + final String selector) throws TimeoutException { + verifyDisplayedCount(helper, num, selector, IncludeSubframes.YES); + } + + public static void verifyDisplayedCount(final TestPagesHelperBase helper, final int num, + final String selector, final IncludeSubframes includeSubframes) + throws TimeoutException { + verifyMatchesCount(helper, num, MATCHES_DISPLAYED_FUNCTION, selector, includeSubframes); + } + + public static void verifyCondition(final TestPagesHelperBase helper, final String predicate) + throws TimeoutException { + final String waitFunction = String.format(WAIT_FUNCTION, predicate); + Assert.assertEquals(STATUS_OK, + JavaScriptUtils.runJavascriptWithAsyncResult( + helper.getWebContents(), waitFunction)); + } + + public static void verifyGreenBackground(final TestPagesHelperBase helper, final String elemId) + throws TimeoutException { + verifyCondition(helper, + "window.getComputedStyle(document.getElementById('" + elemId + + "')).backgroundColor == 'rgb(13, 199, 75)'"); + } + + // For some cases it is better to rely on page script testing element + // rather than invent a specific script to check condition. For example + // checks for rewrite filters replaces content proper way. + public static void verifySelfTestPass(final TestPagesHelperBase helper, final String elemId) + throws TimeoutException { + verifyCondition(helper, + "document.getElementById('" + elemId + + "').getAttribute('data-expectedresult') == 'pass'"); + } + + public static void expectResourceBlocked(final TestPagesHelperBase helper, final String elemId) + throws TimeoutException { + verifyCondition(helper, + "window.getComputedStyle(document.getElementById('" + elemId + + "')).display == 'none'"); + } + + public static void expectResourceShown(final TestPagesHelperBase helper, final String elemId) + throws TimeoutException { + verifyCondition(helper, + "window.getComputedStyle(document.getElementById('" + elemId + + "')).display == 'inline'"); + } +} diff --git a/components/adblock/android/resource_classification_notifier_bindings.cc b/components/adblock/android/resource_classification_notifier_bindings.cc new file mode 100644 --- /dev/null +++ b/components/adblock/android/resource_classification_notifier_bindings.cc @@ -0,0 +1,164 @@ +/* + * 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 "components/adblock/android/resource_classification_notifier_bindings.h" + +#include +#include + +#include "base/android/jni_android.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "base/ranges/algorithm.h" +#include "components/adblock/android/java_bindings_getters.h" +#include "components/adblock/android/jni_headers/ResourceClassificationNotifier_jni.h" +#include "components/adblock/content/browser/resource_classification_runner.h" +#include "components/adblock/core/common/adblock_utils.h" + +namespace adblock { + +using base::android::AttachCurrentThread; +using base::android::CheckException; +using base::android::ConvertJavaStringToUTF8; +using base::android::ConvertUTF8ToJavaString; +using base::android::GetClass; +using base::android::JavaParamRef; +using base::android::JavaRef; +using base::android::MethodID; +using base::android::ScopedJavaGlobalRef; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaArrayOfObjects; +using base::android::ToJavaArrayOfStrings; + +ResourceClassificationNotifierBindings::ResourceClassificationNotifierBindings( + ResourceClassificationRunner* classification_runner) + : classification_runner_(classification_runner) { + classification_runner_->AddObserver(this); +} + +ResourceClassificationNotifierBindings:: + ~ResourceClassificationNotifierBindings() { + classification_runner_->RemoveObserver(this); +} + +void ResourceClassificationNotifierBindings::Bind( + JavaObjectWeakGlobalRef resource_classifier_java) { + bound_counterpart_ = resource_classifier_java; +} + +void ResourceClassificationNotifierBindings::OnAdMatched( + const GURL& url, + FilterMatchResult result, + const std::vector& parent_frame_urls, + ContentType content_type, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(render_frame_host); + DCHECK(result == FilterMatchResult::kBlockRule || + result == FilterMatchResult::kAllowRule); + const bool was_blocked = result == FilterMatchResult::kBlockRule; + DVLOG(3) << "[eyeo] Ad matched " << url << "(type: " << content_type + << (was_blocked ? ", blocked" : ", allowed") << ")"; + JNIEnv* env = AttachCurrentThread(); + + ScopedJavaLocalRef obj = bound_counterpart_.get(env); + if (obj.is_null()) { + return; + } + + ScopedJavaLocalRef j_url = ConvertUTF8ToJavaString(env, url.spec()); + ScopedJavaLocalRef j_parents = + ToJavaArrayOfStrings(env, adblock::utils::ConvertURLs(parent_frame_urls)); + ScopedJavaLocalRef j_subscription = + ConvertUTF8ToJavaString(env, subscription.spec()); + ScopedJavaLocalRef j_configuration = + ConvertUTF8ToJavaString(env, configuration_name); + int tab_id = adblock::GetTabId(render_frame_host); + Java_ResourceClassificationNotifier_adMatchedCallback( + env, obj, j_url, was_blocked, j_parents, j_subscription, j_configuration, + static_cast(content_type), tab_id); +} + +void ResourceClassificationNotifierBindings::OnPageAllowed( + const GURL& url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(render_frame_host); + DVLOG(3) << "[eyeo] Page allowed " << url; + JNIEnv* env = AttachCurrentThread(); + + ScopedJavaLocalRef obj = bound_counterpart_.get(env); + if (obj.is_null()) { + return; + } + + ScopedJavaLocalRef j_url = ConvertUTF8ToJavaString(env, url.spec()); + ScopedJavaLocalRef j_subscription = + ConvertUTF8ToJavaString(env, subscription.spec()); + ScopedJavaLocalRef j_configuration = + ConvertUTF8ToJavaString(env, configuration_name); + int tab_id = adblock::GetTabId(render_frame_host); + Java_ResourceClassificationNotifier_pageAllowedCallback( + env, obj, j_url, j_subscription, j_configuration, tab_id); +} + +void ResourceClassificationNotifierBindings::OnPopupMatched( + const GURL& url, + FilterMatchResult result, + const GURL& opener_url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(render_frame_host); + DCHECK(result == FilterMatchResult::kBlockRule || + result == FilterMatchResult::kAllowRule); + const bool was_blocked = result == FilterMatchResult::kBlockRule; + DVLOG(3) << "[eyeo] Popup matched " << url + << (was_blocked ? ", blocked" : ", allowed"); + JNIEnv* env = AttachCurrentThread(); + + ScopedJavaLocalRef obj = bound_counterpart_.get(env); + if (obj.is_null()) { + return; + } + + ScopedJavaLocalRef j_url = ConvertUTF8ToJavaString(env, url.spec()); + ScopedJavaLocalRef j_opener = + ConvertUTF8ToJavaString(env, opener_url.spec()); + ScopedJavaLocalRef j_subscription = + ConvertUTF8ToJavaString(env, subscription.spec()); + ScopedJavaLocalRef j_configuration = + ConvertUTF8ToJavaString(env, configuration_name); + int tab_id = adblock::GetTabId(render_frame_host); + Java_ResourceClassificationNotifier_popupMatchedCallback( + env, obj, j_url, was_blocked, j_opener, j_subscription, j_configuration, + tab_id); +} + +} // namespace adblock + +static void JNI_ResourceClassificationNotifier_Bind( + JNIEnv* env, + const base::android::JavaParamRef& caller) { + auto& bindings = adblock::GetResourceClassificationNotifierBindings(); + bindings.Bind(JavaObjectWeakGlobalRef(env, caller)); +} diff --git a/components/adblock/android/resource_classification_notifier_bindings.h b/components/adblock/android/resource_classification_notifier_bindings.h new file mode 100644 --- /dev/null +++ b/components/adblock/android/resource_classification_notifier_bindings.h @@ -0,0 +1,69 @@ +/* + * 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 COMPONENTS_ADBLOCK_ANDROID_RESOURCE_CLASSIFICATION_NOTIFIER_BINDINGS_H_ +#define COMPONENTS_ADBLOCK_ANDROID_RESOURCE_CLASSIFICATION_NOTIFIER_BINDINGS_H_ + +#include +#include +#include "base/android/jni_weak_ref.h" +#include "base/memory/raw_ptr.h" +#include "base/sequence_checker.h" +#include "components/adblock/content/browser/resource_classification_runner.h" +#include "components/keyed_service/core/keyed_service.h" +#include "components/prefs/pref_service.h" + +namespace adblock { + +class ResourceClassificationNotifierBindings + : public KeyedService, + public ResourceClassificationRunner::Observer { + public: + explicit ResourceClassificationNotifierBindings( + ResourceClassificationRunner* classification_runner); + ~ResourceClassificationNotifierBindings() override; + + void Bind(JavaObjectWeakGlobalRef resource_classifier_java); + + // ResourceClassificationRunner::Observer + void OnAdMatched(const GURL& url, + FilterMatchResult match_result, + const std::vector& parent_frame_urls, + ContentType content_type, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override; + void OnPageAllowed(const GURL& url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override; + void OnPopupMatched(const GURL& url, + FilterMatchResult match_result, + const GURL& opener_url, + content::RenderFrameHost* render_frame_host, + const GURL& subscription, + const std::string& configuration_name) override; + + private: + SEQUENCE_CHECKER(sequence_checker_); + raw_ptr classification_runner_; + JavaObjectWeakGlobalRef bound_counterpart_; +}; + +} // namespace adblock + +#endif // COMPONENTS_ADBLOCK_ANDROID_RESOURCE_CLASSIFICATION_NOTIFIER_BINDINGS_H_ -- 2.25.1