diff --git a/android/brave_java_resources.gni b/android/brave_java_resources.gni index d06c89cd121e..596f23643138 100644 --- a/android/brave_java_resources.gni +++ b/android/brave_java_resources.gni @@ -55,6 +55,8 @@ brave_java_resources = [ "java/res/drawable-hdpi/ic_ad_free_videos.png", "java/res/drawable-hdpi/ic_brave_onboarding.png", "java/res/drawable-hdpi/ic_chrome.png", + "java/res/drawable-hdpi/ic_cookie.png", + "java/res/drawable-hdpi/ic_cookie_background.png", "java/res/drawable-hdpi/ic_cross_modal.png", "java/res/drawable-hdpi/ic_delete_white_24dp.png", "java/res/drawable-hdpi/ic_expand_less_black_24dp.png", @@ -138,6 +140,8 @@ brave_java_resources = [ "java/res/drawable-mdpi/ic_ad_free_videos.png", "java/res/drawable-mdpi/ic_brave_onboarding.png", "java/res/drawable-mdpi/ic_chrome.png", + "java/res/drawable-mdpi/ic_cookie.png", + "java/res/drawable-mdpi/ic_cookie_background.png", "java/res/drawable-mdpi/ic_cross_modal.png", "java/res/drawable-mdpi/ic_delete_white_24dp.png", "java/res/drawable-mdpi/ic_expand_less_black_24dp.png", @@ -179,6 +183,11 @@ brave_java_resources = [ "java/res/drawable-mdpi/verified_creator_large.png", "java/res/drawable-mdpi/verified_disclosure.png", "java/res/drawable-mdpi/yandex.png", + "java/res/drawable-night-hdpi/ic_cookie_background.png", + "java/res/drawable-night-mdpi/ic_cookie_background.png", + "java/res/drawable-night-xhdpi/ic_cookie_background.png", + "java/res/drawable-night-xxhdpi/ic_cookie_background.png", + "java/res/drawable-night-xxxhdpi/ic_cookie_background.png", "java/res/drawable-nodpi/dylan_malval_sea_min.webp", "java/res/drawable-nodpi/eth.png", "java/res/drawable-nodpi/fee_icon.png", @@ -226,6 +235,8 @@ brave_java_resources = [ "java/res/drawable-xhdpi/ic_ad_free_videos.png", "java/res/drawable-xhdpi/ic_brave_onboarding.png", "java/res/drawable-xhdpi/ic_chrome.png", + "java/res/drawable-xhdpi/ic_cookie.png", + "java/res/drawable-xhdpi/ic_cookie_background.png", "java/res/drawable-xhdpi/ic_cross_modal.png", "java/res/drawable-xhdpi/ic_delete_white_24dp.png", "java/res/drawable-xhdpi/ic_expand_less_black_24dp.png", @@ -308,6 +319,8 @@ brave_java_resources = [ "java/res/drawable-xxhdpi/ic_ad_free_videos.png", "java/res/drawable-xxhdpi/ic_brave_onboarding.png", "java/res/drawable-xxhdpi/ic_chrome.png", + "java/res/drawable-xxhdpi/ic_cookie.png", + "java/res/drawable-xxhdpi/ic_cookie_background.png", "java/res/drawable-xxhdpi/ic_cross_modal.png", "java/res/drawable-xxhdpi/ic_delete_white_24dp.png", "java/res/drawable-xxhdpi/ic_expand_less_black_24dp.png", @@ -389,6 +402,8 @@ brave_java_resources = [ "java/res/drawable-xxxhdpi/ic_ad_free_videos.png", "java/res/drawable-xxxhdpi/ic_brave_onboarding.png", "java/res/drawable-xxxhdpi/ic_chrome.png", + "java/res/drawable-xxxhdpi/ic_cookie.png", + "java/res/drawable-xxxhdpi/ic_cookie_background.png", "java/res/drawable-xxxhdpi/ic_cross_modal.png", "java/res/drawable-xxxhdpi/ic_delete_white_24dp.png", "java/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png", @@ -482,6 +497,7 @@ brave_java_resources = [ "java/res/drawable/brave_sync_warning.xml", "java/res/drawable/card_bg.xml", "java/res/drawable/circular_progress.xml", + "java/res/drawable/cookie_consent_tooltip_background.xml", "java/res/drawable/crypto_wallet_blue_button.xml", "java/res/drawable/crypto_wallet_hollow_button.xml", "java/res/drawable/default_dot.xml", @@ -712,6 +728,7 @@ brave_java_resources = [ "java/res/layout/activity_welcome_onboarding.xml", "java/res/layout/application_item_layout.xml", "java/res/layout/approve_tx_bottom_sheet.xml", + "java/res/layout/block_cookie_consent_notices_tooltip.xml", "java/res/layout/bottom_toolbar.xml", "java/res/layout/bottom_toolbar_browsing.xml", "java/res/layout/bottom_toolbar_menu_button.xml", diff --git a/android/brave_java_sources.gni b/android/brave_java_sources.gni index 6f8ed2623935..92afd6152784 100644 --- a/android/brave_java_sources.gni +++ b/android/brave_java_sources.gni @@ -303,6 +303,7 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/shields/BraveShieldsHandler.java", "../../brave/android/java/org/chromium/chrome/browser/shields/BraveShieldsMenuObserver.java", "../../brave/android/java/org/chromium/chrome/browser/shields/BraveShieldsUtils.java", + "../../brave/android/java/org/chromium/chrome/browser/shields/CookieListOptInServiceFactory.java", "../../brave/android/java/org/chromium/chrome/browser/signin/BraveSigninManager.java", "../../brave/android/java/org/chromium/chrome/browser/site_settings/BraveSiteSettingsDelegate.java", "../../brave/android/java/org/chromium/chrome/browser/site_settings/BraveWalletEthereumConnectedSites.java", diff --git a/android/java/org/chromium/chrome/browser/preferences/BravePreferenceKeys.java b/android/java/org/chromium/chrome/browser/preferences/BravePreferenceKeys.java index f1d1bbf9cd73..fe91750d657b 100644 --- a/android/java/org/chromium/chrome/browser/preferences/BravePreferenceKeys.java +++ b/android/java/org/chromium/chrome/browser/preferences/BravePreferenceKeys.java @@ -44,4 +44,7 @@ public final class BravePreferenceKeys { "org.chromium.chrome.browser.Brave_Biometrics_For_Wallet_Encrypted"; public static final String BRAVE_AD_FREE_CALLOUT_DIALOG = "brave_ad_free_callout_dialog"; public static final String BRAVE_OPENED_YOUTUBE = "brave_opened_youtube"; + public static final String SHOULD_SHOW_COOKIE_CONSENT_NOTICE = + "should_show_cookie_consent_notice"; + public static final String LOADED_SITE_COUNT = "loaded_site_count"; } diff --git a/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java b/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java index 61700e731bdf..7897b4644609 100644 --- a/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java +++ b/android/java/org/chromium/chrome/browser/privacy/settings/BravePrivacySettings.java @@ -15,12 +15,14 @@ import org.chromium.base.ContextUtils; import org.chromium.base.Log; +import org.chromium.brave_shields.mojom.CookieListOptInPageAndroidHandler; import org.chromium.chrome.R; import org.chromium.chrome.browser.BraveConfig; import org.chromium.chrome.browser.metrics.UmaSessionStats; import org.chromium.chrome.browser.preferences.BravePref; import org.chromium.chrome.browser.preferences.BravePrefServiceBridge; import org.chromium.chrome.browser.preferences.Pref; +import org.chromium.chrome.browser.preferences.SharedPreferencesManager; import org.chromium.chrome.browser.preferences.website.BraveShieldsContentSettings; import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManagerImpl; import org.chromium.chrome.browser.privacy.settings.PrivacySettings; @@ -29,14 +31,17 @@ import org.chromium.chrome.browser.settings.BravePreferenceDialogFragment; import org.chromium.chrome.browser.settings.BraveWebrtcPolicyPreference; import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate; +import org.chromium.chrome.browser.shields.CookieListOptInServiceFactory; import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference; import org.chromium.components.browser_ui.settings.ChromeBasePreference; import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; import org.chromium.components.browser_ui.settings.SettingsUtils; import org.chromium.components.prefs.PrefService; import org.chromium.components.user_prefs.UserPrefs; +import org.chromium.mojo.bindings.ConnectionErrorHandler; +import org.chromium.mojo.system.MojoException; -public class BravePrivacySettings extends PrivacySettings { +public class BravePrivacySettings extends PrivacySettings implements ConnectionErrorHandler { // Chromium Prefs private static final String PREF_CAN_MAKE_PAYMENT = "can_make_payment"; private static final String PREF_NETWORK_PREDICTIONS = "preload_pages"; @@ -64,6 +69,7 @@ public class BravePrivacySettings extends PrivacySettings { private static final String PREF_HTTPSE = "httpse"; private static final String PREF_DE_AMP = "de_amp"; private static final String PREF_IPFS_GATEWAY = "ipfs_gateway"; + private static final String PREF_BLOCK_COOKIE_CONSENT_NOTICES = "block_cookie_consent_notices"; private static final String PREF_AD_BLOCK = "ad_block"; private static final String PREF_BLOCK_SCRIPTS = "scripts_block"; public static final String PREF_FINGERPRINTING_PROTECTION = "fingerprinting_protection"; @@ -105,11 +111,11 @@ public class BravePrivacySettings extends PrivacySettings { PREF_OTHER_PRIVACY_SETTINGS_SECTION, // other section PREF_WEBRTC_POLICY, PREF_SAFE_BROWSING, PREF_INCOGNITO_LOCK, PREF_CAN_MAKE_PAYMENT, PREF_UNSTOPPABLE_DOMAINS, PREF_ETH_NAMED_SERVICE, PREF_IPFS_GATEWAY, PREF_SECURE_DNS, - PREF_DO_NOT_TRACK, PREF_PHONE_AS_A_SECURITY_KEY, PREF_CLOSE_TABS_ON_EXIT, PREF_SEND_P3A, - PREF_SEND_CRASH_REPORTS, PREF_BRAVE_STATS_USAGE_PING, - PREF_SHOW_AUTOCOMPLETE_IN_ADDRESS_BAR, PREF_SEARCH_SUGGESTIONS, - PREF_AUTOCOMPLETE_TOP_SITES, PREF_AUTOCOMPLETE_BRAVE_SUGGESTED_SITES, PREF_USAGE_STATS, - PREF_PRIVACY_SANDBOX}; + PREF_BLOCK_COOKIE_CONSENT_NOTICES, PREF_DO_NOT_TRACK, PREF_PHONE_AS_A_SECURITY_KEY, + PREF_CLOSE_TABS_ON_EXIT, PREF_SEND_P3A, PREF_SEND_CRASH_REPORTS, + PREF_BRAVE_STATS_USAGE_PING, PREF_SHOW_AUTOCOMPLETE_IN_ADDRESS_BAR, + PREF_SEARCH_SUGGESTIONS, PREF_AUTOCOMPLETE_TOP_SITES, + PREF_AUTOCOMPLETE_BRAVE_SUGGESTED_SITES, PREF_USAGE_STATS, PREF_PRIVACY_SANDBOX}; private final int STRICT = 0; private final int STANDARD = 1; @@ -137,6 +143,7 @@ public class BravePrivacySettings extends PrivacySettings { private ChromeSwitchPreference mSendCrashReports; private ChromeSwitchPreference mBraveStatsUsagePing; private ChromeSwitchPreference mIpfsGatewayPref; + private ChromeSwitchPreference mBlockCookieConsentNoticesPref; private PreferenceCategory mSocialBlockingCategory; private ChromeSwitchPreference mSocialBlockingGoogle; private ChromeSwitchPreference mHttpsEverywhere; @@ -147,6 +154,31 @@ public class BravePrivacySettings extends PrivacySettings { private ChromeSwitchPreference mClearBrowsingDataOnExit; private Preference mUstoppableDomains; private ChromeSwitchPreference mFingerprntLanguagePref; + private CookieListOptInPageAndroidHandler mCookieListOptInPageAndroidHandler; + + @Override + public void onConnectionError(MojoException e) { + mCookieListOptInPageAndroidHandler = null; + initCookieListOptInPageAndroidHandler(); + } + + private void initCookieListOptInPageAndroidHandler() { + if (mCookieListOptInPageAndroidHandler != null) { + return; + } + + mCookieListOptInPageAndroidHandler = + CookieListOptInServiceFactory.getInstance().getCookieListOptInPageAndroidHandler( + this); + } + + @Override + public void onDestroy() { + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.close(); + } + super.onDestroy(); + } @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -157,6 +189,8 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { SettingsUtils.addPreferencesFromResource(this, R.xml.brave_privacy_preferences); + initCookieListOptInPageAndroidHandler(); + mHttpsePref = (ChromeSwitchPreference) findPreference(PREF_HTTPSE); mHttpsePref.setOnPreferenceChangeListener(this); @@ -178,6 +212,10 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { mIpfsGatewayPref = (ChromeSwitchPreference) findPreference(PREF_IPFS_GATEWAY); mIpfsGatewayPref.setOnPreferenceChangeListener(this); + mBlockCookieConsentNoticesPref = + (ChromeSwitchPreference) findPreference(PREF_BLOCK_COOKIE_CONSENT_NOTICES); + mBlockCookieConsentNoticesPref.setOnPreferenceChangeListener(this); + mBlockCrosssiteCookies = (BraveDialogPreference) findPreference(PREF_BLOCK_CROSS_SITE_COOKIES); mBlockCrosssiteCookies.setOnPreferenceChangeListener(this); @@ -245,6 +283,14 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { removePreferenceIfPresent(PREF_PRIVACY_SANDBOX); removePreferenceIfPresent(PREF_SAFE_BROWSING); + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.shouldShowDialog(shouldShowDialog -> { + if (!shouldShowDialog) { + removePreferenceIfPresent(PREF_BLOCK_COOKIE_CONSENT_NOTICES); + } + }); + } + updateBravePreferences(); } @@ -295,6 +341,11 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { } else if (PREF_IPFS_GATEWAY.equals(key)) { BravePrefServiceBridge.getInstance().setIpfsGatewayEnabled((boolean) newValue); + } else if (PREF_BLOCK_COOKIE_CONSENT_NOTICES.equals(key)) { + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.enableFilter((boolean) newValue); + } + } else if (PREF_FINGERPRINTING_PROTECTION.equals(key)) { if (newValue instanceof String && String.valueOf(newValue).equals( @@ -522,6 +573,10 @@ private void updateBravePreferences() { mAutocompleteBraveSuggestedSites.setEnabled(autocompleteEnabled); mFingerprntLanguagePref.setChecked(UserPrefs.get(Profile.getLastUsedRegularProfile()) .getBoolean(BravePref.REDUCE_LANGUAGE_ENABLED)); + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.isFilterListEnabled( + isEnabled -> { mBlockCookieConsentNoticesPref.setChecked(isEnabled); }); + } } private void removePreferenceIfPresent(String key) { diff --git a/android/java/org/chromium/chrome/browser/shields/CookieListOptInServiceFactory.java b/android/java/org/chromium/chrome/browser/shields/CookieListOptInServiceFactory.java new file mode 100644 index 000000000000..ad05f6e23902 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/shields/CookieListOptInServiceFactory.java @@ -0,0 +1,58 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.chromium.chrome.browser.shields; + +import org.chromium.base.annotations.JNINamespace; +import org.chromium.base.annotations.NativeMethods; +import org.chromium.brave_shields.mojom.CookieListOptInPageAndroidHandler; +import org.chromium.chrome.browser.crypto_wallet.util.Utils; +import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.mojo.bindings.ConnectionErrorHandler; +import org.chromium.mojo.bindings.Interface; +import org.chromium.mojo.bindings.Interface.Proxy.Handler; +import org.chromium.mojo.system.MessagePipeHandle; +import org.chromium.mojo.system.impl.CoreImpl; + +@JNINamespace("chrome::android") +public class CookieListOptInServiceFactory { + private static final Object lock = new Object(); + private static CookieListOptInServiceFactory instance; + + public static CookieListOptInServiceFactory getInstance() { + synchronized (lock) { + if (instance == null) { + instance = new CookieListOptInServiceFactory(); + } + } + return instance; + } + + private CookieListOptInServiceFactory() {} + + public CookieListOptInPageAndroidHandler getCookieListOptInPageAndroidHandler( + ConnectionErrorHandler connectionErrorHandler) { + Profile profile = Utils.getProfile(false); // Always use regular profile + int nativeHandle = + CookieListOptInServiceFactoryJni.get().getInterfaceToCookieListOptInService( + profile); + MessagePipeHandle handle = wrapNativeHandle(nativeHandle); + CookieListOptInPageAndroidHandler cookieListOptInPageAndroidHandler = + CookieListOptInPageAndroidHandler.MANAGER.attachProxy(handle, 0); + Handler handler = ((Interface.Proxy) cookieListOptInPageAndroidHandler).getProxyHandler(); + handler.setErrorHandler(connectionErrorHandler); + + return cookieListOptInPageAndroidHandler; + } + + private MessagePipeHandle wrapNativeHandle(int nativeHandle) { + return CoreImpl.getInstance().acquireNativeHandle(nativeHandle).toMessagePipeHandle(); + } + + @NativeMethods + interface Natives { + int getInterfaceToCookieListOptInService(Profile profile); + } +} diff --git a/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayoutImpl.java b/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayoutImpl.java index 8ce616324cd2..effa8965b5e6 100644 --- a/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayoutImpl.java +++ b/android/java/org/chromium/chrome/browser/toolbar/top/BraveToolbarLayoutImpl.java @@ -53,11 +53,13 @@ import org.chromium.base.BraveFeatureList; import org.chromium.base.BraveReflectionUtil; import org.chromium.base.ContextUtils; +import org.chromium.base.Log; import org.chromium.base.MathUtils; import org.chromium.base.ThreadUtils; import org.chromium.base.supplier.BooleanSupplier; import org.chromium.base.supplier.ObservableSupplier; import org.chromium.base.task.AsyncTask; +import org.chromium.brave_shields.mojom.CookieListOptInPageAndroidHandler; import org.chromium.chrome.R; import org.chromium.chrome.browser.BraveAdsNativeHelper; import org.chromium.chrome.browser.BraveRelaunchUtils; @@ -98,6 +100,7 @@ import org.chromium.chrome.browser.shields.BraveShieldsHandler; import org.chromium.chrome.browser.shields.BraveShieldsMenuObserver; import org.chromium.chrome.browser.shields.BraveShieldsUtils; +import org.chromium.chrome.browser.shields.CookieListOptInServiceFactory; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabImpl; import org.chromium.chrome.browser.tab.TabSelectionType; @@ -123,6 +126,8 @@ import org.chromium.components.url_formatter.UrlFormatter; import org.chromium.components.user_prefs.UserPrefs; import org.chromium.content_public.browser.NavigationHandle; +import org.chromium.mojo.bindings.ConnectionErrorHandler; +import org.chromium.mojo.system.MojoException; import org.chromium.ui.UiUtils; import org.chromium.ui.interpolators.BakedBezierInterpolator; import org.chromium.ui.util.ColorUtils; @@ -143,7 +148,8 @@ public abstract class BraveToolbarLayoutImpl extends ToolbarLayout implements BraveToolbarLayout, OnClickListener, View.OnLongClickListener, - BraveRewardsObserver, BraveRewardsNativeWorker.PublisherObserver { + BraveRewardsObserver, BraveRewardsNativeWorker.PublisherObserver, + ConnectionErrorHandler { private static final String JAPAN_COUNTRY_CODE = "JP"; private static final String YOUTUBE_DOMAIN = "youtube.com"; private static final List mBraveSearchEngineDefaultRegions = @@ -181,6 +187,7 @@ public abstract class BraveToolbarLayoutImpl extends ToolbarLayout private boolean mIsInitialNotificationPosted; // initial red circle notification private PopupWindowTooltip mShieldsPopupWindowTooltip; + private PopupWindowTooltip mCookieConsentTooltip; private boolean mIsBottomToolbarVisible; @@ -190,6 +197,8 @@ public abstract class BraveToolbarLayoutImpl extends ToolbarLayout private final Set mTabsWithWalletIcon = Collections.synchronizedSet(new HashSet()); + private CookieListOptInPageAndroidHandler mCookieListOptInPageAndroidHandler; + private enum BIGTECH_COMPANY { Google, Facebook, Amazon } public BraveToolbarLayoutImpl(Context context, AttributeSet attrs) { @@ -201,8 +210,10 @@ void destroy() { if (mBraveShieldsContentSettings != null) { mBraveShieldsContentSettings.removeObserver(mBraveShieldsContentSettingsObserver); } + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.close(); + } super.destroy(); - if (mBraveRewardsNativeWorker != null) { mBraveRewardsNativeWorker.RemoveObserver(this); mBraveRewardsNativeWorker.RemovePublisherObserver(this); @@ -336,9 +347,26 @@ public void savedBandwidth(long savings) { } } + @Override + public void onConnectionError(MojoException e) { + mCookieListOptInPageAndroidHandler = null; + initCookieListOptInPageAndroidHandler(); + } + + private void initCookieListOptInPageAndroidHandler() { + if (mCookieListOptInPageAndroidHandler != null) { + return; + } + + mCookieListOptInPageAndroidHandler = + CookieListOptInServiceFactory.getInstance().getCookieListOptInPageAndroidHandler( + this); + } + @Override protected void onNativeLibraryReady() { super.onNativeLibraryReady(); + initCookieListOptInPageAndroidHandler(); mBraveShieldsContentSettings = BraveShieldsContentSettings.getInstance(); mBraveShieldsContentSettings.addObserver(mBraveShieldsContentSettingsObserver); @@ -409,6 +437,17 @@ public void onPageLoadFinished(final Tab tab, GURL url) { && mBraveShieldsHandler != null && !mBraveShieldsHandler.isShowing()) { checkForTooltip(tab); } + if (mBraveShieldsButton != null && mBraveShieldsButton.isShown() + && mBraveShieldsHandler != null && !mBraveShieldsHandler.isShowing() + && !url.getSpec().startsWith(UrlConstants.CHROME_SCHEME) + && !UrlUtilities.isNTPUrl(url.getSpec())) { + SharedPreferencesManager.getInstance().writeInt( + BravePreferenceKeys.LOADED_SITE_COUNT, + SharedPreferencesManager.getInstance().readInt( + BravePreferenceKeys.LOADED_SITE_COUNT, 0) + + 1); + maybeShowCookieConsentTooltip(); + } } String countryCode = Locale.getDefault().getCountry(); @@ -581,6 +620,79 @@ private void showTooltip(String tooltipPref, int tabId) { } } + private void maybeShowCookieConsentTooltip() { + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.shouldShowDialog(shouldShowDialog -> { + mCookieListOptInPageAndroidHandler.isFilterListEnabled(isEnabled -> { + if (!isEnabled && shouldShowDialog + && SharedPreferencesManager.getInstance().readBoolean( + BravePreferenceKeys.SHOULD_SHOW_COOKIE_CONSENT_NOTICE, true) + && SharedPreferencesManager.getInstance().readInt( + BravePreferenceKeys.LOADED_SITE_COUNT, 0) + > 5) { + showCookieConsentTooltip(); + } + }); + }); + } + } + + private void showCookieConsentTooltip() { + ViewGroup viewGroup = + BraveActivity.getBraveActivity().getWindow().getDecorView().findViewById( + android.R.id.content); + float padding = (float) dpToPx(getContext(), 20); + mCookieConsentTooltip = new PopupWindowTooltip.Builder(getContext()) + .anchorView(mBraveShieldsButton) + .arrowColor(ContextCompat.getColor( + getContext(), R.color.cookie_consent_tooltip_color)) + .gravity(Gravity.BOTTOM) + .dismissOnOutsideTouch(false) + .dismissOnInsideTouch(false) + .backgroundDimDisabled(false) + .padding(padding) + .parentPaddingHorizontal(dpToPx(getContext(), 10)) + .modal(true) + .onDismissListener(tooltip + -> { + + }) + .contentView(R.layout.block_cookie_consent_notices_tooltip) + .build(); + + Button btnAction = mCookieConsentTooltip.findViewById(R.id.btn_action); + btnAction.setOnClickListener((new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.onTooltipYesClicked(); + mCookieListOptInPageAndroidHandler.enableFilter(true); + } + mCookieConsentTooltip.dismiss(); + } + })); + + TextView txtNoThanks = mCookieConsentTooltip.findViewById(R.id.txt_no_thanks); + txtNoThanks.setOnClickListener((new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.onTooltipNoClicked(); + } + mCookieConsentTooltip.dismiss(); + } + })); + + if (mBraveShieldsButton != null && mBraveShieldsButton.isShown()) { + mCookieConsentTooltip.show(); + SharedPreferencesManager.getInstance().writeBoolean( + BravePreferenceKeys.SHOULD_SHOW_COOKIE_CONSENT_NOTICE, false); + if (mCookieListOptInPageAndroidHandler != null) { + mCookieListOptInPageAndroidHandler.onTooltipShown(); + } + } + } + public void dismissShieldsTooltip() { if (mShieldsPopupWindowTooltip != null && mShieldsPopupWindowTooltip.isShowing()) { mShieldsPopupWindowTooltip.dismiss(); diff --git a/android/java/res/drawable-hdpi/ic_cookie.png b/android/java/res/drawable-hdpi/ic_cookie.png new file mode 100644 index 000000000000..a734969a7d95 Binary files /dev/null and b/android/java/res/drawable-hdpi/ic_cookie.png differ diff --git a/android/java/res/drawable-hdpi/ic_cookie_background.png b/android/java/res/drawable-hdpi/ic_cookie_background.png new file mode 100644 index 000000000000..612e10408da1 Binary files /dev/null and b/android/java/res/drawable-hdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-mdpi/ic_cookie.png b/android/java/res/drawable-mdpi/ic_cookie.png new file mode 100644 index 000000000000..485d845b5b20 Binary files /dev/null and b/android/java/res/drawable-mdpi/ic_cookie.png differ diff --git a/android/java/res/drawable-mdpi/ic_cookie_background.png b/android/java/res/drawable-mdpi/ic_cookie_background.png new file mode 100644 index 000000000000..fc8d6f785e45 Binary files /dev/null and b/android/java/res/drawable-mdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-night-hdpi/ic_cookie_background.png b/android/java/res/drawable-night-hdpi/ic_cookie_background.png new file mode 100644 index 000000000000..a872c0ade48b Binary files /dev/null and b/android/java/res/drawable-night-hdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-night-mdpi/ic_cookie_background.png b/android/java/res/drawable-night-mdpi/ic_cookie_background.png new file mode 100644 index 000000000000..9319c5ee2985 Binary files /dev/null and b/android/java/res/drawable-night-mdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-night-xhdpi/ic_cookie_background.png b/android/java/res/drawable-night-xhdpi/ic_cookie_background.png new file mode 100644 index 000000000000..6212a9a710f2 Binary files /dev/null and b/android/java/res/drawable-night-xhdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-night-xxhdpi/ic_cookie_background.png b/android/java/res/drawable-night-xxhdpi/ic_cookie_background.png new file mode 100644 index 000000000000..be084553de47 Binary files /dev/null and b/android/java/res/drawable-night-xxhdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-night-xxxhdpi/ic_cookie_background.png b/android/java/res/drawable-night-xxxhdpi/ic_cookie_background.png new file mode 100644 index 000000000000..7c31d7f87b97 Binary files /dev/null and b/android/java/res/drawable-night-xxxhdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-xhdpi/ic_cookie.png b/android/java/res/drawable-xhdpi/ic_cookie.png new file mode 100644 index 000000000000..32df68378806 Binary files /dev/null and b/android/java/res/drawable-xhdpi/ic_cookie.png differ diff --git a/android/java/res/drawable-xhdpi/ic_cookie_background.png b/android/java/res/drawable-xhdpi/ic_cookie_background.png new file mode 100644 index 000000000000..23a1f93a5c14 Binary files /dev/null and b/android/java/res/drawable-xhdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-xxhdpi/ic_cookie.png b/android/java/res/drawable-xxhdpi/ic_cookie.png new file mode 100644 index 000000000000..3098e290b6cc Binary files /dev/null and b/android/java/res/drawable-xxhdpi/ic_cookie.png differ diff --git a/android/java/res/drawable-xxhdpi/ic_cookie_background.png b/android/java/res/drawable-xxhdpi/ic_cookie_background.png new file mode 100644 index 000000000000..fd2e6610d24a Binary files /dev/null and b/android/java/res/drawable-xxhdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable-xxxhdpi/ic_cookie.png b/android/java/res/drawable-xxxhdpi/ic_cookie.png new file mode 100644 index 000000000000..0a715f854fc8 Binary files /dev/null and b/android/java/res/drawable-xxxhdpi/ic_cookie.png differ diff --git a/android/java/res/drawable-xxxhdpi/ic_cookie_background.png b/android/java/res/drawable-xxxhdpi/ic_cookie_background.png new file mode 100644 index 000000000000..0269e5772ab5 Binary files /dev/null and b/android/java/res/drawable-xxxhdpi/ic_cookie_background.png differ diff --git a/android/java/res/drawable/cookie_consent_tooltip_background.xml b/android/java/res/drawable/cookie_consent_tooltip_background.xml new file mode 100644 index 000000000000..d88a1ffb8870 --- /dev/null +++ b/android/java/res/drawable/cookie_consent_tooltip_background.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/android/java/res/layout/block_cookie_consent_notices_tooltip.xml b/android/java/res/layout/block_cookie_consent_notices_tooltip.xml new file mode 100644 index 000000000000..fe5468066260 --- /dev/null +++ b/android/java/res/layout/block_cookie_consent_notices_tooltip.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/android/java/res/values-night/brave_colors.xml b/android/java/res/values-night/brave_colors.xml index a5d181dd45ac..2738532df930 100644 --- a/android/java/res/values-night/brave_colors.xml +++ b/android/java/res/values-night/brave_colors.xml @@ -120,4 +120,8 @@ @android:color/white + + @android:color/white + #F0F2FF + #1E2029 diff --git a/android/java/res/values/brave_colors.xml b/android/java/res/values/brave_colors.xml index 22031bb7ff49..d9669f00c88f 100644 --- a/android/java/res/values/brave_colors.xml +++ b/android/java/res/values/brave_colors.xml @@ -212,4 +212,8 @@ #171919 + + @color/brave_action_color + #212529 + @android:color/white diff --git a/android/java/res/xml/brave_privacy_preferences.xml b/android/java/res/xml/brave_privacy_preferences.xml index a664b9f6038c..ee2d7fbe247a 100644 --- a/android/java/res/xml/brave_privacy_preferences.xml +++ b/android/java/res/xml/brave_privacy_preferences.xml @@ -108,6 +108,12 @@ android:title="@string/ipfs_gateway_title" android:summary="@string/ipfs_gateway_summary" android:defaultValue="true" /> + & profile_android) { + auto* profile = ProfileAndroid::FromProfileAndroid(profile_android); + auto pending = brave_shields::CookieListOptInServiceFactory::GetInstance() + ->GetForContext(profile); + + return static_cast(pending.PassPipe().release().value()); +} + +} // namespace android +} // namespace chrome diff --git a/browser/brave_shields/cookie_list_opt_in_service_factory.cc b/browser/brave_shields/cookie_list_opt_in_service_factory.cc new file mode 100644 index 000000000000..2d7d0b875b0e --- /dev/null +++ b/browser/brave_shields/cookie_list_opt_in_service_factory.cc @@ -0,0 +1,68 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/brave_shields/cookie_list_opt_in_service_factory.h" + +#include + +#include "brave/browser/brave_browser_process.h" +#include "brave/components/brave_shields/browser/cookie_list_opt_in_service.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profiles/incognito_helpers.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" + +namespace brave_shields { + +// static +CookieListOptInServiceFactory* CookieListOptInServiceFactory::GetInstance() { + return base::Singleton::get(); +} + +// static +mojo::PendingRemote +CookieListOptInServiceFactory::GetForContext(content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)) + ->MakeRemote(); +} + +// static +CookieListOptInService* CookieListOptInServiceFactory::GetServiceForContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +void CookieListOptInServiceFactory::BindForContext( + content::BrowserContext* context, + mojo::PendingReceiver receiver) { + auto* cookie_list_opt_in_service = + CookieListOptInServiceFactory::GetServiceForContext(context); + if (cookie_list_opt_in_service) { + cookie_list_opt_in_service->Bind(std::move(receiver)); + } +} + +CookieListOptInServiceFactory::CookieListOptInServiceFactory() + : BrowserContextKeyedServiceFactory( + "CookieListOptInService", + BrowserContextDependencyManager::GetInstance()) {} + +CookieListOptInServiceFactory::~CookieListOptInServiceFactory() = default; + +KeyedService* CookieListOptInServiceFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + auto* ad_block_service = g_brave_browser_process->ad_block_service(); + return new CookieListOptInService(ad_block_service, + g_browser_process->local_state()); +} + +content::BrowserContext* CookieListOptInServiceFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return chrome::GetBrowserContextRedirectedInIncognito(context); +} + +} // namespace brave_shields diff --git a/browser/brave_shields/cookie_list_opt_in_service_factory.h b/browser/brave_shields/cookie_list_opt_in_service_factory.h new file mode 100644 index 000000000000..333cd6547e6d --- /dev/null +++ b/browser/brave_shields/cookie_list_opt_in_service_factory.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_BRAVE_SHIELDS_COOKIE_LIST_OPT_IN_SERVICE_FACTORY_H_ +#define BRAVE_BROWSER_BRAVE_SHIELDS_COOKIE_LIST_OPT_IN_SERVICE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "brave/components/brave_shields/common/cookie_list_opt_in.mojom.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" +#include "components/keyed_service/core/keyed_service.h" +#include "content/public/browser/browser_context.h" +#include "mojo/public/cpp/bindings/pending_remote.h" + +namespace brave_shields { + +class CookieListOptInService; + +class CookieListOptInServiceFactory : public BrowserContextKeyedServiceFactory { + public: + CookieListOptInServiceFactory(const CookieListOptInServiceFactory&) = delete; + CookieListOptInServiceFactory& operator=( + const CookieListOptInServiceFactory&) = delete; + + static mojo::PendingRemote + GetForContext(content::BrowserContext* context); + static CookieListOptInService* GetServiceForContext( + content::BrowserContext* context); + static CookieListOptInServiceFactory* GetInstance(); + static void BindForContext( + content::BrowserContext* context, + mojo::PendingReceiver receiver); + + private: + friend struct base::DefaultSingletonTraits; + + CookieListOptInServiceFactory(); + ~CookieListOptInServiceFactory() override; + + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; +}; + +} // namespace brave_shields + +#endif // BRAVE_BROWSER_BRAVE_SHIELDS_COOKIE_LIST_OPT_IN_SERVICE_FACTORY_H_ diff --git a/browser/brave_shields/sources.gni b/browser/brave_shields/sources.gni index 3afe95656f33..7ffa33bf3a4d 100644 --- a/browser/brave_shields/sources.gni +++ b/browser/brave_shields/sources.gni @@ -10,6 +10,8 @@ brave_browser_brave_shields_sources = [ "//brave/browser/brave_shields/ad_block_subscription_download_manager_getter.h", "//brave/browser/brave_shields/brave_shields_web_contents_observer.cc", "//brave/browser/brave_shields/brave_shields_web_contents_observer.h", + "//brave/browser/brave_shields/cookie_list_opt_in_service_factory.cc", + "//brave/browser/brave_shields/cookie_list_opt_in_service_factory.h", "//brave/browser/brave_shields/https_everywhere_component_installer.cc", "//brave/browser/brave_shields/https_everywhere_component_installer.h", ] @@ -34,7 +36,12 @@ brave_browser_brave_shields_deps = [ ] if (is_android) { - brave_browser_brave_shields_sources += [ "//brave/browser/brave_shields/brave_shields_web_contents_observer_android.cc" ] - brave_browser_brave_shields_deps += - [ "//brave/browser/android:android_browser_process" ] + brave_browser_brave_shields_sources += [ + "//brave/browser/brave_shields/android/cookie_list_opt_in_service_factory_android.cc", + "//brave/browser/brave_shields/brave_shields_web_contents_observer_android.cc", + ] + brave_browser_brave_shields_deps += [ + "//brave/browser/android:android_browser_process", + "//brave/build/android:jni_headers", + ] } diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd index 78e58da88742..3923cdb926c0 100644 --- a/browser/ui/android/strings/android_brave_strings.grd +++ b/browser/ui/android/strings/android_brave_strings.grd @@ -212,6 +212,12 @@ This file contains all "about" strings. It is set to NOT be translated, in tran Allows for navigation to IPFS resources through an IPFS Gateway + + Block cookie consent notices + + + Blocks website pop-ups that ask you to accept / decline cookies on their site + Unstoppable Domains @@ -2947,6 +2953,15 @@ If you don't accept this request, VPN will not reconnect and your internet conne Enter your Brave Wallet password + + Block cookie notices + + + Tired of cookie consent notices? + + + Brave already blocks third-party cookies. We can block cookie consent notices, too. + diff --git a/browser/ui/views/brave_shields/cookie_list_opt_in_bubble_host.cc b/browser/ui/views/brave_shields/cookie_list_opt_in_bubble_host.cc index f09c224f4085..df8262ba75be 100644 --- a/browser/ui/views/brave_shields/cookie_list_opt_in_bubble_host.cc +++ b/browser/ui/views/brave_shields/cookie_list_opt_in_bubble_host.cc @@ -8,6 +8,7 @@ #include #include "base/feature_list.h" +#include "base/metrics/histogram_functions.h" #include "brave/browser/brave_browser_process.h" #include "brave/browser/ui/webui/brave_shields/cookie_list_opt_in_ui.h" #include "brave/components/brave_shields/browser/ad_block_regional_service_manager.h" @@ -57,6 +58,8 @@ bool ShouldEventuallyShowBubble() { return false; } + base::UmaHistogramExactLinear(kCookieListPromptHistogram, 0, 4); + auto* regional_service_manager = g_brave_browser_process->ad_block_service()->regional_service_manager(); DCHECK(regional_service_manager); diff --git a/browser/ui/webui/brave_shields/cookie_list_opt_in_page_handler.cc b/browser/ui/webui/brave_shields/cookie_list_opt_in_page_handler.cc index 0e7ca2076474..f1a95d870aab 100644 --- a/browser/ui/webui/brave_shields/cookie_list_opt_in_page_handler.cc +++ b/browser/ui/webui/brave_shields/cookie_list_opt_in_page_handler.cc @@ -7,6 +7,7 @@ #include +#include "base/metrics/histogram_functions.h" #include "brave/browser/brave_browser_process.h" #include "brave/components/brave_shields/browser/ad_block_regional_service_manager.h" #include "brave/components/brave_shields/browser/ad_block_service.h" @@ -27,6 +28,8 @@ CookieListOptInPageHandler::CookieListOptInPageHandler( CookieListOptInPageHandler::~CookieListOptInPageHandler() = default; void CookieListOptInPageHandler::ShowUI() { + base::UmaHistogramExactLinear(brave_shields::kCookieListPromptHistogram, 1, + 4); if (embedder_) { embedder_->ShowUI(); } @@ -43,3 +46,13 @@ void CookieListOptInPageHandler::EnableFilter() { ->regional_service_manager() ->EnableFilterList(brave_shields::kCookieListUuid, true); } + +void CookieListOptInPageHandler::OnUINoClicked() { + base::UmaHistogramExactLinear(brave_shields::kCookieListPromptHistogram, 2, + 4); +} + +void CookieListOptInPageHandler::OnUIYesClicked() { + base::UmaHistogramExactLinear(brave_shields::kCookieListPromptHistogram, 3, + 4); +} diff --git a/browser/ui/webui/brave_shields/cookie_list_opt_in_page_handler.h b/browser/ui/webui/brave_shields/cookie_list_opt_in_page_handler.h index 68295c2c5605..03537e9f3c48 100644 --- a/browser/ui/webui/brave_shields/cookie_list_opt_in_page_handler.h +++ b/browser/ui/webui/brave_shields/cookie_list_opt_in_page_handler.h @@ -35,6 +35,9 @@ class CookieListOptInPageHandler void CloseUI() override; void EnableFilter() override; + void OnUINoClicked() override; + void OnUIYesClicked() override; + private: mojo::Receiver receiver_; base::WeakPtr embedder_; diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn index da39d1b7913d..9c5c54fd53e2 100755 --- a/build/android/BUILD.gn +++ b/build/android/BUILD.gn @@ -198,6 +198,7 @@ generate_jni("jni_headers") { "//brave/android/java/org/chromium/chrome/browser/preferences/BravePrefServiceBridge.java", "//brave/android/java/org/chromium/chrome/browser/preferences/website/BraveShieldsContentSettings.java", "//brave/android/java/org/chromium/chrome/browser/settings/developer/BraveQAPreferences.java", + "//brave/android/java/org/chromium/chrome/browser/shields/CookieListOptInServiceFactory.java", "//brave/android/java/org/chromium/chrome/browser/signin/BraveSigninManager.java", "//brave/android/java/org/chromium/chrome/browser/sync/BraveSyncDevices.java", "//brave/android/java/org/chromium/chrome/browser/vpn/BraveVpnNativeWorker.java", diff --git a/build/android/config.gni b/build/android/config.gni index 94e395166147..fefde1987236 100644 --- a/build/android/config.gni +++ b/build/android/config.gni @@ -20,6 +20,7 @@ brave_chrome_java_deps = [ "//brave/browser/safe_browsing/android/java/src/org/chromium/chrome/browser/safe_browsing/settings:java", "//brave/browser/ui/android/omnibox:java", "//brave/browser/ui/android/theme:java", + "//brave/components/brave_shields/common:mojom_java", "//brave/components/brave_today/common:mojom_java", "//brave/components/brave_wallet/common:mojom_java", "//brave/components/browser_ui/site_settings/android:java", diff --git a/components/brave_shields/browser/BUILD.gn b/components/brave_shields/browser/BUILD.gn index c1fee23067e7..54c62e7e4dee 100644 --- a/components/brave_shields/browser/BUILD.gn +++ b/components/brave_shields/browser/BUILD.gn @@ -42,6 +42,8 @@ if (!is_ios) { "brave_shields_p3a.h", "brave_shields_util.cc", "brave_shields_util.h", + "cookie_list_opt_in_service.cc", + "cookie_list_opt_in_service.h", "domain_block_controller_client.cc", "domain_block_controller_client.h", "domain_block_navigation_throttle.cc", diff --git a/components/brave_shields/browser/ad_block_regional_service_manager.cc b/components/brave_shields/browser/ad_block_regional_service_manager.cc index 6fba4c5e2060..d9c85cd478b0 100644 --- a/components/brave_shields/browser/ad_block_regional_service_manager.cc +++ b/components/brave_shields/browser/ad_block_regional_service_manager.cc @@ -10,6 +10,7 @@ #include #include "base/feature_list.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/string_util.h" #include "base/values.h" #include "brave/components/brave_shields/browser/ad_block_engine.h" @@ -146,6 +147,13 @@ void AdBlockRegionalServiceManager::UpdateFilterListPrefs( if (uuid == kCookieListUuid) { local_state_->SetBoolean(prefs::kAdBlockCookieListSettingTouched, true); } + + RecordP3ACookieListEnabled(); +} + +void AdBlockRegionalServiceManager::RecordP3ACookieListEnabled() { + UMA_HISTOGRAM_BOOLEAN(kCookieListEnabledHistogram, + IsFilterListEnabled(kCookieListUuid)); } bool AdBlockRegionalServiceManager::Start() { @@ -334,6 +342,7 @@ void AdBlockRegionalServiceManager::SetFilterListCatalog( std::vector catalog) { filter_list_catalog_ = std::move(catalog); StartRegionalServices(); + RecordP3ACookieListEnabled(); } const std::vector& diff --git a/components/brave_shields/browser/ad_block_regional_service_manager.h b/components/brave_shields/browser/ad_block_regional_service_manager.h index 8b8987da688c..74ad5c0ae9d5 100644 --- a/components/brave_shields/browser/ad_block_regional_service_manager.h +++ b/components/brave_shields/browser/ad_block_regional_service_manager.h @@ -91,6 +91,8 @@ class AdBlockRegionalServiceManager void StartRegionalServices(); void UpdateFilterListPrefs(const std::string& uuid, bool enabled); + void RecordP3ACookieListEnabled(); + raw_ptr local_state_; std::string locale_; bool initialized_; diff --git a/components/brave_shields/browser/cookie_list_opt_in_service.cc b/components/brave_shields/browser/cookie_list_opt_in_service.cc new file mode 100644 index 000000000000..0d652d26c75b --- /dev/null +++ b/components/brave_shields/browser/cookie_list_opt_in_service.cc @@ -0,0 +1,83 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/components/brave_shields/browser/cookie_list_opt_in_service.h" + +#include + +#include "base/feature_list.h" +#include "base/metrics/histogram_functions.h" +#include "brave/components/brave_shields/browser/ad_block_regional_service_manager.h" +#include "brave/components/brave_shields/browser/ad_block_service.h" +#include "brave/components/brave_shields/common/brave_shield_constants.h" +#include "brave/components/brave_shields/common/cookie_list_opt_in.mojom-forward.h" +#include "brave/components/brave_shields/common/cookie_list_opt_in.mojom-shared.h" +#include "brave/components/brave_shields/common/features.h" +#include "brave/components/brave_shields/common/pref_names.h" +#include "components/prefs/pref_service.h" + +namespace brave_shields { + +CookieListOptInService::CookieListOptInService(AdBlockService* ad_block_service, + PrefService* local_state) + : ad_block_service_(ad_block_service), local_state_(local_state) { + if (base::FeatureList::IsEnabled( + brave_shields::features::kBraveAdblockCookieListOptIn)) { + if (!local_state->GetBoolean(prefs::kAdBlockCookieListOptInShown)) { + base::UmaHistogramExactLinear(kCookieListPromptHistogram, 0, 4); + } + } +} + +CookieListOptInService::~CookieListOptInService() = default; + +mojo::PendingRemote +CookieListOptInService::MakeRemote() { + mojo::PendingRemote remote; + receivers_.Add(this, remote.InitWithNewPipeAndPassReceiver()); + return remote; +} + +void CookieListOptInService::Bind( + mojo::PendingReceiver receiver) { + receivers_.Add(this, std::move(receiver)); +} + +void CookieListOptInService::ShouldShowDialog( + ShouldShowDialogCallback callback) { + bool should_show_dialog = + base::FeatureList::IsEnabled( + brave_shields::features::kBraveAdblockCookieListOptIn) && + ad_block_service_->regional_service_manager()->IsFilterListAvailable( + brave_shields::kCookieListUuid); + std::move(callback).Run(should_show_dialog); +} + +void CookieListOptInService::IsFilterListEnabled( + IsFilterListEnabledCallback callback) { + std::move(callback).Run( + ad_block_service_->regional_service_manager()->IsFilterListEnabled( + brave_shields::kCookieListUuid)); +} + +void CookieListOptInService::EnableFilter(bool shouldEnableFilter) { + ad_block_service_->regional_service_manager()->EnableFilterList( + brave_shields::kCookieListUuid, shouldEnableFilter); +} + +void CookieListOptInService::OnTooltipShown() { + local_state_->SetBoolean(prefs::kAdBlockCookieListOptInShown, true); + base::UmaHistogramExactLinear(kCookieListPromptHistogram, 1, 4); +} + +void CookieListOptInService::OnTooltipNoClicked() { + base::UmaHistogramExactLinear(kCookieListPromptHistogram, 2, 4); +} + +void CookieListOptInService::OnTooltipYesClicked() { + base::UmaHistogramExactLinear(kCookieListPromptHistogram, 3, 4); +} + +} // namespace brave_shields diff --git a/components/brave_shields/browser/cookie_list_opt_in_service.h b/components/brave_shields/browser/cookie_list_opt_in_service.h new file mode 100644 index 000000000000..5f7a57ab827c --- /dev/null +++ b/components/brave_shields/browser/cookie_list_opt_in_service.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2022 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_COMPONENTS_BRAVE_SHIELDS_BROWSER_COOKIE_LIST_OPT_IN_SERVICE_H_ +#define BRAVE_COMPONENTS_BRAVE_SHIELDS_BROWSER_COOKIE_LIST_OPT_IN_SERVICE_H_ + +#include "base/memory/weak_ptr.h" +#include "brave/components/brave_shields/common/cookie_list_opt_in.mojom-forward.h" +#include "brave/components/brave_shields/common/cookie_list_opt_in.mojom.h" +#include "components/keyed_service/core/keyed_service.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote_set.h" + +class PrefService; + +namespace brave_shields { +class AdBlockService; + +// This class is not thread-safe and should have single owner +class CookieListOptInService : public KeyedService, + public mojom::CookieListOptInPageAndroidHandler { + public: + CookieListOptInService(AdBlockService* ad_block_service, + PrefService* local_state); + ~CookieListOptInService() override; + + mojo::PendingRemote MakeRemote(); + void Bind( + mojo::PendingReceiver receiver); + + void ShouldShowDialog(ShouldShowDialogCallback callback) override; + void IsFilterListEnabled(IsFilterListEnabledCallback callback) override; + void EnableFilter(bool shouldEnableFilter) override; + + void OnTooltipShown() override; + void OnTooltipNoClicked() override; + void OnTooltipYesClicked() override; + + private: + raw_ptr ad_block_service_ = nullptr; + raw_ptr local_state_ = nullptr; + mojo::ReceiverSet receivers_; + base::WeakPtrFactory discovery_weak_factory_{this}; + + CookieListOptInService(const CookieListOptInService&) = delete; + CookieListOptInService& operator=(const CookieListOptInService&) = delete; +}; + +} // namespace brave_shields + +#endif // BRAVE_COMPONENTS_BRAVE_SHIELDS_BROWSER_COOKIE_LIST_OPT_IN_SERVICE_H_ diff --git a/components/brave_shields/browser/cookie_list_opt_in_service_unittest.cc b/components/brave_shields/browser/cookie_list_opt_in_service_unittest.cc new file mode 100644 index 000000000000..c70b858df35c --- /dev/null +++ b/components/brave_shields/browser/cookie_list_opt_in_service_unittest.cc @@ -0,0 +1,108 @@ +// Copyright (c) 2022 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. + +#include "brave/components/brave_shields/browser/cookie_list_opt_in_service.h" + +#include + +#include "base/feature_list.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" +#include "brave/components/brave_shields/browser/ad_block_service.h" +#include "brave/components/brave_shields/common/brave_shield_constants.h" +#include "brave/components/brave_shields/common/features.h" +#include "brave/components/brave_shields/common/pref_names.h" +#include "components/prefs/testing_pref_service.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace brave_shields { + +class CookieListOptInServiceTest : public testing::Test { + public: + CookieListOptInServiceTest() = default; + + protected: + void SetUp() override { + auto* registry = pref_service_.registry(); + RegisterPrefsForAdBlockService(registry); + histogram_tester_ = std::make_unique(); + } + + PrefService* GetPrefs() { return &pref_service_; } + + base::test::ScopedFeatureList scoped_feature_list_; + std::unique_ptr histogram_tester_; + TestingPrefServiceSimple pref_service_; +}; + +TEST_F(CookieListOptInServiceTest, FeatureDisabledNoInitHistogram) { + scoped_feature_list_.InitWithFeatures({}, {}); + CookieListOptInService service(nullptr, GetPrefs()); + // Should not write to histogram if feature is disabled + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 0); +} + +TEST_F(CookieListOptInServiceTest, FeatureEnabledInitHistogram) { + scoped_feature_list_.InitWithFeatures( + {brave_shields::features::kBraveAdblockCookieListOptIn}, {}); + CookieListOptInService service(nullptr, GetPrefs()); + // Should write to histogram if feature is enabled + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 1); + histogram_tester_->ExpectBucketCount(kCookieListPromptHistogram, 0, 1); +} + +TEST_F(CookieListOptInServiceTest, FeatureEnabledShownNoInitHistogram) { + scoped_feature_list_.InitWithFeatures( + {brave_shields::features::kBraveAdblockCookieListOptIn}, {}); + GetPrefs()->SetBoolean(prefs::kAdBlockCookieListOptInShown, true); + CookieListOptInService service(nullptr, GetPrefs()); + // Should not write to histogram if tooltip was already shown + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 0); +} + +TEST_F(CookieListOptInServiceTest, TooltipShownHistogram) { + scoped_feature_list_.InitWithFeatures( + {brave_shields::features::kBraveAdblockCookieListOptIn}, {}); + CookieListOptInService service(nullptr, GetPrefs()); + + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 1); + histogram_tester_->ExpectBucketCount(kCookieListPromptHistogram, 0, 1); + + service.OnTooltipShown(); + + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 2); + histogram_tester_->ExpectBucketCount(kCookieListPromptHistogram, 1, 1); +} + +TEST_F(CookieListOptInServiceTest, TooltipNoClickedHistogram) { + scoped_feature_list_.InitWithFeatures( + {brave_shields::features::kBraveAdblockCookieListOptIn}, {}); + CookieListOptInService service(nullptr, GetPrefs()); + + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 1); + histogram_tester_->ExpectBucketCount(kCookieListPromptHistogram, 0, 1); + + service.OnTooltipNoClicked(); + + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 2); + histogram_tester_->ExpectBucketCount(kCookieListPromptHistogram, 2, 1); +} + +TEST_F(CookieListOptInServiceTest, TooltipYesClickedHistogram) { + scoped_feature_list_.InitWithFeatures( + {brave_shields::features::kBraveAdblockCookieListOptIn}, {}); + CookieListOptInService service(nullptr, GetPrefs()); + + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 1); + histogram_tester_->ExpectBucketCount(kCookieListPromptHistogram, 0, 1); + + service.OnTooltipYesClicked(); + + histogram_tester_->ExpectTotalCount(kCookieListPromptHistogram, 2); + histogram_tester_->ExpectBucketCount(kCookieListPromptHistogram, 3, 1); +} + +} // namespace brave_shields diff --git a/components/brave_shields/common/BUILD.gn b/components/brave_shields/common/BUILD.gn index 7104614202f7..34f537a996c4 100644 --- a/components/brave_shields/common/BUILD.gn +++ b/components/brave_shields/common/BUILD.gn @@ -27,6 +27,7 @@ static_library("common") { } mojom("mojom") { + generate_java = true sources = [ "brave_shields.mojom", "brave_shields_panel.mojom", diff --git a/components/brave_shields/common/brave_shield_constants.h b/components/brave_shields/common/brave_shield_constants.h index 6ad2823fd90d..eabcb52cdf9b 100644 --- a/components/brave_shields/common/brave_shield_constants.h +++ b/components/brave_shields/common/brave_shield_constants.h @@ -87,6 +87,9 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { IDS_BRAVE_SHIELDS_BLOCKED_SCRIPTS_LABEL}, }; +const char kCookieListEnabledHistogram[] = "Brave.Shields.CookieListEnabled"; +const char kCookieListPromptHistogram[] = "Brave.Shields.CookieListPrompt"; + } // namespace brave_shields #endif // BRAVE_COMPONENTS_BRAVE_SHIELDS_COMMON_BRAVE_SHIELD_CONSTANTS_H_ diff --git a/components/brave_shields/common/cookie_list_opt_in.mojom b/components/brave_shields/common/cookie_list_opt_in.mojom index 414d89163c01..10d65105e467 100644 --- a/components/brave_shields/common/cookie_list_opt_in.mojom +++ b/components/brave_shields/common/cookie_list_opt_in.mojom @@ -13,4 +13,17 @@ interface CookieListOptInPageHandler { ShowUI(); CloseUI(); EnableFilter(); + + OnUINoClicked(); + OnUIYesClicked(); +}; + +interface CookieListOptInPageAndroidHandler { + ShouldShowDialog() => (bool should_show_dialog); + IsFilterListEnabled() => (bool is_enabled); + EnableFilter(bool should_enable_filter); + + OnTooltipShown(); + OnTooltipNoClicked(); + OnTooltipYesClicked(); }; diff --git a/components/brave_shields/resources/cookie_list_opt_in/cookie_list_opt_in.tsx b/components/brave_shields/resources/cookie_list_opt_in/cookie_list_opt_in.tsx index ba9b57def729..328731196e5c 100644 --- a/components/brave_shields/resources/cookie_list_opt_in/cookie_list_opt_in.tsx +++ b/components/brave_shields/resources/cookie_list_opt_in/cookie_list_opt_in.tsx @@ -19,7 +19,13 @@ function App () { proxy.handler.closeUI() } + const onDecline = () => { + proxy.handler.onUINoClicked() + closeBubble() + } + const onEnable = () => { + proxy.handler.onUIYesClicked() proxy.handler.enableFilter() } @@ -49,7 +55,7 @@ function App () { key={openedAt} onEnable={onEnable} onDismiss={closeBubble} - onDecline={closeBubble} + onDecline={onDecline} onAnimationComplete={closeBubble} /> ) diff --git a/components/p3a/metric_names.h b/components/p3a/metric_names.h index f29e958ffb69..42626bc16e93 100644 --- a/components/p3a/metric_names.h +++ b/components/p3a/metric_names.h @@ -53,6 +53,8 @@ constexpr inline auto kCollectedHistograms = "Brave.Search.SwitchEngine", "Brave.Search.WebDiscoveryEnabled", "Brave.Shields.AdBlockSetting", + "Brave.Shields.CookieListEnabled", + "Brave.Shields.CookieListPrompt", "Brave.Shields.DomainAdsSettingsAboveGlobal", "Brave.Shields.DomainAdsSettingsBelowGlobal", "Brave.Shields.DomainFingerprintSettingsAboveGlobal", diff --git a/test/BUILD.gn b/test/BUILD.gn index ac8d10ad94ee..7f5b472b9ca0 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -122,6 +122,7 @@ test("brave_unit_tests") { "//brave/components/brave_shields/browser/ad_block_regional_service_unittest.cc", "//brave/components/brave_shields/browser/adblock_stub_response_unittest.cc", "//brave/components/brave_shields/browser/brave_farbling_service_unittest.cc", + "//brave/components/brave_shields/browser/cookie_list_opt_in_service_unittest.cc", "//brave/components/brave_shields/browser/cosmetic_merge_unittest.cc", "//brave/components/brave_shields/browser/csp_merge_unittest.cc", "//brave/components/brave_shields/browser/https_everywhere_recently_used_cache_unittest.cpp",