From: chromium-sdk Date: Fri, 14 Feb 2025 08:26:39 +0100 Subject: eyeo Browser Ad filtering Solution: Android API Module Based on Chromium 133.0.6943.49 Pre-requisites: eyeo Browser Ad filtering Solution: Base Module --- chrome/android/BUILD.gn | 12 + chrome/android/chrome_java_sources.gni | 4 + .../browser/adblock/TabIdTranslator.java | 36 ++ chrome/browser/BUILD.gn | 4 + .../android/adblock/tab_id_translator_jni.cc | 43 ++ components/adblock/android/BUILD.gn | 107 +++++ .../adblock/android/adblock_controller_jni.cc | 133 ++++++ .../filtering_configuration_android.cc | 246 ++++++++++ .../android/filtering_configuration_android.h | 94 ++++ .../components/adblock/AdblockController.java | 283 ++++++++++++ .../components/adblock/AdblockSwitches.java | 22 + .../components/adblock/ContentType.java | 61 +++ .../adblock/FilteringConfiguration.java | 424 ++++++++++++++++++ .../ResourceClassificationNotifier.java | 233 ++++++++++ .../adblock/ResourceFilteringCounters.java | 111 +++++ ...esource_classification_notifier_android.cc | 166 +++++++ ...resource_classification_notifier_android.h | 72 +++ 17 files changed, 2051 insertions(+) create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/adblock/TabIdTranslator.java create mode 100644 chrome/browser/android/adblock/tab_id_translator_jni.cc create mode 100644 components/adblock/android/BUILD.gn create mode 100644 components/adblock/android/adblock_controller_jni.cc create mode 100644 components/adblock/android/filtering_configuration_android.cc create mode 100644 components/adblock/android/filtering_configuration_android.h 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/AdblockSwitches.java create mode 100644 components/adblock/android/java/src/org/chromium/components/adblock/ContentType.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/src/org/chromium/components/adblock/ResourceFilteringCounters.java create mode 100644 components/adblock/android/resource_classification_notifier_android.cc create mode 100644 components/adblock/android/resource_classification_notifier_android.h diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn @@ -1,6 +1,9 @@ # Copyright 2014 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# +# This source code is a part of eyeo Chromium SDK. +# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file. import("//build/config/android/config.gni") import("//build/config/cronet/config.gni") @@ -574,6 +577,7 @@ if (_is_default_toolchain) { "//chrome/browser/xsurface:java", "//chrome/browser/xsurface_provider:dependency_provider_impl_java", "//chrome/browser/xsurface_provider:java", + "//components/adblock/android:adblock_controller_java", "//components/android_autofill/browser:java", "//components/autofill/android:autofill_features_java", "//components/autofill/android:autofill_java", @@ -1529,6 +1533,7 @@ if (_is_default_toolchain) { "//chrome/test/data/translate/", "//chrome/test/data/webauthn/", "//chrome/test/media_router/resources/", + "//components/test/data/adblock/", "//components/test/data/autofill/", "//components/test/data/payments/", "//content/test/data/browsing_data/", @@ -3153,6 +3158,12 @@ if (_is_default_toolchain) { sources += [ "java/src/org/chromium/chrome/browser/media/MediaCapturePickerManagerBridge.java" ] } + ### Android API module start + sources += + [ "java/src/org/chromium/chrome/browser/adblock/TabIdTranslator.java" ] + + ### Android API module end + # Used for testing only, should not be shipped to end users. if (enable_offline_pages_harness) { sources += [ "java/src/org/chromium/chrome/browser/offlinepages/evaluation/OfflinePageEvaluationBridge.java" ] @@ -3244,6 +3255,7 @@ if (_is_default_toolchain) { "//chrome/browser/ui/plus_addresses/android:jni_headers", "//chrome/browser/util:jni_headers", "//chrome/browser/webauthn/android:jni_headers", + "//components/adblock/android:jni_headers", "//components/browser_ui/device_lock/android:device_lock_bridge_jni_headers", "//components/content_relationship_verification/android:jni_headers", "//components/image_fetcher:jni_headers", diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni --- a/chrome/android/chrome_java_sources.gni +++ b/chrome/android/chrome_java_sources.gni @@ -1,6 +1,9 @@ # Copyright 2019 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +# +# This source code is a part of eyeo Chromium SDK. +# Use of this source code is governed by the GPLv3 that can be found in the components/adblock/LICENSE file. import("//content/public/common/features.gni") @@ -66,6 +69,7 @@ chrome_java_sources = [ "java/src/org/chromium/chrome/browser/accessibility/settings/ChromeAccessibilitySettingsDelegate.java", "java/src/org/chromium/chrome/browser/actor/ActorForegroundServiceControllerImpl.java", "java/src/org/chromium/chrome/browser/actor/ActorForegroundServiceImpl.java", + "java/src/org/chromium/chrome/browser/adblock/TabIdTranslator.java", "java/src/org/chromium/chrome/browser/ai/AiAssistantService.java", "java/src/org/chromium/chrome/browser/ai/PageSummaryButtonController.java", "java/src/org/chromium/chrome/browser/ai/SystemAiProvider.java", diff --git a/chrome/android/java/src/org/chromium/chrome/browser/adblock/TabIdTranslator.java b/chrome/android/java/src/org/chromium/chrome/browser/adblock/TabIdTranslator.java new file mode 100644 --- /dev/null +++ b/chrome/android/java/src/org/chromium/chrome/browser/adblock/TabIdTranslator.java @@ -0,0 +1,36 @@ +/* + * 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 chromium.chrome.browser.adblock; + +import org.jni_zero.NativeMethods; + +import org.chromium.content_public.browser.GlobalRenderFrameHostId; + +/** + * @brief Helper class to translate AdblockCounters.ResourceInfo.mMainFrameId to TabId. + */ +public final class TabIdTranslator { + public static int fromRenderFrameHostId(GlobalRenderFrameHostId id) { + return TabIdTranslatorJni.get().fromRenderFrameHostId(id.childId(), id.frameRoutingId()); + } + + @NativeMethods + public interface Natives { + int fromRenderFrameHostId(int renderProcessId, int renderFrameId); + } +} diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn @@ -3041,6 +3041,10 @@ static_library("browser") { ] deps += [ "//chrome/browser/password_entry_edit/android" ] + ### Android API module start + sources += [ "android/adblock/tab_id_translator_jni.cc" ] + ### Android API module end + deps += [ ":delta_file_proto", ":language_data_proto", diff --git a/chrome/browser/android/adblock/tab_id_translator_jni.cc b/chrome/browser/android/adblock/tab_id_translator_jni.cc new file mode 100644 --- /dev/null +++ b/chrome/browser/android/adblock/tab_id_translator_jni.cc @@ -0,0 +1,43 @@ +/* + * 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/android/chrome_jni_headers/TabIdTranslator_jni.h" +#include "chrome/browser/android/tab_android.h" +#include "content/public/browser/web_contents.h" + +namespace { +constexpr int kNoTabId = -1; +} + +static jint JNI_TabIdTranslator_FromRenderFrameHostId(JNIEnv* env, + jint render_process_id, + jint render_frame_id) { + auto* web_contents = content::WebContents::FromRenderFrameHost( + content::RenderFrameHost::FromID(render_process_id, render_frame_id)); + if (!web_contents) { + return kNoTabId; + } + + auto* tab = TabAndroid::FromWebContents(web_contents); + if (!tab) { + return kNoTabId; + } + + return tab->GetAndroidId(); +} + +DEFINE_JNI(TabIdTranslator) 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,107 @@ +# 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("//third_party/jni_zero/jni_zero.gni") +import("//tools/grit/grit_rule.gni") + +source_set("java_bindings") { + sources = [ + "adblock_controller_jni.cc", + "filtering_configuration_android.cc", + "filtering_configuration_android.h", + "resource_classification_notifier_android.cc", + "resource_classification_notifier_android.h", + ] + deps = [ + ":jni_headers", + "//base", + "//components/keyed_service/content:content", + "//components/user_prefs:user_prefs", + ] + + 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/AdblockController.java", + "java/src/org/chromium/components/adblock/AdblockSwitches.java", + "java/src/org/chromium/components/adblock/ContentType.java", + "java/src/org/chromium/components/adblock/FilteringConfiguration.java", + "java/src/org/chromium/components/adblock/ResourceClassificationNotifier.java", + "java/src/org/chromium/components/adblock/ResourceFilteringCounters.java", + ] + + srcjar_deps = [ ":jni_headers" ] + deps = [ + "//base:base_java", + "//content/public/android:content_java", + "//third_party/androidx:androidx_annotation_annotation_java", + "//third_party/jni_zero:jni_zero_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/DefaultSettingsTestBase.java", + "javatests/src/org/chromium/components/adblock/FilteringConfigurationTestBase.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/TestPagesInlineCssTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesRemoveTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesRewriteTestBase.java", + "javatests/src/org/chromium/components/adblock/TestPagesServiceWorkersTestBase.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/TestPagesWildcardDomainTestBase.java", + "javatests/src/org/chromium/components/adblock/TestResourceFilteringObserver.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", + "//net/android:net_java_test_support", + "//third_party/androidx:androidx_test_monitor_java", + "//third_party/androidx:androidx_test_runner_java", + "//third_party/junit:junit", + ] +} diff --git a/components/adblock/android/adblock_controller_jni.cc b/components/adblock/android/adblock_controller_jni.cc new file mode 100644 --- /dev/null +++ b/components/adblock/android/adblock_controller_jni.cc @@ -0,0 +1,133 @@ +/* + * This file is part of eyeo Chromium SDK, + * Copyright (C) 2006-present eyeo GmbH + * + * eyeo Chromium SDK is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * eyeo Chromium SDK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eyeo Chromium SDK. If not, see . + */ + +#include +#include +#include + +#include "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/jni_headers/AdblockController_jni.h" +#include "components/adblock/content/browser/factories/subscription_service_factory.h" +#include "components/adblock/core/common/adblock_constants.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "content/public/browser/android/browser_context_handle.h" +#include "content/public/browser/browser_thread.h" + +using base::android::CheckException; +using base::android::ConvertUTF8ToJavaString; +using base::android::GetClass; +using base::android::JavaParamRef; +using base::android::MethodID; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaArrayOfObjects; +using base::android::ToJavaArrayOfStrings; + +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, + const bool autoinstalled) { + ScopedJavaLocalRef url_param = + ScopedJavaLocalRef::Adopt( + 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), + autoinstalled ? JNI_TRUE : JNI_FALSE); +} + +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{}, + sub->GetInstallationState() == + adblock::InstalledSubscription::InstallationState::AutoInstalled)); + } + 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 == adblock::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, + false)); + } + } + } + return jobjects; +} + +} // namespace + +static base::android::ScopedJavaLocalRef +JNI_AdblockController_GetInstalledSubscriptions( + JNIEnv* env, + const base::android::JavaParamRef& jbrowser_context_handle) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + auto* subscription_service = + adblock::SubscriptionServiceFactory::GetForBrowserContext( + content::BrowserContextFromJavaHandle(jbrowser_context_handle)); + if (!subscription_service) { + return ToJavaArrayOfObjects(env, + std::vector>{}); + } + + return ToJavaArrayOfObjects( + env, CSubscriptionsToJObjects( + env, subscription_service->GetCurrentSubscriptions( + subscription_service->GetFilteringConfiguration( + adblock::kAdblockFilteringConfigurationName)))); +} + +static base::android::ScopedJavaLocalRef +JNI_AdblockController_GetRecommendedSubscriptions(JNIEnv* env) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + auto list = adblock::config::GetKnownSubscriptions(); + return ToJavaArrayOfObjects(env, CSubscriptionsToJObjects(env, list)); +} diff --git a/components/adblock/android/filtering_configuration_android.cc b/components/adblock/android/filtering_configuration_android.cc new file mode 100644 --- /dev/null +++ b/components/adblock/android/filtering_configuration_android.cc @@ -0,0 +1,246 @@ +/* + * 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_android.h" + +#include +#include + +#include "base/android/jni_android.h" +#include "base/android/jni_array.h" +#include "base/android/jni_string.h" +#include "components/adblock/android/jni_headers/FilteringConfiguration_jni.h" +#include "components/adblock/content/browser/factories/subscription_service_factory.h" +#include "components/adblock/core/configuration/filtering_configuration.h" +#include "components/adblock/core/configuration/persistent_filtering_configuration.h" +#include "components/adblock/core/subscription/subscription_config.h" +#include "components/user_prefs/user_prefs.h" +#include "content/public/browser/android/browser_context_handle.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertJavaStringToUTF8; +using base::android::ConvertUTF8ToJavaString; +using base::android::JavaRef; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaArrayOfStrings; + +using namespace adblock; + +FilteringConfigurationAndroid::FilteringConfigurationAndroid( + JNIEnv* env, + const JavaRef& jcontroller, + const std::string& configuration_name, + SubscriptionService* subscription_service, + PrefService* pref_service) + : subscription_service_(subscription_service), + pref_service_(pref_service), + java_weak_controller_(env, jcontroller) { + subscription_service_->AddObserver(this); + auto* existing_configuration = + subscription_service_->GetFilteringConfiguration(configuration_name); + if (existing_configuration) { + filtering_configuration_ptr = existing_configuration; + } else { + auto new_filtering_configuration = + std::make_unique(pref_service_, + configuration_name); + filtering_configuration_ptr = new_filtering_configuration.get(); + subscription_service_->InstallFilteringConfiguration( + std::move(new_filtering_configuration)); + } + filtering_configuration_ptr->AddObserver(this); +} + +FilteringConfigurationAndroid::~FilteringConfigurationAndroid() { + filtering_configuration_ptr->RemoveObserver(this); + subscription_service_->UninstallFilteringConfiguration( + filtering_configuration_ptr->GetName()); + subscription_service_->RemoveObserver(this); +} + +void FilteringConfigurationAndroid::Destroy(JNIEnv*) { + delete this; +} + +void FilteringConfigurationAndroid::OnEnabledStateChanged( + FilteringConfiguration* config) { + Notify(config, Java_FilteringConfiguration_enabledStateChanged); +} + +void FilteringConfigurationAndroid::OnFilterListsChanged( + FilteringConfiguration* config) { + Notify(config, Java_FilteringConfiguration_filterListsChanged); +} + +void FilteringConfigurationAndroid::OnAllowedDomainsChanged( + FilteringConfiguration* config) { + Notify(config, Java_FilteringConfiguration_allowedDomainsChanged); +} + +void FilteringConfigurationAndroid::OnCustomFiltersChanged( + FilteringConfiguration* config) { + Notify(config, Java_FilteringConfiguration_customFiltersChanged); +} + +void FilteringConfigurationAndroid::Notify( + FilteringConfiguration* config, + FilteringConfigurationAndroid::JavaEventListener event_listener_function) { + JNIEnv* env = AttachCurrentThread(); + auto java_controller = java_weak_controller_.get(env); + if (!java_controller.is_null()) { + event_listener_function(env, java_controller); + } +} + +void FilteringConfigurationAndroid::OnSubscriptionInstalled(const GURL& url) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef j_url = ConvertUTF8ToJavaString(env, url.spec()); + auto java_controller = java_weak_controller_.get(env); + if (!java_controller.is_null()) { + Java_FilteringConfiguration_onSubscriptionUpdated(env, java_controller, + j_url); + } +} + +jboolean FilteringConfigurationAndroid::IsEnabled(JNIEnv* env) const { + return filtering_configuration_ptr->IsEnabled() ? JNI_TRUE : JNI_FALSE; +} + +void FilteringConfigurationAndroid::SetEnabled(JNIEnv* env, + jboolean j_enabled) { + filtering_configuration_ptr->SetEnabled(j_enabled == JNI_TRUE); +} + +void FilteringConfigurationAndroid::AddAllowedDomain( + JNIEnv* env, + const JavaRef& allowed_domain) { + filtering_configuration_ptr->AddAllowedDomain( + ConvertJavaStringToUTF8(allowed_domain)); +} + +void FilteringConfigurationAndroid::RemoveAllowedDomain( + JNIEnv* env, + const JavaRef& allowed_domain) { + filtering_configuration_ptr->RemoveAllowedDomain( + ConvertJavaStringToUTF8(allowed_domain)); +} + +ScopedJavaLocalRef +FilteringConfigurationAndroid::GetAllowedDomains(JNIEnv* env) const { + return ToJavaArrayOfStrings(env, + filtering_configuration_ptr->GetAllowedDomains()); +} + +void FilteringConfigurationAndroid::AddCustomFilter( + JNIEnv* env, + const JavaRef& custom_filter) { + filtering_configuration_ptr->AddCustomFilter( + ConvertJavaStringToUTF8(custom_filter)); +} + +void FilteringConfigurationAndroid::RemoveCustomFilter( + JNIEnv* env, + const JavaRef& custom_filter) { + filtering_configuration_ptr->RemoveCustomFilter( + ConvertJavaStringToUTF8(custom_filter)); +} + +ScopedJavaLocalRef +FilteringConfigurationAndroid::GetCustomFilters(JNIEnv* env) const { + return ToJavaArrayOfStrings(env, + filtering_configuration_ptr->GetCustomFilters()); +} + +void FilteringConfigurationAndroid::AddFilterList( + JNIEnv* env, + const JavaRef& url) { + filtering_configuration_ptr->AddFilterList( + GURL{ConvertJavaStringToUTF8(url)}); +} + +void FilteringConfigurationAndroid::RemoveFilterList( + JNIEnv* env, + const JavaRef& url) { + filtering_configuration_ptr->RemoveFilterList( + GURL{ConvertJavaStringToUTF8(url)}); +} + +ScopedJavaLocalRef FilteringConfigurationAndroid::GetFilterLists( + JNIEnv* env) const { + // 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; + std::ranges::transform(filtering_configuration_ptr->GetFilterLists(), + std::back_inserter(urls), &GURL::spec); + return ToJavaArrayOfStrings(env, urls); +} + +static ScopedJavaLocalRef +JNI_FilteringConfiguration_GetAcceptableAdsUrl(JNIEnv* env) { + return ConvertUTF8ToJavaString(env, adblock::AcceptableAdsUrl().spec()); +} + +static jlong JNI_FilteringConfiguration_Create( + JNIEnv* env, + const JavaRef& jcontroller, + const JavaRef& jstring, + const JavaRef& jbrowser_context_handle) { + DCHECK(!jcontroller.is_null()); + DCHECK(!jbrowser_context_handle.is_null()); + auto* context = + content::BrowserContextFromJavaHandle(jbrowser_context_handle); + return reinterpret_cast(new FilteringConfigurationAndroid( + env, std::move(jcontroller), ConvertJavaStringToUTF8(jstring), + adblock::SubscriptionServiceFactory::GetForBrowserContext(context), + user_prefs::UserPrefs::Get(context))); +} + +static ScopedJavaLocalRef +JNI_FilteringConfiguration_GetConfigurations( + JNIEnv* env, + const JavaRef& jbrowser_context_handle) { + std::vector configurations; + std::ranges::transform( + adblock::SubscriptionServiceFactory::GetForBrowserContext( + content::BrowserContextFromJavaHandle(jbrowser_context_handle)) + ->GetInstalledFilteringConfigurations(), + std::back_inserter(configurations), + [](adblock::FilteringConfiguration* fc) { return fc->GetName(); }); + return ToJavaArrayOfStrings(env, configurations); +} + +static jboolean JNI_FilteringConfiguration_IsAutoInstallEnabled( + JNIEnv* env, + const JavaRef& jbrowser_context_handle) { + return adblock::SubscriptionServiceFactory::GetForBrowserContext( + content::BrowserContextFromJavaHandle(jbrowser_context_handle)) + ->IsAutoInstallEnabled() + ? JNI_TRUE + : JNI_FALSE; +} + +static void JNI_FilteringConfiguration_SetAutoInstallEnabled( + JNIEnv* env, + jboolean j_enabled, + const JavaRef& jbrowser_context_handle) { + adblock::SubscriptionServiceFactory::GetForBrowserContext( + content::BrowserContextFromJavaHandle(jbrowser_context_handle)) + ->SetAutoInstallEnabled(j_enabled == JNI_TRUE); +} + +DEFINE_JNI(FilteringConfiguration) diff --git a/components/adblock/android/filtering_configuration_android.h b/components/adblock/android/filtering_configuration_android.h new file mode 100644 --- /dev/null +++ b/components/adblock/android/filtering_configuration_android.h @@ -0,0 +1,94 @@ +/* + * 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_ANDROID_H_ +#define COMPONENTS_ADBLOCK_ANDROID_FILTERING_CONFIGURATION_ANDROID_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" + +class FilteringConfigurationAndroid + : public adblock::FilteringConfiguration::Observer, + public adblock::SubscriptionService::SubscriptionObserver { + public: + explicit FilteringConfigurationAndroid( + JNIEnv* env, + const base::android::JavaRef& jcontroller, + const std::string& configuration_name, + adblock::SubscriptionService* subscription_service, + PrefService* pref_service); + ~FilteringConfigurationAndroid() override; + // Called by Java to destroy this instance. + void Destroy(JNIEnv*); + void SetEnabled(JNIEnv* env, jboolean j_enabled); + jboolean IsEnabled(JNIEnv* env) const; + void AddFilterList(JNIEnv* env, + const base::android::JavaRef& url); + void RemoveFilterList(JNIEnv* env, + const base::android::JavaRef& url); + base::android::ScopedJavaLocalRef GetFilterLists( + JNIEnv* env) const; + void AddAllowedDomain(JNIEnv* env, + const base::android::JavaRef& domain); + void RemoveAllowedDomain(JNIEnv* env, + const base::android::JavaRef& domain); + base::android::ScopedJavaLocalRef GetAllowedDomains( + JNIEnv* env) const; + void AddCustomFilter(JNIEnv* env, + const base::android::JavaRef& filter); + void RemoveCustomFilter(JNIEnv* env, + const base::android::JavaRef& filter); + base::android::ScopedJavaLocalRef GetCustomFilters( + JNIEnv* env) const; + + // FilteringConfiguration::Observer: + void OnEnabledStateChanged(adblock::FilteringConfiguration* config) override; + void OnFilterListsChanged(adblock::FilteringConfiguration* config) override; + void OnAllowedDomainsChanged( + adblock::FilteringConfiguration* config) override; + void OnCustomFiltersChanged(adblock::FilteringConfiguration* config) override; + + // SubcriptionService::Observer: + void OnSubscriptionInstalled(const GURL& subscription_url) override; + + private: + using JavaEventListener = void(JNIEnv* env, + const base::android::JavaRef& obj); + void Notify(adblock::FilteringConfiguration* config, + JavaEventListener event_listener_function); + SEQUENCE_CHECKER(sequence_checker_); + raw_ptr subscription_service_; + raw_ptr pref_service_; + raw_ptr filtering_configuration_ptr; + + // Direct reference to FilteringConfiguration java class. Kept for as + // long as this instance of FilteringConfigurationAndroid lives: + // until corresponding Profile gets destroyed. Destruction of Profile triggers + // destruction of both C++ FilteringConfigurationAndroid and Java + // FilteringConfiguration objects. + const JavaObjectWeakGlobalRef java_weak_controller_; +}; + +#endif // COMPONENTS_ADBLOCK_ANDROID_FILTERING_CONFIGURATION_ANDROID_H_ 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,283 @@ +/* + * 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.annotation.UiThread; + +import org.jni_zero.CalledByNative; +import org.jni_zero.NativeMethods; + +import org.chromium.base.ResettersForTesting; +import org.chromium.base.ThreadUtils; +import org.chromium.base.library_loader.LibraryLoader; +import org.chromium.content_public.browser.BrowserContextHandle; + +import java.net.URL; +import java.util.Arrays; +import java.util.List; + +/** + * DEPRECATED, please use {@link FilteringConfiguration} instead. + * + * @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. + */ +@Deprecated +public class AdblockController { + private static final String TAG = AdblockController.class.getSimpleName(); + private FilteringConfiguration mFilteringConfiguration; + private BrowserContextHandle mBrowserContextHandle; + + private URL mAcceptableAds; + + private static AdblockController sInstance; + + private AdblockController(BrowserContextHandle contextHandle) { + mBrowserContextHandle = contextHandle; + mFilteringConfiguration = + FilteringConfiguration.createConfiguration("adblock", mBrowserContextHandle); + 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(BrowserContextHandle contextHandle) { + // Make sure native libraries are ready to avoid org.chromium.base.JniException + LibraryLoader.getInstance().ensureInitialized(); + ThreadUtils.assertOnUiThread(); + if (sInstance == null) { + sInstance = new AdblockController(contextHandle); + } + return sInstance; + } + + public static void setInstanceForTesting(final AdblockController adblockController) { + var oldValue = sInstance; + sInstance = adblockController; + ResettersForTesting.register(() -> sInstance = oldValue); + } + + public static class Subscription { + private URL mUrl; + private String mTitle; + private String mVersion = ""; + private String[] mLanguages = {}; + private boolean mAutoInstalled; + + 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, + boolean autoinstalled) { + this.mUrl = url; + this.mTitle = title; + this.mVersion = version; + this.mLanguages = languages; + this.mAutoInstalled = autoinstalled; + } + + public String title() { + return mTitle; + } + + public URL url() { + return mUrl; + } + + public String version() { + return mVersion; + } + + public String[] languages() { + return mLanguages; + } + + public boolean autoinstalled() { + return mAutoInstalled; + } + + @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) mFilteringConfiguration.addFilterList(mAcceptableAds); + else mFilteringConfiguration.removeFilterList(mAcceptableAds); + } + + @UiThread + public boolean isAcceptableAdsEnabled() { + return mFilteringConfiguration.getFilterLists().contains(mAcceptableAds); + } + + @UiThread + public List getRecommendedSubscriptions() { + return (List) + (List) Arrays.asList(AdblockControllerJni.get().getRecommendedSubscriptions()); + } + + @UiThread + public void setAutoInstallEnabled(boolean enable) { + FilteringConfiguration.setAutoInstallEnabled(enable, mBrowserContextHandle); + } + + @UiThread + public boolean isAutoInstallEnabled() { + return FilteringConfiguration.isAutoInstallEnabled(mBrowserContextHandle); + } + + @UiThread + public void installSubscription(final URL url) { + mFilteringConfiguration.addFilterList(url); + } + + @UiThread + public void uninstallSubscription(final URL url) { + mFilteringConfiguration.removeFilterList(url); + } + + @UiThread + public List getInstalledSubscriptions() { + return (List) + (List) + Arrays.asList( + AdblockControllerJni.get() + .getInstalledSubscriptions(mBrowserContextHandle)); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void setEnabled(boolean enabled) throws IllegalStateException { + mFilteringConfiguration.setEnabled(enabled); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public boolean isEnabled() throws IllegalStateException { + return mFilteringConfiguration.isEnabled(); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void removeFilterList(final URL url) throws IllegalStateException { + mFilteringConfiguration.removeFilterList(url); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getFilterLists() throws IllegalStateException { + return mFilteringConfiguration.getFilterLists(); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void addAllowedDomain(final String domain) throws IllegalStateException { + mFilteringConfiguration.addAllowedDomain(domain); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void removeAllowedDomain(final String domain) throws IllegalStateException { + mFilteringConfiguration.removeAllowedDomain(domain); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getAllowedDomains() throws IllegalStateException { + return mFilteringConfiguration.getAllowedDomains(); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void addCustomFilter(final String filter) throws IllegalStateException { + mFilteringConfiguration.addCustomFilter(filter); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void removeCustomFilter(final String filter) throws IllegalStateException { + mFilteringConfiguration.removeCustomFilter(filter); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getCustomFilters() throws IllegalStateException { + return mFilteringConfiguration.getCustomFilters(); + } + + public interface SubscriptionUpdateObserver + extends FilteringConfiguration.SubscriptionUpdateObserver {} + + @UiThread + public void addSubscriptionUpdateObserver(final SubscriptionUpdateObserver observer) { + mFilteringConfiguration.addSubscriptionUpdateObserver(observer); + } + + @UiThread + public void removeSubscriptionUpdateObserver(final SubscriptionUpdateObserver observer) { + mFilteringConfiguration.removeSubscriptionUpdateObserver(observer); + } + + @NativeMethods + interface Natives { + Object[] getInstalledSubscriptions(BrowserContextHandle contextHandle); + + Object[] getRecommendedSubscriptions(); + } +} diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/AdblockSwitches.java b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockSwitches.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/java/src/org/chromium/components/adblock/AdblockSwitches.java @@ -0,0 +1,22 @@ +/* + * 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; + +public class AdblockSwitches { + public static final String DISABLE_EYEO_REQUEST_THROTTLING = "disable-eyeo-request-throttling"; +} diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/ContentType.java b/components/adblock/android/java/src/org/chromium/components/adblock/ContentType.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/java/src/org/chromium/components/adblock/ContentType.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 ContentType { + // 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 ContentType(int contentType) { + this.contentType = contentType; + } + + private static final Map intToContentTypeMap = + new HashMap(); + + static { + for (ContentType type : ContentType.values()) { + intToContentTypeMap.put(type.contentType, type); + } + } + + public static ContentType fromInt(int i) { + return intToContentTypeMap.get(i); + } + + public int getValue() { + return contentType; + } +} 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,424 @@ +/* + * 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.util.Log; +import android.webkit.URLUtil; + +import androidx.annotation.UiThread; + +import org.jni_zero.CalledByNative; +import org.jni_zero.NativeMethods; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.library_loader.LibraryLoader; +import org.chromium.content_public.browser.BrowserContextHandle; + +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 final class FilteringConfiguration { + private static final String TAG = FilteringConfiguration.class.getSimpleName(); + private static final Set mConfigurations = new HashSet<>(); + + private final Set mConfigurationChangeObservers = new HashSet<>(); + private final Set mSubscriptionUpdateObservers = new HashSet<>(); + private String mName; + private long mNativeController; + + private FilteringConfiguration( + final String configuration_name, BrowserContextHandle browserContextHandle) { + // Make sure native libraries are ready to avoid org.chromium.base.JniException + LibraryLoader.getInstance().ensureInitialized(); + + mName = configuration_name; + mNativeController = + FilteringConfigurationJni.get() + .create(this, configuration_name, browserContextHandle); + } + + private void DestroyNative() { + FilteringConfigurationJni.get().destroy(mNativeController); + mNativeController = 0; + } + + private void ValidateConfiguration() throws IllegalStateException { + if (mNativeController == 0) { + throw new IllegalStateException("Configuration does not exist!"); + } + } + + 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 { + ValidateConfiguration(); + FilteringConfigurationJni.get().setEnabled(mNativeController, enabled); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public boolean isEnabled() throws IllegalStateException { + ValidateConfiguration(); + return FilteringConfigurationJni.get().isEnabled(mNativeController); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void addFilterList(final URL url) throws IllegalStateException { + ValidateConfiguration(); + FilteringConfigurationJni.get().addFilterList(mNativeController, url.toString()); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void removeFilterList(final URL url) throws IllegalStateException { + ValidateConfiguration(); + FilteringConfigurationJni.get().removeFilterList(mNativeController, url.toString()); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getFilterLists() throws IllegalStateException { + ValidateConfiguration(); + List filterListsStr = + Arrays.asList(FilteringConfigurationJni.get().getFilterLists(mNativeController)); + 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; + } + + @UiThread + public String getAcceptableAdsUrl() { + ValidateConfiguration(); + return FilteringConfigurationJni.get().getAcceptableAdsUrl(); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void addAllowedDomain(final String domain) throws IllegalStateException { + ValidateConfiguration(); + String sanitizedDomain = sanitizeSite(domain); + if (sanitizedDomain == null) return; + FilteringConfigurationJni.get().addAllowedDomain(mNativeController, sanitizedDomain); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void removeAllowedDomain(final String domain) throws IllegalStateException { + ValidateConfiguration(); + String sanitizedDomain = sanitizeSite(domain); + if (sanitizedDomain == null) return; + FilteringConfigurationJni.get().removeAllowedDomain(mNativeController, sanitizedDomain); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getAllowedDomains() throws IllegalStateException { + ValidateConfiguration(); + List allowedDomains = + Arrays.asList(FilteringConfigurationJni.get().getAllowedDomains(mNativeController)); + Collections.sort(allowedDomains); + return allowedDomains; + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void addCustomFilter(final String filter) throws IllegalStateException { + ValidateConfiguration(); + FilteringConfigurationJni.get().addCustomFilter(mNativeController, filter); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public void removeCustomFilter(final String filter) throws IllegalStateException { + ValidateConfiguration(); + FilteringConfigurationJni.get().removeCustomFilter(mNativeController, filter); + } + + /** + * @throws IllegalStateException when called on removed FilteringConfiguration. + */ + @UiThread + public List getCustomFilters() throws IllegalStateException { + ValidateConfiguration(); + return Arrays.asList(FilteringConfigurationJni.get().getCustomFilters(mNativeController)); + } + + @UiThread + public static FilteringConfiguration createConfiguration( + final String configuration_name, BrowserContextHandle browserContextHandle) { + FilteringConfiguration configuration = findConfigurationByName(configuration_name); + if (configuration == null) { + configuration = new FilteringConfiguration(configuration_name, browserContextHandle); + mConfigurations.add(configuration); + } + return configuration; + } + + @UiThread + public static void removeConfiguration( + final String configuration_name, BrowserContextHandle browserContextHandle) { + // sync with native filtering configurations + getConfigurations(browserContextHandle); + final FilteringConfiguration configuration = findConfigurationByName(configuration_name); + if (configuration != null) { + configuration.DestroyNative(); + mConfigurations.remove(configuration); + } + } + + @UiThread + public static List getConfigurations( + BrowserContextHandle browserContextHandle) { + // 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(browserContextHandle); + 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, browserContextHandle); + mConfigurations.add(configuration); + } + configurations_to_return.add((FilteringConfiguration) 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; + } + + @UiThread + private static FilteringConfiguration findConfigurationByName(final String configuration_name) { + for (final FilteringConfiguration configuration : mConfigurations) { + if (configuration.mName.equals(configuration_name)) { + return configuration; + } + } + return null; + } + + @UiThread + public static void setAutoInstallEnabled( + boolean enable, BrowserContextHandle browserContextHandle) { + FilteringConfigurationJni.get().setAutoInstallEnabled(enable, browserContextHandle); + } + + @UiThread + public static boolean isAutoInstallEnabled(BrowserContextHandle browserContextHandle) { + return FilteringConfigurationJni.get().isAutoInstallEnabled(browserContextHandle); + } + + 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(); + } + } + + @CalledByNative + private void onSubscriptionUpdated(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 + public interface Natives { + void destroy(long nativeFilteringConfigurationAndroid); + + void setEnabled(long nativeFilteringConfigurationAndroid, boolean enabled); + + boolean isEnabled(long nativeFilteringConfigurationAndroid); + + void addAllowedDomain(long nativeFilteringConfigurationAndroid, String domain); + + void removeAllowedDomain(long nativeFilteringConfigurationAndroid, String domain); + + String[] getAllowedDomains(long nativeFilteringConfigurationAndroid); + + void addCustomFilter(long nativeFilteringConfigurationAndroid, String filter); + + void removeCustomFilter(long nativeFilteringConfigurationAndroid, String filter); + + String[] getCustomFilters(long nativeFilteringConfigurationAndroid); + + void addFilterList(long nativeFilteringConfigurationAndroid, String url); + + void removeFilterList(long nativeFilteringConfigurationAndroid, String url); + + String[] getFilterLists(long nativeFilteringConfigurationAndroid); + + long create( + FilteringConfiguration controller, + final String configuration_name, + BrowserContextHandle contextHandle); + + void setAutoInstallEnabled(boolean enable, BrowserContextHandle contextHandle); + + boolean isAutoInstallEnabled(BrowserContextHandle contextHandle); + + String[] getConfigurations(BrowserContextHandle contextHandle); + + String getAcceptableAdsUrl(); + } +} 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,233 @@ +/* + * 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.util.Log; + +import androidx.annotation.UiThread; + +import org.jni_zero.CalledByNative; +import org.jni_zero.NativeMethods; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.library_loader.LibraryLoader; +import org.chromium.content_public.browser.BrowserContextHandle; +import org.chromium.content_public.browser.GlobalRenderFrameHostId; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class ResourceClassificationNotifier { + private static final String TAG = ResourceClassificationNotifier.class.getSimpleName(); + + private long mNativeController; + private static ResourceClassificationNotifier sInstance; + + private final Set mResourceFilteringObservers = new HashSet<>(); + + // TODO(mpawlowski) in the future, we can consider adding filter hit + // notifications here as well (DPD-1233) + + public interface ResourceFilteringObserver { + /** + * "Request 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 onRequestAllowed(ResourceFilteringCounters.ResourceInfo info); + + /** + * "Request 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 onRequestBlocked(ResourceFilteringCounters.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(ResourceFilteringCounters.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(ResourceFilteringCounters.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(ResourceFilteringCounters.ResourceInfo info); + } + + private ResourceClassificationNotifier(BrowserContextHandle contextHandle) { + mNativeController = ResourceClassificationNotifierJni.get().create(this, contextHandle); + assert mNativeController != 0 + : "Failed to instantiate native ResourceClassificationNotifier"; + } + + public static ResourceClassificationNotifier getInstance(BrowserContextHandle contextHandle) { + // Make sure native libraries are ready to avoid org.chromium.base.JniException + LibraryLoader.getInstance().ensureInitialized(); + ThreadUtils.assertOnUiThread(); + if (sInstance == null) { + sInstance = new ResourceClassificationNotifier(contextHandle); + } + return sInstance; + } + + @UiThread + public void addResourceFilteringObserver(final ResourceFilteringObserver observer) { + mResourceFilteringObservers.add(observer); + } + + @UiThread + public void removeResourceFilteringObserver(final ResourceFilteringObserver observer) { + mResourceFilteringObservers.remove(observer); + } + + @CalledByNative + private void requestMatchedCallback( + final String requestUrl, + boolean wasBlocked, + final String[] parentFrameUrls, + final String subscriptionUrl, + final String configurationName, + final int contentType, + final int renderProcessId, + final int renderFrameId) { + ThreadUtils.assertOnUiThread(); + final List parentsList = Arrays.asList(parentFrameUrls); + final ResourceFilteringCounters.ResourceInfo resourceInfo = + new ResourceFilteringCounters.ResourceInfo( + requestUrl, + parentsList, + subscriptionUrl, + configurationName, + contentType, + new GlobalRenderFrameHostId(renderProcessId, renderFrameId)); + Log.d( + TAG, + "eyeo: requestMatchedCallback() notifies " + + mResourceFilteringObservers.size() + + " listeners about " + + resourceInfo.toString() + + (wasBlocked ? " getting blocked" : " being allowed")); + for (final ResourceFilteringObserver observer : mResourceFilteringObservers) { + if (wasBlocked) { + observer.onRequestBlocked(resourceInfo); + } else { + observer.onRequestAllowed(resourceInfo); + } + } + } + + @CalledByNative + private void pageAllowedCallback( + final String requestUrl, + final String subscriptionUrl, + final String configurationName, + final int renderProcessId, + final int renderFrameId) { + ThreadUtils.assertOnUiThread(); + final ResourceFilteringCounters.ResourceInfo resourceInfo = + new ResourceFilteringCounters.ResourceInfo( + requestUrl, + new ArrayList(), + subscriptionUrl, + configurationName, + ContentType.CONTENT_TYPE_OTHER.getValue(), + new GlobalRenderFrameHostId(renderProcessId, renderFrameId)); + Log.d( + TAG, + "eyeo: pageAllowedCallback() notifies " + + mResourceFilteringObservers.size() + + " listeners about " + + resourceInfo.toString()); + for (final ResourceFilteringObserver observer : mResourceFilteringObservers) { + observer.onPageAllowed(resourceInfo); + } + } + + @CalledByNative + private void popupMatchedCallback( + final String requestUrl, + boolean wasBlocked, + final String openerUrl, + final String subscription, + final String configurationName, + final int renderProcessId, + final int renderFrameId) { + ThreadUtils.assertOnUiThread(); + final List parentsList = Arrays.asList(openerUrl); + final ResourceFilteringCounters.ResourceInfo resourceInfo = + new ResourceFilteringCounters.ResourceInfo( + requestUrl, + parentsList, + subscription, + configurationName, + ContentType.CONTENT_TYPE_OTHER.getValue(), + new GlobalRenderFrameHostId(renderProcessId, renderFrameId)); + Log.d( + TAG, + "eyeo: popupMatchedCallback() notifies " + + mResourceFilteringObservers.size() + + " listeners about " + + resourceInfo.toString() + + (wasBlocked ? " getting blocked" : " being allowed")); + for (final ResourceFilteringObserver observer : mResourceFilteringObservers) { + if (wasBlocked) { + observer.onPopupBlocked(resourceInfo); + } else { + observer.onPopupAllowed(resourceInfo); + } + } + } + + @NativeMethods + public interface Natives { + // Create an instance of ResourceClassificationNotifier associated with the supplied + // profile. + long create(ResourceClassificationNotifier controller, BrowserContextHandle contextHandle); + } +} diff --git a/components/adblock/android/java/src/org/chromium/components/adblock/ResourceFilteringCounters.java b/components/adblock/android/java/src/org/chromium/components/adblock/ResourceFilteringCounters.java new file mode 100644 --- /dev/null +++ b/components/adblock/android/java/src/org/chromium/components/adblock/ResourceFilteringCounters.java @@ -0,0 +1,111 @@ +/* + * 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.chromium.content_public.browser.GlobalRenderFrameHostId; + +import java.util.List; + +public class ResourceFilteringCounters { + /** Immutable data-class containing an auxiliary information about resource event. */ + public static class ResourceInfo { + // TODO: Make members private + final String mRequestUrl; + final List mParentFrameUrls; + final String mSubscriptionUrl; + final String mConfigurationName; + final ContentType mContentType; + final GlobalRenderFrameHostId mMainFrameId; + + ResourceInfo( + final String requestUrl, + final List parentFrameUrls, + final String subscriptionUrl, + final String configurationName, + final int contentType, + final GlobalRenderFrameHostId mainFrameId) { + this.mRequestUrl = requestUrl; + this.mParentFrameUrls = parentFrameUrls; + this.mSubscriptionUrl = subscriptionUrl; + this.mConfigurationName = configurationName; + this.mContentType = ContentType.fromInt(contentType); + this.mMainFrameId = mainFrameId; + } + + /** + * @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 resource content type. See enum ContentType in components/adblock/types.h + */ + public ContentType getContentType() { + return mContentType; + } + + /** + * @return The current tab id for the mRequestUrl. -1 means no tab id, likely tab closed + * before event arrived. Numbers start from 0. + */ + public GlobalRenderFrameHostId getMainFrameId() { + return mMainFrameId; + } + + @Override + public String toString() { + return "mRequestUrl: " + + mRequestUrl + + ", mParentFrameUrls: " + + mParentFrameUrls.toString() + + ", mSubscriptionUrl:" + + mSubscriptionUrl + + ", mConfigurationName:" + + mConfigurationName + + ", mContentType: " + + mContentType.getValue() + + ", mMainFrameId: " + + mMainFrameId.frameRoutingId(); + } + } +} diff --git a/components/adblock/android/resource_classification_notifier_android.cc b/components/adblock/android/resource_classification_notifier_android.cc new file mode 100644 --- /dev/null +++ b/components/adblock/android/resource_classification_notifier_android.cc @@ -0,0 +1,166 @@ +/* + * 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_android.h" + +#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 "components/adblock/content/browser/factories/resource_classification_runner_factory.h" +#include "components/adblock/core/common/adblock_utils.h" +#include "content/public/browser/android/browser_context_handle.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/web_contents.h" + +#include "components/adblock/android/jni_headers/ResourceClassificationNotifier_jni.h" + +using base::android::AttachCurrentThread; +using base::android::ConvertUTF8ToJavaString; +using base::android::JavaRef; +using base::android::ScopedJavaLocalRef; +using base::android::ToJavaArrayOfStrings; + +namespace adblock { + +ResourceClassificationNotifierAndroid::ResourceClassificationNotifierAndroid( + JNIEnv* env, + const JavaRef& jcontroller, + ResourceClassificationRunner* classification_runner) + : classification_runner_(classification_runner), + java_weak_controller_(env, jcontroller) { + if (classification_runner_) { + classification_runner_->AddObserver(this); + } +} + +ResourceClassificationNotifierAndroid:: + ~ResourceClassificationNotifierAndroid() { + if (classification_runner_) { + classification_runner_->RemoveObserver(this); + } +} + +void ResourceClassificationNotifierAndroid::OnRequestMatched( + 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(); + auto java_controller = java_weak_controller_.get(env); + if (!java_controller.is_null()) { + 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); + const content::GlobalRenderFrameHostId rfh_id = + render_frame_host->GetGlobalId(); + Java_ResourceClassificationNotifier_requestMatchedCallback( + env, java_controller, j_url, was_blocked, j_parents, j_subscription, + j_configuration, static_cast(content_type), rfh_id.child_id.GetUnsafeValue(), + rfh_id.frame_routing_id); + } +} + +void ResourceClassificationNotifierAndroid::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(); + auto java_controller = java_weak_controller_.get(env); + if (!java_controller.is_null()) { + ScopedJavaLocalRef j_url = + ConvertUTF8ToJavaString(env, url.spec()); + ScopedJavaLocalRef j_subscription = + ConvertUTF8ToJavaString(env, subscription.spec()); + ScopedJavaLocalRef j_configuration = + ConvertUTF8ToJavaString(env, configuration_name); + const content::GlobalRenderFrameHostId rfh_id = + render_frame_host->GetGlobalId(); + Java_ResourceClassificationNotifier_pageAllowedCallback( + env, java_controller, j_url, j_subscription, j_configuration, + rfh_id.child_id.GetUnsafeValue(), rfh_id.frame_routing_id); + } +} + +void ResourceClassificationNotifierAndroid::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(); + auto java_controller = java_weak_controller_.get(env); + if (!java_controller.is_null()) { + 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); + const content::GlobalRenderFrameHostId rfh_id = + render_frame_host->GetGlobalId(); + Java_ResourceClassificationNotifier_popupMatchedCallback( + env, java_controller, j_url, was_blocked, j_opener, j_subscription, + j_configuration, rfh_id.child_id.GetUnsafeValue(), rfh_id.frame_routing_id); + } +} + +} // namespace adblock + +static jlong JNI_ResourceClassificationNotifier_Create( + JNIEnv* env, + const JavaRef& jcontroller, + const base::android::JavaRef& jbrowser_context_handle) { + DCHECK(!jcontroller.is_null()); + DCHECK(!jbrowser_context_handle.is_null()); + return reinterpret_cast( + new adblock::ResourceClassificationNotifierAndroid( + env, std::move(jcontroller), + adblock::ResourceClassificationRunnerFactory::GetForBrowserContext( + content::BrowserContextFromJavaHandle(jbrowser_context_handle)))); +} + +DEFINE_JNI(ResourceClassificationNotifier) diff --git a/components/adblock/android/resource_classification_notifier_android.h b/components/adblock/android/resource_classification_notifier_android.h new file mode 100644 --- /dev/null +++ b/components/adblock/android/resource_classification_notifier_android.h @@ -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 . + */ + +#ifndef COMPONENTS_ADBLOCK_ANDROID_RESOURCE_CLASSIFICATION_NOTIFIER_ANDROID_H_ +#define COMPONENTS_ADBLOCK_ANDROID_RESOURCE_CLASSIFICATION_NOTIFIER_ANDROID_H_ + +#include "base/android/jni_android.h" +#include "base/android/jni_weak_ref.h" +#include "components/adblock/content/browser/resource_classification_runner.h" +#include "content/public/browser/browser_context.h" + +class FilterMatchResult; + +namespace adblock { + +class ResourceClassificationNotifierAndroid + : public ResourceClassificationRunner::Observer { + public: + ResourceClassificationNotifierAndroid( + JNIEnv* env, + const base::android::JavaRef& jcontroller, + ResourceClassificationRunner* classification_runner); + ~ResourceClassificationNotifierAndroid() override; + + // ResourceClassificationRunner::Observer + void OnRequestMatched(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_; + + // Direct reference to ResourceClassificationNotifier java class. Kept for as + // long as this instance of ResourceClassificationNotifierAndroid lives: + // until corresponding Profile gets destroyed. Destruction of Profile triggers + // destruction of both C++ ResourceClassificationNotifierAndroid and Java + // ResourceClassificationNotifier objects. + const JavaObjectWeakGlobalRef java_weak_controller_; +}; + +} // namespace adblock + +#endif // COMPONENTS_ADBLOCK_ANDROID_RESOURCE_CLASSIFICATION_NOTIFIER_ANDROID_H_ --