[Shred] Privacy Bypass – Private Tab Domain Disclosure via “Shred Data” Popup on Brave Android (#36920)
Added an ability to hide the manual shred button when the tab switch mode is active in incognito mode and private tabs are locked. Resolves: https://github.com/brave/brave-browser/issues/55944 --------- Signed-off-by: Vadym Struts <vstruts@brave.com>
This commit is contained in:
@@ -145,6 +145,8 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||
import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
|
||||
import org.chromium.chrome.browser.fullscreen.FullscreenManager;
|
||||
import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthController;
|
||||
import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager;
|
||||
import org.chromium.chrome.browser.informers.BraveSyncAccountDeletedInformer;
|
||||
import org.chromium.chrome.browser.lifetime.ApplicationLifetime;
|
||||
import org.chromium.chrome.browser.misc_metrics.MiscAndroidMetricsConnectionErrorHandler;
|
||||
@@ -357,6 +359,7 @@ public abstract class BraveActivity extends ChromeActivity
|
||||
private SearchWidgetPromoPanel mSearchWidgetPromoPanel;
|
||||
|
||||
private ApplicationStateListener mApplicationStateListener;
|
||||
private IncognitoReauthController mIncognitoReauthController;
|
||||
|
||||
/** Serves as a general exception for failed attempts to get BraveActivity. */
|
||||
public static class BraveActivityNotFoundException extends Exception {
|
||||
@@ -2461,6 +2464,9 @@ public abstract class BraveActivity extends ChromeActivity
|
||||
dbUtil.cleanUpDbOperationRequest();
|
||||
}
|
||||
super.performPreInflationStartup();
|
||||
mRootUiCoordinator
|
||||
.getIncognitoReauthControllerSupplier()
|
||||
.onAvailable(irc -> mIncognitoReauthController = irc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2824,6 +2830,30 @@ public abstract class BraveActivity extends ChromeActivity
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShredButtonVisible() {
|
||||
return mIncognitoReauthController == null
|
||||
|| !mIncognitoReauthController.isIncognitoReauthPending();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setShredButtonVisibilityObserver(
|
||||
IncognitoReauthManager.IncognitoReauthCallback callback) {
|
||||
if (mIncognitoReauthController == null) {
|
||||
return;
|
||||
}
|
||||
mIncognitoReauthController.addIncognitoReauthCallback(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeShredButtonVisibilityObserver(
|
||||
IncognitoReauthManager.IncognitoReauthCallback callback) {
|
||||
if (mIncognitoReauthController == null) {
|
||||
return;
|
||||
}
|
||||
mIncognitoReauthController.removeIncognitoReauthCallback(callback);
|
||||
}
|
||||
|
||||
private void shredData(Tab currentTab) {
|
||||
LayoutInflater inflater =
|
||||
(LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
@@ -218,4 +218,17 @@ if (is_android) {
|
||||
|
||||
deps = [ "//chrome/android/junit:chrome_junit_tests_helper" ]
|
||||
}
|
||||
|
||||
robolectric_library("brave_junit_tests_org.chromium.chrome.browser.hub") {
|
||||
sources = [
|
||||
"src/org/chromium/chrome/browser/hub/BraveHubToolbarViewUnitTest.java",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//brave/browser/brave_shields/android:java",
|
||||
"//chrome/android:chrome_java",
|
||||
"//chrome/android/junit:chrome_junit_tests_helper",
|
||||
"//chrome/browser/hub/internal:java",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/* Copyright (c) 2026 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 https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.chromium.chrome.browser.hub;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import static org.chromium.chrome.browser.hub.HubColorMixer.COLOR_MIXER;
|
||||
import static org.chromium.chrome.browser.hub.HubToolbarProperties.IS_INCOGNITO;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import org.chromium.base.BraveFeatureList;
|
||||
import org.chromium.base.supplier.ObservableSuppliers;
|
||||
import org.chromium.base.supplier.SettableMonotonicObservableSupplier;
|
||||
import org.chromium.base.test.BaseRobolectricTestRunner;
|
||||
import org.chromium.base.test.util.CommandLineFlags;
|
||||
import org.chromium.base.test.util.Features.DisableFeatures;
|
||||
import org.chromium.base.test.util.Features.EnableFeatures;
|
||||
import org.chromium.chrome.R;
|
||||
import org.chromium.chrome.browser.ChromeTabbedActivity;
|
||||
import org.chromium.chrome.browser.app.BraveActivity;
|
||||
import org.chromium.chrome.browser.brave_shields.FirstPartyStorageCleanerInterface;
|
||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||
import org.chromium.ui.modelutil.PropertyModel;
|
||||
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
|
||||
|
||||
/** Unit tests for {@link BraveHubToolbarView}. */
|
||||
@RunWith(BaseRobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
@CommandLineFlags.Add({
|
||||
ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
|
||||
ChromeSwitches.DISABLE_NATIVE_INITIALIZATION
|
||||
})
|
||||
public class BraveHubToolbarViewUnitTest {
|
||||
@Rule
|
||||
public ActivityScenarioRule<ChromeTabbedActivity> mActivityScenarioRule =
|
||||
new ActivityScenarioRule<>(ChromeTabbedActivity.class);
|
||||
|
||||
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
|
||||
private Pane mPane;
|
||||
private SettableMonotonicObservableSupplier<Pane> mFocusedPaneSupplier;
|
||||
private FrameLayout mToolbarContainer;
|
||||
private Button mShredButton;
|
||||
private PropertyModel mPropertyModel;
|
||||
private BraveActivity mActivity;
|
||||
private HubColorMixer mColorMixer;
|
||||
private BraveHubToolbarView mHubToolbarView;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mActivityScenarioRule.getScenario().onActivity(this::onActivity);
|
||||
}
|
||||
|
||||
private void onActivity(ChromeTabbedActivity activity) {
|
||||
mPane = mock();
|
||||
BraveActivity braveActivity = (BraveActivity) (Object) activity;
|
||||
mActivity = spy(braveActivity);
|
||||
mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(mActivity);
|
||||
mToolbarContainer =
|
||||
(FrameLayout) inflater.inflate(R.layout.hub_toolbar_layout, null, false);
|
||||
mHubToolbarView = (BraveHubToolbarView) mToolbarContainer.findViewById(R.id.hub_toolbar);
|
||||
mShredButton =
|
||||
mToolbarContainer.findViewById(
|
||||
org.chromium.chrome.browser.brave_shields.R.id.shred_data_button);
|
||||
mActivity.setContentView(mToolbarContainer);
|
||||
|
||||
mHubToolbarView.setFirstPartyStorageCleanerForTesting(
|
||||
(FirstPartyStorageCleanerInterface) mActivity);
|
||||
|
||||
mFocusedPaneSupplier = ObservableSuppliers.createMonotonic();
|
||||
mColorMixer =
|
||||
spy(
|
||||
new HubColorMixerImpl(
|
||||
mActivity, ObservableSuppliers.alwaysTrue(), mFocusedPaneSupplier));
|
||||
mPropertyModel =
|
||||
new PropertyModel.Builder(HubToolbarProperties.ALL_KEYS)
|
||||
.with(COLOR_MIXER, mColorMixer)
|
||||
.build();
|
||||
PropertyModelChangeProcessor.create(
|
||||
mPropertyModel, mHubToolbarView, HubToolbarViewBinder::bind);
|
||||
when(mPane.getColorScheme()).thenReturn(HubColorScheme.DEFAULT);
|
||||
mFocusedPaneSupplier.set(mPane);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFeatures({BraveFeatureList.BRAVE_SHRED})
|
||||
public void testHideManualShredButtonInIncognitoMode() {
|
||||
when(mActivity.isShredButtonVisible()).thenReturn(true);
|
||||
assertEquals(View.VISIBLE, mShredButton.getVisibility());
|
||||
|
||||
when(mActivity.isShredButtonVisible()).thenReturn(false);
|
||||
mPropertyModel.set(IS_INCOGNITO, true);
|
||||
assertEquals(View.INVISIBLE, mShredButton.getVisibility());
|
||||
|
||||
when(mActivity.isShredButtonVisible()).thenReturn(true);
|
||||
mPropertyModel.set(IS_INCOGNITO, false);
|
||||
assertEquals(View.VISIBLE, mShredButton.getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisableFeatures({BraveFeatureList.BRAVE_SHRED})
|
||||
public void testHideManualShredButtonIfFeatureFlagDisabled() {
|
||||
when(mActivity.isShredButtonVisible()).thenReturn(true);
|
||||
assertEquals(View.VISIBLE, mShredButton.getVisibility());
|
||||
|
||||
when(mActivity.isShredButtonVisible()).thenReturn(false);
|
||||
mPropertyModel.set(IS_INCOGNITO, true);
|
||||
assertEquals(View.INVISIBLE, mShredButton.getVisibility());
|
||||
|
||||
when(mActivity.isShredButtonVisible()).thenReturn(true);
|
||||
mPropertyModel.set(IS_INCOGNITO, false);
|
||||
assertEquals(View.INVISIBLE, mShredButton.getVisibility());
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ android_library("java") {
|
||||
deps = [
|
||||
":java_resources",
|
||||
"//base:base_java",
|
||||
"//chrome/browser/incognito:java",
|
||||
"//chrome/browser/profiles/android:java",
|
||||
"//chrome/browser/tab:java",
|
||||
"//third_party/android_deps:com_airbnb_android_lottie_java",
|
||||
|
||||
+8
@@ -6,8 +6,16 @@
|
||||
package org.chromium.chrome.browser.brave_shields;
|
||||
|
||||
import org.chromium.build.annotations.NullMarked;
|
||||
import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager;
|
||||
|
||||
@NullMarked
|
||||
public interface FirstPartyStorageCleanerInterface {
|
||||
void shredSiteData();
|
||||
|
||||
boolean isShredButtonVisible();
|
||||
|
||||
void setShredButtonVisibilityObserver(IncognitoReauthManager.IncognitoReauthCallback callback);
|
||||
|
||||
void removeShredButtonVisibilityObserver(
|
||||
IncognitoReauthManager.IncognitoReauthCallback callback);
|
||||
}
|
||||
|
||||
@@ -13,5 +13,6 @@ brave_browser_hub_internal_java_sources = [
|
||||
brave_browser_hub_internal_java_deps = [
|
||||
"//base:base_shared_preferences_java",
|
||||
"//brave/browser/brave_shields/android:java",
|
||||
"//chrome/browser/incognito:java",
|
||||
"//chrome/browser/preferences:java",
|
||||
]
|
||||
|
||||
+66
-9
@@ -14,10 +14,15 @@ import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import org.chromium.base.ApplicationStatus;
|
||||
import org.chromium.base.BraveFeatureList;
|
||||
import org.chromium.base.BravePreferenceKeys;
|
||||
import org.chromium.build.annotations.Nullable;
|
||||
import org.chromium.chrome.browser.brave_shields.FirstPartyStorageCleanerInterface;
|
||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||
import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager;
|
||||
import org.chromium.chrome.browser.preferences.ChromeSharedPreferences;
|
||||
import org.chromium.chrome.browser.toolbar.settings.AddressBarPreference;
|
||||
|
||||
@@ -25,10 +30,14 @@ import org.chromium.chrome.browser.toolbar.settings.AddressBarPreference;
|
||||
* Brave's extension for {@link HubToolbarView}. Here we control what elements should be visible in
|
||||
* tab switcher mode when bottom toolbar is visible.
|
||||
*/
|
||||
public class BraveHubToolbarView extends HubToolbarView {
|
||||
public class BraveHubToolbarView extends HubToolbarView
|
||||
implements ApplicationStatus.TaskVisibilityListener,
|
||||
IncognitoReauthManager.IncognitoReauthCallback {
|
||||
private Button mActionButton;
|
||||
private Button mShredButton;
|
||||
private FrameLayout mMenuButton;
|
||||
private boolean mIsIncognitoSelected = true;
|
||||
private @Nullable FirstPartyStorageCleanerInterface mFpCleaner;
|
||||
|
||||
public BraveHubToolbarView(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
@@ -37,7 +46,6 @@ public class BraveHubToolbarView extends HubToolbarView {
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
mActionButton = findViewById(R.id.toolbar_action_button);
|
||||
mShredButton =
|
||||
findViewById(org.chromium.chrome.browser.brave_shields.R.id.shred_data_button);
|
||||
@@ -45,15 +53,34 @@ public class BraveHubToolbarView extends HubToolbarView {
|
||||
|
||||
mShredButton.setOnClickListener(
|
||||
v -> {
|
||||
Context context = getContext();
|
||||
if (context instanceof FirstPartyStorageCleanerInterface) {
|
||||
FirstPartyStorageCleanerInterface fpCleaner =
|
||||
(FirstPartyStorageCleanerInterface) context;
|
||||
fpCleaner.shredSiteData();
|
||||
if (mFpCleaner != null) {
|
||||
mFpCleaner.shredSiteData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
Context context = getContext();
|
||||
if (context instanceof FirstPartyStorageCleanerInterface) {
|
||||
mFpCleaner = (FirstPartyStorageCleanerInterface) context;
|
||||
mFpCleaner.setShredButtonVisibilityObserver(this);
|
||||
}
|
||||
|
||||
ApplicationStatus.registerTaskVisibilityListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
ApplicationStatus.unregisterTaskVisibilityListener(this);
|
||||
if (mFpCleaner != null) {
|
||||
mFpCleaner.removeShredButtonVisibilityObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void setPaneSwitcherIndex(int index) {
|
||||
super.setPaneSwitcherIndex(index);
|
||||
@@ -62,6 +89,13 @@ public class BraveHubToolbarView extends HubToolbarView {
|
||||
updateButtonsVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateIncognitoElements(boolean isIncognito) {
|
||||
super.updateIncognitoElements(isIncognito);
|
||||
mIsIncognitoSelected = isIncognito;
|
||||
updateButtonsVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
@@ -76,10 +110,31 @@ public class BraveHubToolbarView extends HubToolbarView {
|
||||
@Override
|
||||
void setMenuButtonVisible(boolean visible) {
|
||||
super.setMenuButtonVisible(visible);
|
||||
|
||||
updateButtonsVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIncognitoReauthSuccess() {
|
||||
updateButtonsVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIncognitoReauthNotPossible() {}
|
||||
|
||||
@Override
|
||||
public void onIncognitoReauthFailure() {}
|
||||
|
||||
@Override
|
||||
public void onTaskVisibilityChanged(int taskId, boolean isVisible) {
|
||||
updateButtonsVisibility();
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
public void setFirstPartyStorageCleanerForTesting(
|
||||
FirstPartyStorageCleanerInterface firstPartyStorageCleaner) {
|
||||
mFpCleaner = firstPartyStorageCleaner;
|
||||
}
|
||||
|
||||
private void updateButtonsVisibility() {
|
||||
boolean shouldHideButtons =
|
||||
AddressBarPreference.isToolbarConfiguredToShowOnTop()
|
||||
@@ -104,8 +159,10 @@ public class BraveHubToolbarView extends HubToolbarView {
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isShredButtonVisible =
|
||||
!mIsIncognitoSelected || mFpCleaner == null || mFpCleaner.isShredButtonVisible();
|
||||
final boolean shouldShowShredButton =
|
||||
ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_SHRED);
|
||||
ChromeFeatureList.isEnabled(BraveFeatureList.BRAVE_SHRED) && isShredButtonVisible;
|
||||
mShredButton.setVisibility(shouldShowShredButton ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1690,6 +1690,7 @@ if (is_android) {
|
||||
"//brave/android/junit:brave_junit_tests_org.chromium.chrome.browser.compositor",
|
||||
"//brave/android/junit:brave_junit_tests_org.chromium.chrome.browser.custom_search_engines",
|
||||
"//brave/android/junit:brave_junit_tests_org.chromium.chrome.browser.homepage",
|
||||
"//brave/android/junit:brave_junit_tests_org.chromium.chrome.browser.hub",
|
||||
"//brave/android/junit:brave_junit_tests_org.chromium.chrome.browser.ntp",
|
||||
"//brave/android/junit:brave_junit_tests_org.chromium.chrome.browser.preferences.website",
|
||||
"//brave/android/junit:brave_junit_tests_org.chromium.chrome.browser.quick_search_engines",
|
||||
|
||||
Reference in New Issue
Block a user