diff --git a/build/patches/Add-history-support-in-incognito-mode.patch b/build/patches/Add-history-support-in-incognito-mode.patch index 92a4d4ff..2847db04 100644 --- a/build/patches/Add-history-support-in-incognito-mode.patch +++ b/build/patches/Add-history-support-in-incognito-mode.patch @@ -2,17 +2,82 @@ From: uazo Date: Fri, 7 Aug 2020 16:33:47 +0000 Subject: Add history support in incognito mode ---- - .../java/res/xml/privacy_preferences.xml | 5 +++++ - .../privacy/settings/PrivacySettings.java | 18 ++++++++++++++++++ - chrome/browser/history/history_tab_helper.cc | 16 ++++++++++++++++ - chrome/browser/history/history_tab_helper.h | 4 ++++ - chrome/browser/prefs/browser_prefs.cc | 3 +++ - .../android/strings/android_chrome_strings.grd | 6 ++++++ - chrome/common/pref_names.cc | 5 +++++ - chrome/common/pref_names.h | 4 ++++ - 8 files changed, 61 insertions(+) +Add history, recents, offlinepages and send to home screen for always incognito. +History, recent tabs and offline pages require the INCOGNITO_TAB_HISTORY_ENABLED +flag turned on. +IncognitoPlaceholder is also deactivated, both in the phone and tablet version. +The relative tests are also present. +See also: https://github.com/bromite/bromite/pull/1427 +--- + .../chrome_junit_test_java_sources.gni | 4 + + chrome/android/chrome_test_java_sources.gni | 6 + + .../java/res/xml/privacy_preferences.xml | 5 + + .../AppMenuPropertiesDelegateImpl.java | 29 +- + .../CustomTabAppMenuPropertiesDelegate.java | 3 + + .../browser/download/DownloadUtils.java | 16 +- + .../browser/history/HistoryManager.java | 17 +- + .../chrome/browser/history/HistoryPage.java | 14 + + .../chrome/browser/ntp/RecentTabsManager.java | 8 +- + .../privacy/settings/PrivacySettings.java | 18 + + .../browser/tab/HistoricalTabSaver.java | 12 +- + .../browser/tabmodel/TabPersistentStore.java | 5 +- + .../history/Bromite_HistoryManagerTest.java | 112 ++++++ + ...FragmentTest_HistoryInAlwaysIncognito.java | 120 +++++++ + ...ite_AppMenuPropertiesDelegateUnitTest.java | 323 ++++++++++++++++++ + .../tab/HistoricalTabSaverUnitTest.java | 16 +- + .../browser/android/historical_tab_saver.cc | 28 +- + chrome/browser/android/historical_tab_saver.h | 27 ++ + .../android/historical_tab_saver_unittest.cc | 148 ++++++++ + chrome/browser/history/history_tab_helper.cc | 16 + + chrome/browser/history/history_tab_helper.h | 10 +- + .../history/history_tab_helper_unittest.cc | 22 ++ + .../android/offline_page_bridge.cc | 13 +- + .../android/offline_page_model_factory.cc | 21 +- + .../android/request_coordinator_factory.cc | 17 + + .../offline_page_model_factory.h | 2 + + .../offline_pages/recent_tab_helper.cc | 12 + + .../request_coordinator_factory.h | 3 + + chrome/browser/prefs/browser_prefs.cc | 3 + + .../browser/ui/android/native_page/BUILD.gn | 2 + + .../browser/ui/native_page/NativePage.java | 6 +- + .../ui/native_page/NativePageTest.java | 26 ++ + .../strings/android_chrome_strings.grd | 6 + + chrome/common/pref_names.cc | 5 + + chrome/common/pref_names.h | 4 + + chrome/test/BUILD.gn | 5 + + 36 files changed, 1046 insertions(+), 38 deletions(-) + create mode 100644 chrome/android/javatests/src/org/chromium/chrome/browser/history/Bromite_HistoryManagerTest.java + create mode 100644 chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/Bromite_PrivacySettingsFragmentTest_HistoryInAlwaysIncognito.java + create mode 100644 chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/Bromite_AppMenuPropertiesDelegateUnitTest.java + create mode 100644 chrome/browser/android/historical_tab_saver.h + create mode 100644 chrome/browser/android/historical_tab_saver_unittest.cc + +diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni +--- a/chrome/android/chrome_junit_test_java_sources.gni ++++ b/chrome/android/chrome_junit_test_java_sources.gni +@@ -219,3 +219,7 @@ chrome_junit_test_java_sources = [ + "junit/src/org/chromium/chrome/browser/webapps/WebappLauncherActivityTest.java", + "junit/src/org/chromium/chrome/browser/webapps/WebappRegistryTest.java", + ] ++ ++chrome_junit_test_java_sources += [ ++ "junit/src/org/chromium/chrome/browser/app/appmenu/Bromite_AppMenuPropertiesDelegateUnitTest.java", ++] +\ No newline at end of file +diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni +--- a/chrome/android/chrome_test_java_sources.gni ++++ b/chrome/android/chrome_test_java_sources.gni +@@ -641,3 +641,9 @@ chrome_test_java_sources = [ + if (enable_feed_v2) { + chrome_test_java_sources += [ "javatests/src/org/chromium/chrome/browser/ntp/NewTabPageColorWithFeedV2Test.java" ] + } ++ ++# Bromite tests ++chrome_test_java_sources += [ ++ "javatests/src/org/chromium/chrome/browser/privacy/settings/Bromite_PrivacySettingsFragmentTest_HistoryInAlwaysIncognito.java", ++ "javatests/src/org/chromium/chrome/browser/history/Bromite_HistoryManagerTest.java", ++] diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml --- a/chrome/android/java/res/xml/privacy_preferences.xml +++ b/chrome/android/java/res/xml/privacy_preferences.xml @@ -28,6 +93,207 @@ diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/androi tabSupplier) { + super(host); + ++ if (isIncognito && ++ ContextUtils.getAppSharedPreferences().getBoolean("always_incognito", false)) { ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ boolean historyEnabledInIncognito = ++ prefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ if (historyEnabledInIncognito == true) isIncognito = false; ++ } ++ + mHistoryManager = new HistoryManager( + activity, false, snackbarManager, isIncognito, tabCreatorManager, tabSupplier); + mTitle = host.getContext().getResources().getString(R.string.menu_history); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsManager.java +@@ -86,15 +86,15 @@ public class RecentTabsManager implements SignInStateObserver, + */ + public RecentTabsManager( + Tab tab, Profile profile, Context context, Runnable showHistoryManager) { +- mProfile = profile; ++ mProfile = profile.getOriginalProfile(); + mTab = tab; + mShowHistoryManager = showHistoryManager; +- mForeignSessionHelper = new ForeignSessionHelper(profile); +- mPrefs = new RecentTabsPagePrefs(profile); ++ mForeignSessionHelper = new ForeignSessionHelper(mProfile); ++ mPrefs = new RecentTabsPagePrefs(mProfile); + mFaviconHelper = new FaviconHelper(); + mRecentlyClosedTabManager = sRecentlyClosedTabManagerForTests != null + ? sRecentlyClosedTabManagerForTests +- : new RecentlyClosedBridge(profile); ++ : new RecentlyClosedBridge(mProfile); + + mRecentlyClosedTabManager.setTabsUpdatedRunnable(() -> { + updateRecentlyClosedTabs(); diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java --- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java @@ -83,6 +349,919 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/setting } private ChromeManagedPreferenceDelegate createManagedPreferenceDelegate() { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/HistoricalTabSaver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/HistoricalTabSaver.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/HistoricalTabSaver.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/HistoricalTabSaver.java +@@ -15,28 +15,28 @@ public class HistoricalTabSaver { + /** + * Creates a historical tab from a tab being closed. + */ +- public static void createHistoricalTab(Tab tab) { ++ public static void createHistoricalTab(Tab tab, boolean is_always_incognito) { + if (tab.isFrozen()) { + WebContentsState state = CriticalPersistedTabData.from(tab).getWebContentsState(); + if (state != null) { + WebContents webContents = + WebContentsStateBridge.restoreContentsFromByteBuffer(state, true); + if (webContents != null) { +- createHistoricalTabFromContents(webContents); ++ createHistoricalTabFromContents(webContents, is_always_incognito); + webContents.destroy(); + } + } + } else { +- createHistoricalTabFromContents(tab.getWebContents()); ++ createHistoricalTabFromContents(tab.getWebContents(), is_always_incognito); + } + } + +- private static void createHistoricalTabFromContents(WebContents webContents) { +- HistoricalTabSaverJni.get().createHistoricalTabFromContents(webContents); ++ private static void createHistoricalTabFromContents(WebContents webContents, boolean is_always_incognito) { ++ HistoricalTabSaverJni.get().createHistoricalTabFromContents(webContents, is_always_incognito); + } + + @NativeMethods + interface Natives { +- void createHistoricalTabFromContents(WebContents webContents); ++ void createHistoricalTabFromContents(WebContents webContents, boolean is_always_incognito); + } + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java +@@ -153,7 +153,10 @@ public class TabPersistentStore { + @Override + public void didCloseTab(Tab tab) { + PersistedTabData.onTabClose(tab); +- if (!tab.isIncognito()) HistoricalTabSaver.createHistoricalTab(tab); ++ boolean is_always_incognito = ++ ContextUtils.getAppSharedPreferences().getBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false); ++ if (!tab.isIncognito() || is_always_incognito) ++ HistoricalTabSaver.createHistoricalTab(tab, is_always_incognito); + removeTabFromQueues(tab); + } + +diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/Bromite_HistoryManagerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/Bromite_HistoryManagerTest.java +new file mode 100644 +--- /dev/null ++++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/Bromite_HistoryManagerTest.java +@@ -0,0 +1,112 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite 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 Bromite. If not, see . ++*/ ++ ++package org.chromium.chrome.browser.history; ++ ++import android.app.Activity; ++import android.content.Context; ++import android.content.Intent; ++ ++import androidx.test.espresso.intent.rule.IntentsTestRule; ++import androidx.test.filters.SmallTest; ++ ++import org.hamcrest.Matcher; ++import org.junit.Assert; ++import org.junit.After; ++import org.junit.Before; ++import org.junit.Rule; ++import org.junit.Test; ++import org.junit.runner.RunWith; ++import org.chromium.base.test.util.Restriction; ++ ++import org.chromium.chrome.browser.IntentHandler; ++import org.chromium.chrome.browser.incognito.IncognitoUtils; ++import org.chromium.chrome.browser.flags.ChromeSwitches; ++import org.chromium.chrome.browser.profiles.Profile; ++import org.chromium.base.ContextUtils; ++import org.chromium.components.prefs.PrefService; ++import org.chromium.components.user_prefs.UserPrefs; ++import org.chromium.chrome.browser.preferences.Pref; ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++import android.content.SharedPreferences; ++ ++import org.chromium.chrome.test.ChromeJUnit4ClassRunner; ++import org.chromium.base.test.BaseActivityTestRule; ++import org.chromium.base.test.util.CommandLineFlags; ++import org.chromium.ui.test.util.UiRestriction; ++import org.chromium.content_public.browser.test.util.TestThreadUtils; ++ ++/** ++ * Tests the {@link HistoryManager}. ++ */ ++@RunWith(ChromeJUnit4ClassRunner.class) ++@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) ++@Restriction(UiRestriction.RESTRICTION_TYPE_PHONE) ++public class Bromite_HistoryManagerTest { ++ @Rule ++ public BaseActivityTestRule mIncognitoActivityTestRule = ++ new BaseActivityTestRule<>(HistoryActivity.class); ++ ++ @Before ++ public void setUp() { ++ } ++ ++ @After ++ public void tearDown() { ++ TestThreadUtils.runOnUiThreadBlocking(() -> { ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ prefService.clearPref(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ ++ SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); ++ SharedPreferences.Editor editor = prefs.edit(); ++ editor.putBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false); ++ editor.apply(); ++ }); ++ } ++ ++ boolean mShouldShowIncognitoPlaceholder = true; ++ ++ @Test ++ @SmallTest ++ public void testShouldShowIncognitoPlaceholder_AlwaysIncognito() throws Exception { ++ // launch HistoryActivity in incognito mode ++ Intent intent = new Intent(ContextUtils.getApplicationContext(), HistoryActivity.class); ++ intent.putExtra(IntentHandler.EXTRA_INCOGNITO_MODE, true); ++ ++ mIncognitoActivityTestRule.launchActivity(intent); ++ HistoryActivity activity = mIncognitoActivityTestRule.getActivity(); ++ HistoryManager historyManager = activity.getHistoryManagerForTests(); ++ Assert.assertTrue(historyManager.isIncognito()); ++ ++ TestThreadUtils.runOnUiThreadBlocking(() -> { ++ // set always incognito on ++ SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); ++ SharedPreferences.Editor editor = prefs.edit(); ++ editor.putBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, true); ++ editor.apply(); ++ ++ // set INCOGNITO_TAB_HISTORY_ENABLED on ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ prefService.setBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED, true); ++ ++ mShouldShowIncognitoPlaceholder = historyManager.shouldShowIncognitoPlaceholder(); ++ }); ++ ++ // shouldShowIncognitoPlaceholder must be false ++ Assert.assertFalse(mShouldShowIncognitoPlaceholder); ++ } ++} +diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/Bromite_PrivacySettingsFragmentTest_HistoryInAlwaysIncognito.java b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/Bromite_PrivacySettingsFragmentTest_HistoryInAlwaysIncognito.java +new file mode 100644 +--- /dev/null ++++ b/chrome/android/javatests/src/org/chromium/chrome/browser/privacy/settings/Bromite_PrivacySettingsFragmentTest_HistoryInAlwaysIncognito.java +@@ -0,0 +1,120 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite 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 Bromite. If not, see . ++*/ ++ ++package org.chromium.chrome.browser.privacy.settings; ++ ++import android.view.View; ++ ++import androidx.recyclerview.widget.RecyclerView; ++import androidx.test.filters.LargeTest; ++ ++import org.junit.Assert; ++import org.junit.After; ++import org.junit.Before; ++import org.junit.Rule; ++import org.junit.Test; ++import org.junit.rules.RuleChain; ++import org.junit.runner.RunWith; ++ ++import org.chromium.base.test.util.CommandLineFlags; ++import org.chromium.base.test.util.CriteriaHelper; ++import org.chromium.base.test.util.Feature; ++import org.chromium.chrome.R; ++import org.chromium.chrome.browser.flags.ChromeSwitches; ++import org.chromium.chrome.browser.settings.SettingsActivityTestRule; ++import org.chromium.chrome.test.ChromeJUnit4ClassRunner; ++import org.chromium.chrome.test.ChromeTabbedActivityTestRule; ++import org.chromium.chrome.test.util.ChromeRenderTestRule; ++import org.chromium.content_public.browser.test.util.TestThreadUtils; ++ ++import org.chromium.chrome.browser.profiles.Profile; ++import org.chromium.chrome.browser.preferences.Pref; ++import org.chromium.components.prefs.PrefService; ++import org.chromium.components.user_prefs.UserPrefs; ++import org.chromium.components.browser_ui.settings.ChromeSwitchPreference; ++ ++/** ++ * Tests for {@link PrivacySettings}. ++ */ ++@RunWith(ChromeJUnit4ClassRunner.class) ++@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE}) ++public class Bromite_PrivacySettingsFragmentTest_HistoryInAlwaysIncognito { ++ public final ChromeTabbedActivityTestRule mActivityTestRule = ++ new ChromeTabbedActivityTestRule(); ++ public final SettingsActivityTestRule mSettingsActivityTestRule = ++ new SettingsActivityTestRule<>(PrivacySettings.class); ++ ++ // SettingsActivity has to be finished before the outer CTA can be finished or trying to finish ++ // CTA won't work. ++ @Rule ++ public final RuleChain mRuleChain = ++ RuleChain.outerRule(mActivityTestRule).around(mSettingsActivityTestRule); ++ ++ @Before ++ public void setUp() { ++ mActivityTestRule.startMainActivityOnBlankPage(); ++ } ++ ++ @After ++ public void tearDown() { ++ TestThreadUtils.runOnUiThreadBlocking(() -> { ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ prefService.clearPref(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ }); ++ } ++ ++ private void waitForOptionsMenu() { ++ CriteriaHelper.pollUiThread(() -> { ++ return mSettingsActivityTestRule.getActivity().findViewById(R.id.menu_id_targeted_help) ++ != null; ++ }); ++ } ++ ++ @Test ++ @LargeTest ++ public void testIncognitoHistoryPreference() { ++ mSettingsActivityTestRule.startSettingsActivity(); ++ waitForOptionsMenu(); ++ PrivacySettings fragment = mSettingsActivityTestRule.getFragment(); ++ TestThreadUtils.runOnUiThreadBlocking(() -> { ++ RecyclerView recyclerView = fragment.getView().findViewById(R.id.recycler_view); ++ recyclerView.scrollToPosition(recyclerView.getAdapter().getItemCount() - 1); ++ }); ++ ++ ChromeSwitchPreference historyInIncognitoPref = ++ (ChromeSwitchPreference) fragment.findPreference(PrivacySettings.PREF_INCOGNITO_TAB_HISTORY_ENABLED); ++ Assert.assertNotNull(historyInIncognitoPref); ++ ++ // Test when ON ++ TestThreadUtils.runOnUiThreadBlocking(() -> { ++ fragment.onPreferenceChange(historyInIncognitoPref, true); ++ ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ boolean incognito_tab_history_enabled = prefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ Assert.assertTrue(incognito_tab_history_enabled); ++ }); ++ ++ // Test when OFF ++ TestThreadUtils.runOnUiThreadBlocking(() -> { ++ fragment.onPreferenceChange(historyInIncognitoPref, false); ++ ++ PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile()); ++ boolean incognito_tab_history_enabled = prefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED); ++ Assert.assertFalse(incognito_tab_history_enabled); ++ }); ++ } ++} +diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/Bromite_AppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/Bromite_AppMenuPropertiesDelegateUnitTest.java +new file mode 100644 +--- /dev/null ++++ b/chrome/android/junit/src/org/chromium/chrome/browser/app/appmenu/Bromite_AppMenuPropertiesDelegateUnitTest.java +@@ -0,0 +1,323 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite 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 Bromite. If not, see . ++*/ ++ ++package org.chromium.chrome.browser.app.appmenu; ++ ++import static org.mockito.ArgumentMatchers.any; ++import static org.mockito.ArgumentMatchers.anyBoolean; ++import static org.mockito.Mockito.doReturn; ++import static org.mockito.Mockito.when; ++ ++import android.content.Context; ++import android.view.Menu; ++import android.view.MenuItem; ++import android.view.SubMenu; ++import android.view.View; ++import android.widget.PopupMenu; ++ ++import org.hamcrest.Matchers; ++import org.junit.After; ++import org.junit.Assert; ++import org.junit.Before; ++import org.junit.Rule; ++import org.junit.Test; ++import org.junit.rules.TestRule; ++import org.junit.runner.RunWith; ++import org.mockito.Mock; ++import org.mockito.Mockito; ++import org.mockito.MockitoAnnotations; ++import org.robolectric.annotation.Config; ++ ++import org.chromium.base.ContextUtils; ++import org.chromium.base.FeatureList; ++import org.chromium.base.ThreadUtils; ++import org.chromium.base.supplier.ObservableSupplierImpl; ++import org.chromium.base.supplier.OneshotSupplierImpl; ++import org.chromium.base.test.BaseRobolectricTestRunner; ++import org.chromium.base.test.util.JniMocker; ++import org.chromium.chrome.R; ++import org.chromium.chrome.browser.ActivityTabProvider; ++import org.chromium.chrome.browser.app.appmenu.AppMenuPropertiesDelegateImpl.MenuGroup; ++import org.chromium.chrome.browser.bookmarks.BookmarkBridge; ++import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; ++import org.chromium.chrome.browser.device.DeviceConditions; ++import org.chromium.chrome.browser.device.ShadowDeviceConditions; ++import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher; ++import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; ++import org.chromium.chrome.browser.preferences.Pref; ++import org.chromium.chrome.browser.profiles.Profile; ++import org.chromium.chrome.browser.tab.Tab; ++import org.chromium.chrome.browser.tabmodel.TabModel; ++import org.chromium.chrome.browser.tabmodel.TabModelFilter; ++import org.chromium.chrome.browser.tabmodel.TabModelFilterProvider; ++import org.chromium.chrome.browser.tabmodel.TabModelSelector; ++import org.chromium.chrome.browser.toolbar.ToolbarManager; ++import org.chromium.chrome.browser.toolbar.menu_button.MenuUiState; ++import org.chromium.chrome.browser.util.ChromeAccessibilityUtil; ++import org.chromium.chrome.test.util.browser.Features; ++import org.chromium.components.prefs.PrefService; ++import org.chromium.components.user_prefs.UserPrefs; ++import org.chromium.components.user_prefs.UserPrefsJni; ++import org.chromium.components.webapps.AppBannerManager; ++import org.chromium.content.browser.ContentFeatureListImpl; ++import org.chromium.content.browser.ContentFeatureListImplJni; ++import org.chromium.content_public.browser.ContentFeatureList; ++import org.chromium.content_public.browser.NavigationController; ++import org.chromium.content_public.browser.WebContents; ++import org.chromium.net.ConnectionType; ++import org.chromium.url.JUnitTestGURLs; ++ ++import java.util.ArrayList; ++import java.util.List; ++ ++import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor; ++import android.content.SharedPreferences; ++ ++/** ++ * Unit tests for {@link AppMenuPropertiesDelegateImpl}. ++ */ ++@RunWith(BaseRobolectricTestRunner.class) ++public class Bromite_AppMenuPropertiesDelegateUnitTest { ++ @Rule ++ public TestRule mProcessor = new Features.JUnitProcessor(); ++ ++ @Rule ++ public JniMocker mJniMocker = new JniMocker(); ++ ++ @Mock ++ private ActivityTabProvider mActivityTabProvider; ++ @Mock ++ private Tab mTab; ++ @Mock ++ private WebContents mWebContents; ++ @Mock ++ private NavigationController mNavigationController; ++ @Mock ++ private MultiWindowModeStateDispatcher mMultiWindowModeStateDispatcher; ++ @Mock ++ private TabModelSelector mTabModelSelector; ++ @Mock ++ private TabModel mTabModel; ++ @Mock ++ private TabModel mIncognitoTabModel; ++ @Mock ++ private ToolbarManager mToolbarManager; ++ @Mock ++ private View mDecorView; ++ @Mock ++ private OverviewModeBehavior mOverviewModeBehavior; ++ @Mock ++ private UpdateMenuItemHelper mUpdateMenuItemHelper; ++ @Mock ++ private ContentFeatureListImpl.Natives mContentFeatureListJniMock; ++ @Mock ++ private UserPrefs.Natives mUserPrefsJniMock; ++ @Mock ++ private Profile mProfile; ++ @Mock ++ private PrefService mPrefService; ++ @Mock ++ private TabModelFilterProvider mTabModelFilterProvider; ++ @Mock ++ private TabModelFilter mTabModelFilter; ++ ++ private OneshotSupplierImpl mOverviewModeSupplier = ++ new OneshotSupplierImpl<>(); ++ private ObservableSupplierImpl mBookmarkBridgeSupplier = ++ new ObservableSupplierImpl<>(); ++ ++ private AppMenuPropertiesDelegateImpl mAppMenuPropertiesDelegate; ++ ++ private MenuUiState mMenuUiState; ++ ++ @Before ++ public void setUp() { ++ MockitoAnnotations.initMocks(this); ++ ++ mOverviewModeSupplier.set(mOverviewModeBehavior); ++ when(mTab.getWebContents()).thenReturn(mWebContents); ++ when(mWebContents.getNavigationController()).thenReturn(mNavigationController); ++ when(mNavigationController.getUseDesktopUserAgent()).thenReturn(false); ++ when(mTabModelSelector.getCurrentModel()).thenReturn(mTabModel); ++ when(mTabModelSelector.getModel(false)).thenReturn((mTabModel)); ++ when(mTabModelSelector.getModel(true)).thenReturn((mIncognitoTabModel)); ++ when(mTabModelSelector.getTabModelFilterProvider()).thenReturn(mTabModelFilterProvider); ++ when(mTabModelFilterProvider.getCurrentTabModelFilter()).thenReturn(mTabModelFilter); ++ when(mTabModelFilter.getTabModel()).thenReturn(mTabModel); ++ ++ UpdateMenuItemHelper.setInstanceForTesting(mUpdateMenuItemHelper); ++ mMenuUiState = new MenuUiState(); ++ doReturn(mMenuUiState).when(mUpdateMenuItemHelper).getUiState(); ++ ++ mJniMocker.mock(ContentFeatureListImplJni.TEST_HOOKS, mContentFeatureListJniMock); ++ mJniMocker.mock(UserPrefsJni.TEST_HOOKS, mUserPrefsJniMock); ++ Profile.setLastUsedProfileForTesting(mProfile); ++ Mockito.when(mUserPrefsJniMock.get(mProfile)).thenReturn(mPrefService); ++ FeatureList.setTestCanUseDefaultsForTesting(); ++ ++ AppMenuPropertiesDelegateImpl obj = new AppMenuPropertiesDelegateImpl( ++ ContextUtils.getApplicationContext(), mActivityTabProvider, ++ mMultiWindowModeStateDispatcher, mTabModelSelector, mToolbarManager, mDecorView, ++ mOverviewModeSupplier, mBookmarkBridgeSupplier); ++ obj.setPrefForTest(mPrefService); ++ mAppMenuPropertiesDelegate = Mockito.spy(obj); ++ } ++ ++ @After ++ public void tearDown() { ++ ThreadUtils.setThreadAssertsDisabledForTesting(false); ++ ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(false); ++ ChromeAccessibilityUtil.get().setTouchExplorationEnabledForTesting(false); ++ } ++ ++ private void setUpTestPrefs(SharedPreferences prefs, boolean always_incognito) { ++ SharedPreferences.Editor editor = prefs.edit(); ++ editor.putBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, always_incognito); ++ editor.apply(); ++ } ++ ++ @Test ++ public void testOverviewMenuItems_HideNewTabInAlwaysIncognito() { ++ SharedPreferences mPrefs = ContextUtils.getAppSharedPreferences(); ++ setUpTestPrefs(mPrefs, /*always_incognito*/true); ++ ++ setUpMocksForPageMenu(); ++ when(mTab.getUrl()).thenReturn(JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL)); ++ ++ Menu menu = createTestMenu(); ++ mAppMenuPropertiesDelegate.prepareMenu(menu, null); ++ ++ // in always incognito, open new tab is NOT visible ++ final MenuItem newTabOption = menu.findItem(R.id.new_tab_menu_id); ++ Assert.assertFalse(newTabOption.isVisible()); ++ } ++ ++ @Test ++ public void testOverviewMenuItems_ShowHomeScreenMenuItemInAlwaysIncognito() { ++ SharedPreferences mPrefs = ContextUtils.getAppSharedPreferences(); ++ setUpTestPrefs(mPrefs, /*always_incognito*/true); ++ ++ setUpMocksForPageMenu(); ++ when(mTab.getUrl()).thenReturn(JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL)); ++ ++ Menu menu = createTestMenu(); ++ mAppMenuPropertiesDelegate.prepareMenu(menu, null); ++ ++ // in always incognito, home screen menu is visible ++ final MenuItem homescreenItem = menu.findItem(R.id.add_to_homescreen_id); ++ Assert.assertTrue(homescreenItem.isVisible()); ++ } ++ ++ @Test ++ public void testOverviewMenuItems_HideRecentTabsInAlwaysIncognito() { ++ SharedPreferences mPrefs = ContextUtils.getAppSharedPreferences(); ++ setUpTestPrefs(mPrefs, /*always_incognito*/true); ++ ++ setUpMocksForPageMenu(); ++ when(mTab.getUrl()).thenReturn(JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL)); ++ ++ when(mPrefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED)) ++ .thenReturn(false); ++ ++ Menu menu = createTestMenu(); ++ mAppMenuPropertiesDelegate.prepareMenu(menu, null); ++ ++ // in always incognito, without INCOGNITO_TAB_HISTORY_ENABLED, ++ // recent tabs menu tab is NOT visible ++ final MenuItem recent_tabs_menu = menu.findItem(R.id.recent_tabs_menu_id); ++ Assert.assertFalse(recent_tabs_menu.isVisible()); ++ } ++ ++ @Test ++ public void testOverviewMenuItems_ShowRecentTabsInAlwaysIncognito() { ++ SharedPreferences mPrefs = ContextUtils.getAppSharedPreferences(); ++ setUpTestPrefs(mPrefs, /*always_incognito*/true); ++ ++ setUpMocksForPageMenu(); ++ when(mTab.getUrl()).thenReturn(JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL)); ++ ++ when(mPrefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED)) ++ .thenReturn(true); ++ ++ Menu menu = createTestMenu(); ++ mAppMenuPropertiesDelegate.prepareMenu(menu, null); ++ ++ // in always incognito, with INCOGNITO_TAB_HISTORY_ENABLED ++ // recent tabs menu item is visible ++ final MenuItem recent_tabs_menu = menu.findItem(R.id.recent_tabs_menu_id); ++ Assert.assertTrue(recent_tabs_menu.isVisible()); ++ } ++ ++ @Test ++ public void testOverviewMenuItems_ShowDownloadItemInAlwaysIncognito() { ++ SharedPreferences mPrefs = ContextUtils.getAppSharedPreferences(); ++ setUpTestPrefs(mPrefs, /*always_incognito*/true); ++ ++ setUpMocksForPageMenu(); ++ when(mTab.getUrl()).thenReturn(JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL)); ++ ++ when(mPrefService.getBoolean(Pref.INCOGNITO_TAB_HISTORY_ENABLED)) ++ .thenReturn(true); ++ ++ Menu menu = createTestMenu(); ++ mAppMenuPropertiesDelegate.prepareMenu(menu, null); ++ ++ // in always incognito, with INCOGNITO_TAB_HISTORY_ENABLED ++ // download page menu item is visible ++ final MenuItem offline_page = menu.findItem(R.id.offline_page_id); ++ Assert.assertTrue(offline_page.isVisible()); ++ } ++ ++ private void setUpMocksForPageMenu() { ++ when(mTabModel.isIncognito()).thenReturn(true); ++ when(mIncognitoTabModel.isIncognito()).thenReturn(true); ++ ++ when(mActivityTabProvider.get()).thenReturn(mTab); ++ when(mOverviewModeBehavior.overviewVisible()).thenReturn(false); ++ doReturn(false).when(mAppMenuPropertiesDelegate).shouldCheckBookmarkStar(any(Tab.class)); ++ doReturn(false).when(mAppMenuPropertiesDelegate).shouldEnableDownloadPage(any(Tab.class)); ++ doReturn(false).when(mAppMenuPropertiesDelegate).shouldShowReaderModePrefs(any(Tab.class)); ++ doReturn(false) ++ .when(mAppMenuPropertiesDelegate) ++ .shouldShowManagedByMenuItem(any(Tab.class)); ++ doReturn(false) ++ .when(mAppMenuPropertiesDelegate) ++ .shouldShowTranslateMenuItem(any(Tab.class)); ++ doReturn(new AppBannerManager.InstallStringPair( ++ R.string.menu_add_to_homescreen, R.string.add)) ++ .when(mAppMenuPropertiesDelegate) ++ .getAddToHomeScreenTitle(mTab); ++ setUpIncognitoMocks(); ++ } ++ ++ private void setUpMocksForOverviewMenu() { ++ when(mOverviewModeBehavior.overviewVisible()).thenReturn(true); ++ when(mTabModelSelector.getTotalTabCount()).thenReturn(1); ++ setUpIncognitoMocks(); ++ } ++ ++ private void setUpIncognitoMocks() { ++ doReturn(true).when(mAppMenuPropertiesDelegate).isIncognitoEnabled(); ++ } ++ ++ private Menu createTestMenu() { ++ PopupMenu tempMenu = new PopupMenu(ContextUtils.getApplicationContext(), mDecorView); ++ tempMenu.inflate(mAppMenuPropertiesDelegate.getAppMenuLayoutId()); ++ Menu menu = tempMenu.getMenu(); ++ return menu; ++ } ++} +diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tab/HistoricalTabSaverUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tab/HistoricalTabSaverUnitTest.java +--- a/chrome/android/junit/src/org/chromium/chrome/browser/tab/HistoricalTabSaverUnitTest.java ++++ b/chrome/android/junit/src/org/chromium/chrome/browser/tab/HistoricalTabSaverUnitTest.java +@@ -61,9 +61,9 @@ public class HistoricalTabSaverUnitTest { + doReturn(false).when(mTabImplMock).isFrozen(); + doReturn(mWebContentsMock).when(mTabImplMock).getWebContents(); + +- HistoricalTabSaver.createHistoricalTab(mTabImplMock); ++ HistoricalTabSaver.createHistoricalTab(mTabImplMock, false); + +- verify(mHistoricalTabSaverJni).createHistoricalTabFromContents(eq(mWebContentsMock)); ++ verify(mHistoricalTabSaverJni).createHistoricalTabFromContents(eq(mWebContentsMock), eq(false)); + } + + @Test +@@ -71,9 +71,9 @@ public class HistoricalTabSaverUnitTest { + doReturn(true).when(mTabImplMock).isFrozen(); + doReturn(null).when(mCriticalPersistedTabData).getWebContentsState(); + +- HistoricalTabSaver.createHistoricalTab(mTabImplMock); ++ HistoricalTabSaver.createHistoricalTab(mTabImplMock, false); + +- verify(mHistoricalTabSaverJni, never()).createHistoricalTabFromContents(any()); ++ verify(mHistoricalTabSaverJni, never()).createHistoricalTabFromContents(any(), eq(false)); + } + + @Test +@@ -88,9 +88,9 @@ public class HistoricalTabSaverUnitTest { + .when(mWebContentsStateBridgeJni) + .restoreContentsFromByteBuffer(eq(buffer), eq(123), eq(true)); + +- HistoricalTabSaver.createHistoricalTab(mTabImplMock); ++ HistoricalTabSaver.createHistoricalTab(mTabImplMock, false); + +- verify(mHistoricalTabSaverJni, never()).createHistoricalTabFromContents(any()); ++ verify(mHistoricalTabSaverJni, never()).createHistoricalTabFromContents(any(), eq(false)); + } + + @Test +@@ -105,8 +105,8 @@ public class HistoricalTabSaverUnitTest { + .when(mWebContentsStateBridgeJni) + .restoreContentsFromByteBuffer(eq(buffer), eq(123), eq(true)); + +- HistoricalTabSaver.createHistoricalTab(mTabImplMock); ++ HistoricalTabSaver.createHistoricalTab(mTabImplMock, false); + +- verify(mHistoricalTabSaverJni).createHistoricalTabFromContents(eq(mWebContentsMock)); ++ verify(mHistoricalTabSaverJni).createHistoricalTabFromContents(eq(mWebContentsMock), eq(false)); + } + } +diff --git a/chrome/browser/android/historical_tab_saver.cc b/chrome/browser/android/historical_tab_saver.cc +--- a/chrome/browser/android/historical_tab_saver.cc ++++ b/chrome/browser/android/historical_tab_saver.cc +@@ -12,16 +12,27 @@ + #include "components/sessions/core/tab_restore_service.h" + #include "content/public/browser/web_contents.h" + ++#include "chrome/common/pref_names.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "historical_tab_saver.h" ++ + using base::android::JavaParamRef; + + namespace { + +-void CreateHistoricalTab(content::WebContents* web_contents) { ++void CreateHistoricalTab(content::WebContents* web_contents, ++ bool is_always_incognito) { + DCHECK(web_contents); + ++ auto* profile = Profile::FromBrowserContext(web_contents->GetBrowserContext()); ++ if (is_always_incognito) { ++ if(profile->GetOriginalProfile()->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled)) ++ profile = profile->GetOriginalProfile(); ++ } ++ + sessions::TabRestoreService* service = +- TabRestoreServiceFactory::GetForProfile( +- Profile::FromBrowserContext(web_contents->GetBrowserContext())); ++ TabRestoreServiceFactory::GetForProfile(profile); + if (!service) + return; + +@@ -41,11 +52,18 @@ void CreateHistoricalTab(content::WebContents* web_contents) { + + // Static JNI methods. + ++// static ++void CreateHistoricalTabForTest(content::WebContents* web_contents, ++ bool is_always_incognito) { ++ CreateHistoricalTab(web_contents, is_always_incognito); ++} ++ + // static + static void JNI_HistoricalTabSaver_CreateHistoricalTabFromContents( + JNIEnv* env, +- const JavaParamRef& jweb_contents) { ++ const JavaParamRef& jweb_contents, ++ jboolean is_always_incognito) { + auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents); + if (web_contents) +- CreateHistoricalTab(web_contents); ++ CreateHistoricalTab(web_contents, is_always_incognito); + } +diff --git a/chrome/browser/android/historical_tab_saver.h b/chrome/browser/android/historical_tab_saver.h +new file mode 100644 +--- /dev/null ++++ b/chrome/browser/android/historical_tab_saver.h +@@ -0,0 +1,27 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite 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 Bromite. If not, see . ++*/ ++ ++#ifndef CHROME_BROWSER_ANDROID_HISTORICAL_TAB_SAVER_H_ ++#define CHROME_BROWSER_ANDROID_HISTORICAL_TAB_SAVER_H_ ++ ++#include "content/public/browser/web_contents.h" ++ ++// static ++void CreateHistoricalTabForTest(content::WebContents* web_contents, ++ bool is_always_incognito); ++ ++#endif // CHROME_BROWSER_ANDROID_HISTORICAL_TAB_SAVER_H_ +diff --git a/chrome/browser/android/historical_tab_saver_unittest.cc b/chrome/browser/android/historical_tab_saver_unittest.cc +new file mode 100644 +--- /dev/null ++++ b/chrome/browser/android/historical_tab_saver_unittest.cc +@@ -0,0 +1,148 @@ ++/* ++ This file is part of Bromite. ++ ++ Bromite is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ Bromite 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 Bromite. If not, see . ++*/ ++ ++#include "base/run_loop.h" ++#include "base/strings/stringprintf.h" ++#include "base/strings/utf_string_conversions.h" ++#include "base/task/cancelable_task_tracker.h" ++#include "base/test/bind.h" ++#include "build/build_config.h" ++#include "chrome/browser/history/history_service_factory.h" ++#include "chrome/test/base/chrome_render_view_host_test_harness.h" ++#include "chrome/test/base/testing_profile.h" ++#include "components/history/core/browser/history_constants.h" ++#include "components/history/core/browser/history_service.h" ++#include "components/history/core/browser/history_types.h" ++#include "components/history/core/browser/url_row.h" ++#include "content/public/browser/browser_context.h" ++#include "content/public/browser/navigation_controller.h" ++#include "content/public/browser/web_contents.h" ++#include "content/public/test/mock_navigation_handle.h" ++#include "content/public/test/web_contents_tester.h" ++#include "testing/gmock/include/gmock/gmock.h" ++#include "testing/gtest/include/gtest/gtest.h" ++#include "ui/base/page_transition_types.h" ++ ++#include "chrome/test/base/testing_browser_process.h" ++#include "chrome/test/base/testing_profile_manager.h" ++#include "content/public/test/test_browser_context.h" ++#include "content/test/test_web_contents.h" ++#include "components/sessions/core/tab_restore_service.h" ++#include "components/sessions/core/tab_restore_service_impl.h" ++#include "components/sessions/content/content_live_tab.h" ++#include "chrome/browser/android/historical_tab_saver.h" ++#include "chrome/browser/sessions/tab_restore_service_factory.h" ++#include "chrome/browser/sessions/chrome_tab_restore_service_client.h" ++#include "chrome/common/pref_names.h" ++ ++namespace { ++ ++static std::unique_ptr GetTabRestoreService( ++ content::BrowserContext* browser_context) { ++ return std::make_unique( ++ std::make_unique( ++ Profile::FromBrowserContext(browser_context)), ++ nullptr, nullptr); ++} ++ ++// --gtest_filter=*.Bromite* ++class HistoryTabSaverTest : public ChromeRenderViewHostTestHarness { ++ protected: ++ HistoryTabSaverTest() {} ++ ++ // ChromeRenderViewHostTestHarness: ++ void SetUp() override { ++ ChromeRenderViewHostTestHarness::SetUp(); ++ TabRestoreServiceFactory::GetInstance()->SetTestingFactory( ++ profile(), ++ base::BindRepeating(&GetTabRestoreService)); ++ ASSERT_TRUE(profile()->CreateHistoryService()); ++ } ++ ++ const GURL page_url_ = GURL("http://foo.com"); ++ ++ protected: ++// history::HistoryService* history_service_; ++ ++ DISALLOW_COPY_AND_ASSIGN(HistoryTabSaverTest); ++}; ++ ++TEST_F(HistoryTabSaverTest, Bromite_HistoricalTabForNormalNavigation) { ++ sessions::TabRestoreService* service = ++ TabRestoreServiceFactory::GetForProfile(profile()); ++ ASSERT_NE(nullptr, service); ++ ++ auto* profile_ = profile(); ++ auto web_other_contents = content::TestWebContents::Create(profile_, nullptr); ++ web_other_contents->NavigateAndCommit(page_url_); ++ ++ // Make sure was no entry. ++ ASSERT_EQ(0U, service->entries().size()); ++ ++ CreateHistoricalTabForTest(web_other_contents.get(), /*is_always_incognito*/false); ++ ++ // Make sure entry was created. ++ ASSERT_EQ(1U, service->entries().size()); ++} ++ ++TEST_F(HistoryTabSaverTest, Bromite_HistoricalTabForNavigationInIncognito) { ++ sessions::TabRestoreService* service = ++ TabRestoreServiceFactory::GetForProfile(profile()); ++ ASSERT_NE(nullptr, service); ++ ++ auto* profile_ = profile()->GetOffTheRecordProfile( ++ Profile::OTRProfileID::PrimaryID(), /*create_if_needed=*/true); ++ auto web_other_contents = content::TestWebContents::Create(profile_, nullptr); ++ web_other_contents->NavigateAndCommit(page_url_); ++ ++ // Make sure was no entry. ++ ASSERT_EQ(0U, service->entries().size()); ++ ++ CreateHistoricalTabForTest(web_other_contents.get(), /*is_always_incognito*/false); ++ ++ // Make sure was no entry. ++ ASSERT_EQ(0U, service->entries().size()); ++} ++ ++TEST_F(HistoryTabSaverTest, Bromite_HistoricalTabForNavigationInIncognito_AlwaysIncognito) { ++ sessions::TabRestoreService* service = ++ TabRestoreServiceFactory::GetForProfile(profile()); ++ ASSERT_NE(nullptr, service); ++ ++ auto* profile_ = profile()->GetOffTheRecordProfile( ++ Profile::OTRProfileID::PrimaryID(), /*create_if_needed=*/true); ++ auto web_other_contents = content::TestWebContents::Create(profile_, nullptr); ++ web_other_contents->NavigateAndCommit(page_url_); ++ ++ // test kIncognitoTabHistoryEnabled with false ++ profile()->GetPrefs()->SetBoolean(prefs::kIncognitoTabHistoryEnabled, false); ++ ++ CreateHistoricalTabForTest(web_other_contents.get(), /*is_always_incognito*/true); ++ ++ // Make sure was no entry. ++ ASSERT_EQ(0U, service->entries().size()); ++ ++ // test kIncognitoTabHistoryEnabled with true ++ profile()->GetPrefs()->SetBoolean(prefs::kIncognitoTabHistoryEnabled, true); ++ ++ CreateHistoricalTabForTest(web_other_contents.get(), /*is_always_incognito*/true); ++ ++ // Make sure was the entry. ++ ASSERT_EQ(1U, service->entries().size()); ++} ++ ++} // namespace diff --git a/chrome/browser/history/history_tab_helper.cc b/chrome/browser/history/history_tab_helper.cc --- a/chrome/browser/history/history_tab_helper.cc +++ b/chrome/browser/history/history_tab_helper.cc @@ -102,9 +1281,9 @@ diff --git a/chrome/browser/history/history_tab_helper.cc b/chrome/browser/histo Profile::FromBrowserContext(web_contents()->GetBrowserContext()); + +#if defined(OS_ANDROID) -+ if(profile->GetOriginalProfile()->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled)) { -+ return HistoryServiceFactory::GetForProfile(profile, ServiceAccessType::IMPLICIT_ACCESS); -+ } ++ if(profile->GetOriginalProfile()->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled)) { ++ return HistoryServiceFactory::GetForProfile(profile, ServiceAccessType::IMPLICIT_ACCESS); ++ } +#endif + if (profile->IsOffTheRecord()) @@ -135,15 +1314,258 @@ diff --git a/chrome/browser/history/history_tab_helper.h b/chrome/browser/histor namespace history { struct HistoryAddPageArgs; -@@ -39,6 +41,8 @@ class HistoryTabHelper : public content::WebContentsObserver, +@@ -39,6 +41,11 @@ class HistoryTabHelper : public content::WebContentsObserver, force_eligibile_tab_for_testing_ = force; } + static void RegisterProfilePrefs(PrefRegistrySimple* registry); ++ ++ // Helper function to return the history service. May return null. ++ history::HistoryService* GetHistoryService(); + private: explicit HistoryTabHelper(content::WebContents* web_contents); friend class content::WebContentsUserData; +@@ -53,9 +60,6 @@ class HistoryTabHelper : public content::WebContentsObserver, + void TitleWasSet(content::NavigationEntry* entry) override; + void WebContentsDestroyed() override; + +- // Helper function to return the history service. May return null. +- history::HistoryService* GetHistoryService(); +- + // Returns true if our observed web contents is an eligible tab. + bool IsEligibleTab(const history::HistoryAddPageArgs& add_page_args) const; + +diff --git a/chrome/browser/history/history_tab_helper_unittest.cc b/chrome/browser/history/history_tab_helper_unittest.cc +--- a/chrome/browser/history/history_tab_helper_unittest.cc ++++ b/chrome/browser/history/history_tab_helper_unittest.cc +@@ -32,6 +32,9 @@ + #include "components/feed/core/v2/public/test/stub_feed_api.h" + #endif + ++#include "chrome/common/pref_names.h" ++#include "content/test/test_web_contents.h" ++ + namespace { + + #if defined(OS_ANDROID) +@@ -264,4 +267,23 @@ TEST_F(HistoryTabHelperTest, FeedNavigationsDoNotContributeToMostVisited) { + + #endif + ++TEST_F(HistoryTabHelperTest, Bromite_CheckHistoryService_IncognitoTabHistoryEnabled) { ++ auto* profile_ = profile()->GetOffTheRecordProfile( ++ Profile::OTRProfileID::PrimaryID(), /*create_if_needed=*/true); ++ auto web_other_contents = content::TestWebContents::Create(profile_, nullptr); ++ web_other_contents->NavigateAndCommit(page_url_); ++ ++ HistoryTabHelper::CreateForWebContents(web_other_contents.get()); ++ HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(web_other_contents.get()); ++ ASSERT_NE(nullptr, history_tab_helper); ++ ++ // test kIncognitoTabHistoryEnabled with false ++ profile()->GetPrefs()->SetBoolean(prefs::kIncognitoTabHistoryEnabled, false); ++ ASSERT_EQ(nullptr, history_tab_helper->GetHistoryService()); ++ ++ // test kIncognitoTabHistoryEnabled with true ++ profile()->GetPrefs()->SetBoolean(prefs::kIncognitoTabHistoryEnabled, true); ++ ASSERT_NE(nullptr, history_tab_helper->GetHistoryService()); ++} ++ + } // namespace +diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc +--- a/chrome/browser/offline_pages/android/offline_page_bridge.cc ++++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc +@@ -49,6 +49,9 @@ + #include "content/public/browser/web_contents.h" + #include "net/base/filename_util.h" + #include "url/android/gurl_android.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" + + using base::android::ConvertJavaStringToUTF8; + using base::android::ConvertUTF16ToJavaString; +@@ -158,7 +161,13 @@ void ValidateFileCallback( + int64_t offline_id, + const GURL& url, + const base::FilePath& file_path, ++ SimpleFactoryKey* key, + bool is_trusted) { ++ // in always incognito, never trust input file (show file name in url) ++ ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key); ++ if(profile_key->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled)) ++ is_trusted = false; ++ + // If trusted, the launch url will be the http/https url of the offline + // page. If the file path is content URI, directly open it. Otherwise, the + // launch url will be the file URL pointing to the archive file of the offline +@@ -785,7 +794,7 @@ void OfflinePageBridge::GetPageByOfflineIdDone( + if (offline_page_model_->IsArchiveInInternalDir(offline_page->file_path)) { + ValidateFileCallback(launch_location, j_callback_obj, + offline_page->offline_id, offline_page->url, +- offline_page->file_path, true /* is_trusted*/); ++ offline_page->file_path, key_, true /* is_trusted*/); + return; + } + +@@ -795,7 +804,7 @@ void OfflinePageBridge::GetPageByOfflineIdDone( + offline_page->file_size, offline_page->digest), + base::BindOnce(&ValidateFileCallback, launch_location, j_callback_obj, + offline_page->offline_id, offline_page->url, +- offline_page->file_path)); ++ offline_page->file_path, key_)); + } + + void OfflinePageBridge::GetSizeAndComputeDigestDone( +diff --git a/chrome/browser/offline_pages/android/offline_page_model_factory.cc b/chrome/browser/offline_pages/android/offline_page_model_factory.cc +--- a/chrome/browser/offline_pages/android/offline_page_model_factory.cc ++++ b/chrome/browser/offline_pages/android/offline_page_model_factory.cc +@@ -26,6 +26,9 @@ + #include "components/keyed_service/core/simple_dependency_manager.h" + #include "components/offline_pages/core/model/offline_page_model_taskified.h" + #include "components/offline_pages/core/offline_page_metadata_store.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" + + namespace offline_pages { + +@@ -48,6 +51,7 @@ OfflinePageModel* OfflinePageModelFactory::GetForKey(SimpleFactoryKey* key) { + OfflinePageModel* OfflinePageModelFactory::GetForBrowserContext( + content::BrowserContext* browser_context) { + Profile* profile = Profile::FromBrowserContext(browser_context); ++ profile = profile->GetOriginalProfile(); + return GetForKey(profile->GetProfileKey()); + } + +@@ -56,13 +60,15 @@ std::unique_ptr OfflinePageModelFactory::BuildServiceInstanceFor( + scoped_refptr background_task_runner = + base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}); + ++ ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key)->GetOriginalKey(); ++ + base::FilePath store_path = +- key->GetPath().Append(chrome::kOfflinePageMetadataDirname); ++ profile_key->GetPath().Append(chrome::kOfflinePageMetadataDirname); + std::unique_ptr metadata_store( + new OfflinePageMetadataStore(background_task_runner, store_path)); + + base::FilePath persistent_archives_dir = +- key->GetPath().Append(chrome::kOfflinePageArchivesDirname); ++ profile_key->GetPath().Append(chrome::kOfflinePageArchivesDirname); + // If base::PathService::Get returns false, the temporary_archives_dir will be + // empty, and no temporary pages will be saved during this chrome lifecycle. + base::FilePath temporary_archives_dir; +@@ -71,7 +77,6 @@ std::unique_ptr OfflinePageModelFactory::BuildServiceInstanceFor( + temporary_archives_dir.Append(chrome::kOfflinePageArchivesDirname); + } + +- ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key); + auto archive_manager = std::make_unique( + temporary_archives_dir, persistent_archives_dir, + DownloadPrefs::GetDefaultDownloadDirectory(), background_task_runner, +@@ -91,4 +96,14 @@ std::unique_ptr OfflinePageModelFactory::BuildServiceInstanceFor( + return model; + } + ++SimpleFactoryKey* OfflinePageModelFactory::GetKeyToUse( ++ SimpleFactoryKey* key) const { ++ ProfileKey* profile_key = ProfileKey::FromSimpleFactoryKey(key); ++ if(profile_key->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled) == false) { ++ return SimpleKeyedServiceFactory::GetKeyToUse(key); ++ } else { ++ return profile_key->GetOriginalKey(); ++ } ++} ++ + } // namespace offline_pages +diff --git a/chrome/browser/offline_pages/android/request_coordinator_factory.cc b/chrome/browser/offline_pages/android/request_coordinator_factory.cc +--- a/chrome/browser/offline_pages/android/request_coordinator_factory.cc ++++ b/chrome/browser/offline_pages/android/request_coordinator_factory.cc +@@ -29,6 +29,11 @@ + #include "components/offline_pages/core/offline_page_feature.h" + #include "content/public/browser/web_contents.h" + ++#include "chrome/browser/profiles/incognito_helpers.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" ++ + namespace network { + class NetworkQualityTracker; + } +@@ -114,4 +119,16 @@ KeyedService* RequestCoordinatorFactory::BuildServiceInstanceFor( + return request_coordinator; + } + ++content::BrowserContext* ++RequestCoordinatorFactory::GetBrowserContextToUse( ++ content::BrowserContext* context) const { ++ if(Profile::FromBrowserContext(context)->GetOriginalProfile() ++ ->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled) == false) { ++ return BrowserContextKeyedServiceFactory::GetBrowserContextToUse(context); ++ } else { ++ return chrome::GetBrowserContextRedirectedInIncognito(context); ++ } ++} ++ ++ + } // namespace offline_pages +diff --git a/chrome/browser/offline_pages/offline_page_model_factory.h b/chrome/browser/offline_pages/offline_page_model_factory.h +--- a/chrome/browser/offline_pages/offline_page_model_factory.h ++++ b/chrome/browser/offline_pages/offline_page_model_factory.h +@@ -47,6 +47,8 @@ class OfflinePageModelFactory : public SimpleKeyedServiceFactory { + std::unique_ptr BuildServiceInstanceFor( + SimpleFactoryKey* key) const override; + ++ SimpleFactoryKey* GetKeyToUse(SimpleFactoryKey* key) const override; ++ + DISALLOW_COPY_AND_ASSIGN(OfflinePageModelFactory); + }; + +diff --git a/chrome/browser/offline_pages/recent_tab_helper.cc b/chrome/browser/offline_pages/recent_tab_helper.cc +--- a/chrome/browser/offline_pages/recent_tab_helper.cc ++++ b/chrome/browser/offline_pages/recent_tab_helper.cc +@@ -30,6 +30,11 @@ + #include "content/public/browser/navigation_entry.h" + #include "content/public/browser/navigation_handle.h" + ++#include "chrome/browser/profiles/profile.h" ++#include "components/prefs/pref_registry_simple.h" ++#include "components/prefs/pref_service.h" ++#include "chrome/common/pref_names.h" ++ + namespace { + class DefaultRecentTabHelperDelegate + : public offline_pages::RecentTabHelper::Delegate { +@@ -181,6 +186,13 @@ bool RecentTabHelper::EnsureInitialized() { + // WebContents with its origin as well. + snapshots_enabled_ = !tab_id_.empty() && + !web_contents()->GetBrowserContext()->IsOffTheRecord(); ++ if(!tab_id_.empty() && web_contents()->GetBrowserContext()->IsOffTheRecord()) { ++ if(Profile::FromBrowserContext(web_contents()->GetBrowserContext()) ++ ->GetOriginalProfile() ++ ->GetPrefs()->GetBoolean(prefs::kIncognitoTabHistoryEnabled) == true) { ++ snapshots_enabled_ = true; ++ } ++ } + + if (snapshots_enabled_) { + page_model_ = OfflinePageModelFactory::GetForBrowserContext( +diff --git a/chrome/browser/offline_pages/request_coordinator_factory.h b/chrome/browser/offline_pages/request_coordinator_factory.h +--- a/chrome/browser/offline_pages/request_coordinator_factory.h ++++ b/chrome/browser/offline_pages/request_coordinator_factory.h +@@ -34,6 +34,9 @@ class RequestCoordinatorFactory : public BrowserContextKeyedServiceFactory { + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + ++ content::BrowserContext* GetBrowserContextToUse( ++ content::BrowserContext* context) const override; ++ + DISALLOW_COPY_AND_ASSIGN(RequestCoordinatorFactory); + }; + diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc @@ -164,6 +1586,84 @@ diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browse feed::RegisterProfilePrefs(registry); #else // defined(OS_ANDROID) AppShortcutManager::RegisterProfilePrefs(registry); +diff --git a/chrome/browser/ui/android/native_page/BUILD.gn b/chrome/browser/ui/android/native_page/BUILD.gn +--- a/chrome/browser/ui/android/native_page/BUILD.gn ++++ b/chrome/browser/ui/android/native_page/BUILD.gn +@@ -31,8 +31,10 @@ java_library("junit") { + + deps = [ + ":java", ++ "//base:base_java", + "//base:base_junit_test_support", + "//third_party/android_deps:robolectric_all_java", + "//third_party/junit", ++ "//components/embedder_support/android:util_java", + ] + } +diff --git a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java +--- a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java ++++ b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePage.java +@@ -16,6 +16,8 @@ import org.chromium.url.GURL; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + ++import org.chromium.base.ContextUtils; ++ + /** + * An interface for pages that will be using Android views instead of html/rendered Web content. + */ +@@ -156,7 +158,9 @@ public interface NativePage { + return NativePageType.DOWNLOADS; + } else if (UrlConstants.HISTORY_HOST.equals(host)) { + return NativePageType.HISTORY; +- } else if (UrlConstants.RECENT_TABS_HOST.equals(host) && !isIncognito) { ++ } else if (UrlConstants.RECENT_TABS_HOST.equals(host) && ++ (!isIncognito || ++ ContextUtils.getAppSharedPreferences().getBoolean("always_incognito", false))) { + return NativePageType.RECENT_TABS; + } else if (UrlConstants.EXPLORE_HOST.equals(host)) { + return NativePageType.EXPLORE; +diff --git a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePageTest.java b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePageTest.java +--- a/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePageTest.java ++++ b/chrome/browser/ui/android/native_page/java/src/org/chromium/chrome/browser/ui/native_page/NativePageTest.java +@@ -12,6 +12,10 @@ import org.robolectric.annotation.Config; + import org.chromium.base.test.BaseRobolectricTestRunner; + import org.chromium.chrome.browser.ui.native_page.NativePage.NativePageType; + ++import org.chromium.base.ContextUtils; ++import org.chromium.components.embedder_support.util.UrlConstants; ++import android.content.SharedPreferences; ++ + /** + * Tests public methods in NativePage. + */ +@@ -91,4 +95,26 @@ public class NativePageTest { + Assert.assertFalse(invalidUrl, NativePage.isNativePageUrl(invalidUrl, true)); + } + } ++ ++ @Test ++ public void Bromite_testNativePage_RecentTabsInAlwaysIncognito() { ++ SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); ++ SharedPreferences.Editor editor = prefs.edit(); ++ ++ String url = "chrome-native://recent-tabs"; ++ ++ editor.putBoolean(/*AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO*/ ++ "always_incognito", false); ++ editor.apply(); ++ ++ Assert.assertEquals(NativePageType.NONE, ++ NativePage.nativePageType(url, null, /*isIncognito*/true)); ++ ++ editor.putBoolean(/*AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO*/ ++ "always_incognito", true); ++ editor.apply(); ++ ++ Assert.assertEquals(NativePageType.RECENT_TABS, ++ NativePage.nativePageType(url, null, /*isIncognito*/true)); ++ } + } diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd --- a/chrome/browser/ui/android/strings/android_chrome_strings.grd +++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd @@ -209,6 +1709,21 @@ diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h } // namespace prefs #endif // CHROME_COMMON_PREF_NAMES_H_ +diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn +--- a/chrome/test/BUILD.gn ++++ b/chrome/test/BUILD.gn +@@ -4638,6 +4638,11 @@ test("unit_tests") { + "../../base/path_service_unittest.cc", + ] + ++ # Bromite tests ++ sources += [ ++ "../browser/android/historical_tab_saver_unittest.cc", ++ ] ++ + if (is_android) { + sources += [ + "../browser/android/bookmarks/partner_bookmarks_shim_unittest.cc", -- 2.17.1