diff --git a/Add-Always-Incognito-option.patch b/Add-Always-Incognito-option.patch new file mode 100644 index 00000000..f4e02b18 --- /dev/null +++ b/Add-Always-Incognito-option.patch @@ -0,0 +1,267 @@ +From: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Wed, 2 May 2018 01:24:39 +0200 +Subject: Add Always Incognito option + +--- + chrome/android/java/res/menu/custom_tabs_menu.xml | 12 ++++++++ + chrome/android/java/res/menu/main_menu.xml | 12 ++++++++ + .../chromium/chrome/browser/ChromeActivity.java | 4 +++ + .../chrome/browser/ChromeTabbedActivity.java | 4 +++ + .../org/chromium/chrome/browser/IntentHandler.java | 8 ++--- + .../chrome/browser/LaunchIntentDispatcher.java | 7 +++++ + .../browser/appmenu/AppMenuPropertiesDelegate.java | 35 ++++++++++++++++++++++ + .../CustomTabAppMenuPropertiesDelegate.java | 2 ++ + .../browser/preferences/PrefServiceBridge.java | 17 +++++++++++ + .../java/strings/android_chrome_strings.grd | 10 +++++++ + 10 files changed, 106 insertions(+), 5 deletions(-) + +diff --git a/chrome/android/java/res/menu/custom_tabs_menu.xml b/chrome/android/java/res/menu/custom_tabs_menu.xml +--- a/chrome/android/java/res/menu/custom_tabs_menu.xml ++++ b/chrome/android/java/res/menu/custom_tabs_menu.xml +@@ -62,6 +62,18 @@ + + + ++ ++ ++ ++ ++ ++ ++ + +diff --git a/chrome/android/java/res/menu/main_menu.xml b/chrome/android/java/res/menu/main_menu.xml +--- a/chrome/android/java/res/menu/main_menu.xml ++++ b/chrome/android/java/res/menu/main_menu.xml +@@ -72,6 +72,18 @@ + + + ++ ++ ++ ++ ++ ++ ++ + + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +@@ -1905,6 +1905,10 @@ public abstract class ChromeActivity extends AsyncInitializationActivity + RecordUserAction.record("MobileMenuSettings"); + } else if (id == R.id.show_menu) { + showAppMenuForKeyboardEvent(); ++ } else if (id == R.id.enable_always_incognito_id || id == R.id.enable_always_incognito_check_id) { ++ final boolean alwaysIncognito = PrefServiceBridge.getInstance().alwaysIncognitoEnabled(); ++ PrefServiceBridge.getInstance().setAlwaysIncognitoEnabled(!alwaysIncognito); ++ RecordUserAction.record("MobileMenuRequestEnableAlwaysIncognito"); + } else if (id == R.id.find_in_page_id) { + if (mFindToolbarManager == null) return false; + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +@@ -1753,6 +1753,10 @@ public class ChromeTabbedActivity + reportNewTabShortcutUsed(true); + getTabCreator(true).launchNTP(); + } ++ } else if (id == R.id.enable_always_incognito_id || id == R.id.enable_always_incognito_check_id) { ++ final boolean alwaysIncognito = PrefServiceBridge.getInstance().alwaysIncognitoEnabled(); ++ PrefServiceBridge.getInstance().setAlwaysIncognitoEnabled(!alwaysIncognito); ++ RecordUserAction.record("MobileMenuRequestEnableAlwaysIncognito"); + } else if (id == R.id.all_bookmarks_menu_id) { + if (currentTab != null) { + getCompositorViewHolder().hideKeyboard(() -> { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/IntentHandler.java +@@ -732,7 +732,6 @@ public class IntentHandler { + // Determine if this intent came from a trustworthy source (either Chrome or Google + // first party applications). + boolean isInternal = isIntentChromeOrFirstParty(intent); +- boolean isFromChrome = wasIntentSenderChrome(intent); + + // "Open new incognito tab" is currently limited to Chrome. + // +@@ -741,11 +740,10 @@ public class IntentHandler { + // from the list. In this case, we do not apply our Chrome token as the user has the + // option to select apps outside of our control, so we rely on this in memory check + // instead. +- if (!isFromChrome +- && IntentUtils.safeGetBooleanExtra( ++ if (IntentUtils.safeGetBooleanExtra( + intent, EXTRA_OPEN_NEW_INCOGNITO_TAB, false) +- && (getPendingIncognitoUrl() == null +- || !getPendingIncognitoUrl().equals(intent.getDataString()))) { ++ && getPendingIncognitoUrl() != null ++ && getPendingIncognitoUrl().equals(intent.getDataString())) { + return true; + } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/LaunchIntentDispatcher.java +@@ -36,6 +36,7 @@ import org.chromium.chrome.browser.instantapps.InstantAppsHandler; + import org.chromium.chrome.browser.metrics.MediaNotificationUma; + import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; + import org.chromium.chrome.browser.notifications.NotificationPlatformBridge; ++import org.chromium.chrome.browser.preferences.PrefServiceBridge; + import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations; + import org.chromium.chrome.browser.searchwidget.SearchActivity; + import org.chromium.chrome.browser.tab.Tab; +@@ -185,6 +186,12 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega + boolean incognito = + mIntent.getBooleanExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, false); + ++ if (!incognito) { ++ incognito = PrefServiceBridge.getInstance().alwaysIncognitoEnabled(); ++ if (incognito) { ++ mIntent.putExtra(IntentHandler.EXTRA_OPEN_NEW_INCOGNITO_TAB, true); ++ } ++ } + // Check if a web search Intent is being handled. + IntentHandler intentHandler = new IntentHandler(this, mActivity.getPackageName()); + String url = IntentHandler.getUrlFromIntent(mIntent); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java +@@ -211,6 +211,8 @@ public class AppMenuPropertiesDelegate { + && !TextUtils.isEmpty(url); + prepareAddToHomescreenMenuItem(menu, currentTab, canShowHomeScreenMenuItem); + ++ updateEnableAlwaysIncognitoMenuItem(menu, currentTab); ++ + updateRequestDesktopSiteMenuItem(menu, currentTab, true /* can show */); + + updateEnableJavascriptMenuItem(menu, currentTab); +@@ -410,6 +412,39 @@ public class AppMenuPropertiesDelegate { + } + + /** ++ * Updates the enable Always Incognito item's state. ++ * ++ * @param menu {@link Menu} for enable always incognito ++ * @param currentTab Current tab being displayed. ++ */ ++ protected void updateEnableAlwaysIncognitoMenuItem( ++ Menu menu, Tab currentTab) { ++ MenuItem enableMenuRow = menu.findItem(R.id.enable_always_incognito_row_menu_id); ++ MenuItem enableMenuLabel = menu.findItem(R.id.enable_always_incognito_id); ++ MenuItem enableMenuCheck = menu.findItem(R.id.enable_always_incognito_check_id); ++ ++ // Hide enable always incognito on all chrome:// pages and the NTP. ++ String url = currentTab.getUrl(); ++ boolean isChromeScheme = url.startsWith(UrlConstants.CHROME_URL_PREFIX) ++ || url.startsWith(UrlConstants.CHROME_NATIVE_URL_PREFIX); ++ ++ boolean itemVisible = !isChromeScheme && !currentTab.isNativePage(); ++ enableMenuRow.setVisible(itemVisible); ++ if (!itemVisible) return; ++ ++ boolean alwaysIncognito = PrefServiceBridge.getInstance().alwaysIncognitoEnabled(); ++ ++ // Mark the checkbox if always incognito is activated on this tab. ++ enableMenuCheck.setChecked(alwaysIncognito); ++ ++ // This title doesn't seem to be displayed by Android, but it is used to set up ++ // accessibility text in {@link AppMenuAdapter#setupMenuButton}. ++ enableMenuLabel.setTitleCondensed(alwaysIncognito ++ ? mActivity.getString(R.string.menu_enable_always_incognito_on) ++ : mActivity.getString(R.string.menu_enable_always_incognito_off)); ++ } ++ ++ /** + * Updates the request desktop site item's state. + * + * @param menu {@link Menu} for request desktop site. +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java +@@ -163,6 +163,8 @@ public class CustomTabAppMenuPropertiesDelegate extends AppMenuPropertiesDelegat + } + } + ++ updateEnableAlwaysIncognitoMenuItem(menu, currentTab); ++ + updateRequestDesktopSiteMenuItem(menu, currentTab, requestDesktopSiteVisible); + updateEnableJavascriptMenuItem(menu, currentTab); + prepareAddToHomescreenMenuItem(menu, currentTab, addToHomeScreenVisible); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java +--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/PrefServiceBridge.java +@@ -57,6 +57,8 @@ public class PrefServiceBridge { + private static final String CONTEXTUAL_SEARCH_DISABLED = "false"; + private static final String CONTEXTUAL_SEARCH_ENABLED = "true"; + ++ private static boolean alwaysIncognito = false; ++ + /** + * Structure that holds all the version information about the current Chrome browser. + */ +@@ -359,6 +361,14 @@ public class PrefServiceBridge { + } + + /** ++ * @return true if always-incognito is enabled. ++ * The default is false. ++ */ ++ public boolean alwaysIncognitoEnabled() { ++ return alwaysIncognito; ++ } ++ ++ /** + * @return true if background sync is managed by policy. + */ + public boolean isBackgroundSyncManaged() { +@@ -422,6 +432,13 @@ public class PrefServiceBridge { + } + + /** ++ * Enable or disable always-incognito mode. ++ */ ++ public void setAlwaysIncognitoEnabled(boolean enabled) { ++ alwaysIncognito = enabled; ++ } ++ ++ /** + * Enable or disable background sync. + */ + public void setBackgroundSyncEnabled(boolean enabled) { +diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd +--- a/chrome/android/java/strings/android_chrome_strings.grd ++++ b/chrome/android/java/strings/android_chrome_strings.grd +@@ -2502,6 +2502,16 @@ To obtain new licenses, connect to the internet and play your downloaded content + Turn on JavaScript + + ++ ++ Always Incognito ++ ++ ++ Turn off Always Incognito ++ ++ ++ Turn on Always Incognito ++ ++ + + Desktop site + +-- +2.7.4 + diff --git a/CAF-incognito-only.patch b/CAF-incognito-only.patch new file mode 100644 index 00000000..934728e2 --- /dev/null +++ b/CAF-incognito-only.patch @@ -0,0 +1,974 @@ +commit 56ab68688e7d2c7734e6190bf602dd584eae3222 +Author: csagan5 <32685696+csagan5@users.noreply.github.com> +Date: Sat Dec 9 23:03:50 2017 +0100 + + Incognito-only mode ported from CAF + Some rework was needed, almost nothing applied cleanly. + See below for the description of the two commits that were merged in this one. + + Browser setting to switch to incognito only mode + + - Added a setting to switch to incognito only mode in + privacy settings + - Upon enabling or disabling the switch, the browser will + restart in the respective mode (regular or incognito only) + + Change-Id: Ic42daa3e9bfdc0fcb4029d9df16ed57c5776d966 + + Fix issues seen in full incognito mode + + - Fixes crashes seen in incognito mode. + - Allow Custom Tabs, Web Apps to use the incognito only mode. + - Ensure we notify the user of the incognito mode in the notification bar and remove the notification when all + incognito tabs are closed. + - Ensure the status bar for Custom Tabs and Web Apps are grey to give a visual notification to use. + - Ensure the tabs are correctly ref counted for Custom Tabs, Web Apps when using incognito only mode. + + CR-Fixed: SWE-8867, SWE-8802 + + Change-Id: I95a77016e7562d82359c7780771da4c7bb73aa06 + +diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml +index ce7db92..a85aea3 100644 +--- a/chrome/android/java/res/xml/privacy_preferences.xml ++++ b/chrome/android/java/res/xml/privacy_preferences.xml +@@ -31,6 +31,10 @@ + android:title="@string/network_predictions_title" + android:summary="@string/network_predictions_summary" + android:persistent="false" /> ++ + 0)) { ++ profile.destroyWhenAppropriate(); ++ IncognitoNotificationManager.dismissIncognitoNotification(); ++ } ++ ++ } ++ ++ @Override ++ protected void onDestroyInternal() { ++ handleTabClose(); ++ } + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java +index afc92bed..39caa0b 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/WarmupManager.java +@@ -31,6 +31,7 @@ import org.chromium.chrome.browser.profiles.Profile; + import org.chromium.chrome.browser.widget.ControlContainer; + import org.chromium.content_public.browser.WebContents; + import org.chromium.content_public.browser.WebContentsObserver; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + + import java.net.InetAddress; + import java.net.MalformedURLException; +@@ -289,8 +290,9 @@ public final class WarmupManager { + public void createSpareWebContents() { + ThreadUtils.assertOnUiThread(); + if (!LibraryLoader.isInitialized()) return; ++ boolean incognitoOnly = IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); + if (mSpareWebContents != null || SysUtils.isLowEndDevice()) return; +- mSpareWebContents = WebContentsFactory.createWebContentsWithWarmRenderer(false, false); ++ mSpareWebContents = WebContentsFactory.createWebContentsWithWarmRenderer(incognitoOnly, false); + mObserver = new RenderProcessGoneObserver(); + mSpareWebContents.addObserver(mObserver); + mWebContentsCreationTimeMs = SystemClock.elapsedRealtime(); +@@ -316,7 +318,7 @@ public final class WarmupManager { + */ + public WebContents takeSpareWebContents(boolean incognito, boolean initiallyHidden) { + ThreadUtils.assertOnUiThread(); +- if (incognito || initiallyHidden) return null; ++ if ((incognito != mSpareWebContents.isIncognito()) || initiallyHidden) return null; + WebContents result = mSpareWebContents; + if (result == null) return null; + mSpareWebContents = null; +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java +index e48a3e8..5435958 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegate.java +@@ -29,6 +29,7 @@ import org.chromium.chrome.browser.UrlConstants; + import org.chromium.chrome.browser.banners.AppBannerManager; + import org.chromium.chrome.browser.bookmarks.BookmarkBridge; + import org.chromium.chrome.browser.download.DownloadUtils; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; + import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; + import org.chromium.chrome.browser.preferences.ManagedPreferencesUtils; +@@ -175,6 +176,8 @@ public class AppMenuPropertiesDelegate { + + MenuItem allBookmarksMenuItem = menu.findItem(R.id.all_bookmarks_menu_id); + // Hide 'History' in incognito only browser ++ boolean isIncognitoOnlyBrowser = IncognitoOnlyModeUtil.getInstance() ++ .isIncognitoOnlyBrowser(); + menu.findItem(R.id.open_history_menu_id).setVisible(!isIncognitoOnlyBrowser); + + allBookmarksMenuItem.setTitle(mActivity.getString(R.string.menu_bookmarks)); +@@ -201,7 +204,7 @@ public class AppMenuPropertiesDelegate { + // is not persisted when adding to the homescreen. + // * If creating shortcuts it not supported by the current home screen. + boolean canShowHomeScreenMenuItem = ShortcutHelper.isAddToHomeIntentSupported() +- && !isChromeScheme && !isFileScheme && !isContentScheme && !isIncognito ++ && !isChromeScheme && !isFileScheme && !isContentScheme && (!isIncognito || isIncognitoOnlyBrowser) + && !TextUtils.isEmpty(url); + prepareAddToHomescreenMenuItem(menu, currentTab, canShowHomeScreenMenuItem); + +@@ -224,7 +227,8 @@ public class AppMenuPropertiesDelegate { + } + + if (isOverviewMenu) { +- if (isIncognito) { ++ if (isIncognito ++ && !(IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled())) { + // Hide normal close all tabs item. + menu.findItem(R.id.close_all_tabs_menu_id).setVisible(false); + // Enable close incognito tabs only if there are incognito tabs. +@@ -244,6 +248,7 @@ public class AppMenuPropertiesDelegate { + } + + boolean incognitoMenuItemVisible = !isBottomSheetNtpMenu || !isIncognito; ++ incognitoMenuItemVisible &= !(IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled()); + // Disable new incognito tab when it is blocked (e.g. by a policy). + // findItem(...).setEnabled(...)" is not enough here, because of the inflated + // main_menu.xml contains multiple items with the same id in different groups +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java +index 03edf2d..fb2cc2e 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java +@@ -33,6 +33,7 @@ import org.chromium.chrome.browser.compositor.scene_layer.SceneLayer; + import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer; + import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; + import org.chromium.chrome.browser.partnercustomizations.HomepageManager; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.tab.Tab; + import org.chromium.chrome.browser.tabmodel.TabModel; + import org.chromium.chrome.browser.tabmodel.TabModelSelector; +@@ -328,8 +329,10 @@ public class StackLayout extends Layout implements Animatable 0); ++ return (!mTabModel.isIncognito() ++ && !IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled()) ++ || (!mIsDying && mTabModel.getCount() > 0); + } + + private float getDefaultDiscardDirection() { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java +index 25f1fd7..6257c82 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java +@@ -23,6 +23,7 @@ import org.chromium.chrome.browser.compositor.layouts.eventfilter.GestureHandler + import org.chromium.chrome.browser.compositor.overlays.SceneOverlay; + import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer; + import org.chromium.chrome.browser.compositor.scene_layer.TabStripSceneLayer; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.tab.Tab; + import org.chromium.chrome.browser.tabmodel.TabCreatorManager; + import org.chromium.chrome.browser.tabmodel.TabModel; +@@ -377,7 +378,8 @@ public class StripLayoutHelperManager implements SceneOverlay { + mModelSelectorButton.setIncognito(mIsIncognito); + if (mTabModelSelector != null) { + boolean isVisible = mTabModelSelector.getModel(true).getCount() != 0; +- mModelSelectorButton.setVisible(isVisible); ++ mModelSelectorButton.setVisible(isVisible ++ && !IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled()); + + float endMargin = isVisible + ? MODEL_SELECTOR_BUTTON_WIDTH_DP + MODEL_SELECTOR_BUTTON_END_PADDING_DP +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java +index 43b556f..2b92e83 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java +@@ -59,9 +59,13 @@ import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils; + import org.chromium.chrome.browser.compositor.layouts.LayoutManager; + import org.chromium.chrome.browser.datausage.DataUseTabUIManager; + import org.chromium.chrome.browser.externalnav.ExternalNavigationDelegateImpl; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor; + import org.chromium.chrome.browser.fullscreen.BrowserStateBrowserControlsVisibilityDelegate; + import org.chromium.chrome.browser.gsa.GSAState; ++import org.chromium.chrome.browser.incognito.IncognitoNotificationManager; ++import org.chromium.chrome.browser.incognito.IncognitoNotificationService; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.metrics.PageLoadMetrics; + import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; + import org.chromium.chrome.browser.page_info.PageInfoPopup; +@@ -334,7 +338,12 @@ public class CustomTabActivity extends ChromeActivity { + if (mConnection.shouldHideDomainForSession(mSession)) { + getToolbarManager().setUrlBarHidden(true); + } ++ boolean incognitoOnly = IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); + int toolbarColor = mIntentDataProvider.getToolbarColor(); ++ if (incognitoOnly) { ++ toolbarColor = ApiCompatibilityUtils.getColor(getResources(), ++ R.color.incognito_primary_color); ++ } + getToolbarManager().updatePrimaryColor(toolbarColor, false); + if (!mIntentDataProvider.isOpenedByChrome()) { + getToolbarManager().setShouldUpdateToolbarPrimaryColor(false); +@@ -390,7 +399,8 @@ public class CustomTabActivity extends ChromeActivity { + mConnection.cancelSpeculation(mSession); + } + +- getTabModelSelector().getModel(false).addObserver(mTabModelObserver); ++ boolean incognitoOnly = IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); ++ getTabModelSelector().getModel(incognitoOnly).addObserver(mTabModelObserver); + + boolean successfulStateRestore = false; + // Attempt to restore the previous tab state if applicable. +@@ -412,7 +422,7 @@ public class CustomTabActivity extends ChromeActivity { + } else { + mMainTab = createMainTab(); + } +- getTabModelSelector().getModel(false).addTab(mMainTab, 0, mMainTab.getLaunchType()); ++ getTabModelSelector().getModel(incognitoOnly).addTab(mMainTab, 0, mMainTab.getLaunchType()); + } + + // This cannot be done before because we want to do the reparenting only +@@ -550,7 +560,9 @@ public class CustomTabActivity extends ChromeActivity { + getIntent(), IntentHandler.EXTRA_TAB_ID, Tab.INVALID_TAB_ID); + int parentTabId = IntentUtils.safeGetIntExtra( + getIntent(), IntentHandler.EXTRA_PARENT_TAB_ID, Tab.INVALID_TAB_ID); +- Tab tab = new Tab(assignedTabId, parentTabId, false, getWindowAndroid(), ++ ++ boolean incognitoOnly = IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); ++ Tab tab = new Tab(assignedTabId, parentTabId, incognitoOnly, getWindowAndroid(), + TabLaunchType.FROM_EXTERNAL_APP, null, null); + if (getIntent().getIntExtra( + CustomTabIntentDataProvider.EXTRA_BROWSER_LAUNCH_SOURCE, ACTIVITY_TYPE_OTHER) +@@ -588,12 +600,12 @@ public class CustomTabActivity extends ChromeActivity { + webContentsStateOnLaunch = WEBCONTENTS_STATE_TRANSFERRED_WEBCONTENTS; + webContents.resumeLoadingCreatedWebContents(); + } else { +- webContents = WarmupManager.getInstance().takeSpareWebContents(false, false); ++ webContents = WarmupManager.getInstance().takeSpareWebContents(IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(), false); + if (webContents != null) { + webContentsStateOnLaunch = WEBCONTENTS_STATE_SPARE_WEBCONTENTS; + } else { + webContents = +- WebContentsFactory.createWebContentsWithWarmRenderer(false, false); ++ WebContentsFactory.createWebContentsWithWarmRenderer(IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(), false); + webContentsStateOnLaunch = WEBCONTENTS_STATE_NO_WEBCONTENTS; + } + } +@@ -835,6 +847,13 @@ public class CustomTabActivity extends ChromeActivity { + if (getAppMenuHandler() != null) getAppMenuHandler().hideAppMenu(); + + super.finish(); ++ boolean incognitoOnly = IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); ++ if (incognitoOnly) { ++ int incognitoCount = IncognitoNotificationService.getIncognitoTabCount(); ++ if (incognitoCount == 0) { ++ IncognitoNotificationManager.dismissIncognitoNotification(); ++ } ++ } + if (mIntentDataProvider != null && mIntentDataProvider.shouldAnimateOnFinish()) { + mShouldOverridePackage = true; + overridePendingTransition(mIntentDataProvider.getAnimationEnterRes(), +@@ -853,7 +872,8 @@ public class CustomTabActivity extends ChromeActivity { + public final void finishAndClose(boolean reparenting) { + if (mIsClosing) return; + mIsClosing = true; +- if (!reparenting) { ++ boolean incognitoOnly = IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); ++ if (!reparenting && !incognitoOnly) { + // Closing the activity destroys the renderer as well. Re-create a spare renderer some + // time after, so that we have one ready for the next tab open. This does not increase + // memory consumption, as the current renderer goes away. We create a renderer as a lot +@@ -1025,7 +1045,8 @@ public class CustomTabActivity extends ChromeActivity { + protected void setStatusBarColor(Tab tab, int color) { + // Intentionally do nothing as CustomTabActivity explicitly sets status bar color. Except + // for Custom Tabs opened by Chrome. +- if (mIntentDataProvider.isOpenedByChrome()) super.setStatusBarColor(tab, color); ++ boolean incognitoOnly = IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); ++ if (mIntentDataProvider.isOpenedByChrome() && !incognitoOnly) super.setStatusBarColor(tab, color); + } + + /** +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java +index f9e91ad..3e9bd1c 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java +@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.incognito; + + import android.annotation.TargetApi; + import android.app.Activity; ++import android.util.SparseArray; + import android.app.ActivityManager; + import android.app.ActivityManager.AppTask; + import android.app.ActivityManager.RecentTaskInfo; +@@ -24,15 +25,21 @@ import org.chromium.base.ContextUtils; + import org.chromium.base.ThreadUtils; + import org.chromium.base.VisibleForTesting; + import org.chromium.base.library_loader.LibraryProcessType; ++import org.chromium.chrome.browser.ChromeActivity; ++import org.chromium.chrome.browser.tab.Tab; + import org.chromium.chrome.browser.ChromeTabbedActivity; + import org.chromium.chrome.browser.TabState; ++import org.chromium.chrome.browser.customtabs.CustomTabActivity; + import org.chromium.chrome.browser.document.ChromeLauncherActivity; + import org.chromium.chrome.browser.document.DocumentUtils; + import org.chromium.chrome.browser.profiles.Profile; ++import org.chromium.chrome.browser.tabmodel.AsyncTabParams; ++import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager; + import org.chromium.chrome.browser.tabmodel.TabWindowManager; + import org.chromium.chrome.browser.tabmodel.TabbedModeTabPersistencePolicy; + import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet; + import org.chromium.content.browser.BrowserStartupController; ++import org.chromium.chrome.browser.webapps.WebappActivity; + + import java.io.File; + import java.lang.ref.WeakReference; +@@ -189,7 +196,9 @@ public class IncognitoNotificationService extends IntentService { + for (int i = 0; i < runningActivities.size(); i++) { + Activity activity = runningActivities.get(i).get(); + if (activity == null) continue; +- if (!(activity instanceof ChromeTabbedActivity)) continue; ++ if (!(activity instanceof ChromeTabbedActivity || ++ activity instanceof CustomTabActivity || ++ activity instanceof WebappActivity)) continue; + + ChromeTabbedActivity tabbedActivity = (ChromeTabbedActivity) activity; + if (tabbedActivity.isActivityDestroyed()) continue; +@@ -205,7 +214,7 @@ public class IncognitoNotificationService extends IntentService { + BottomSheet.SHEET_STATE_PEEK, false); + } + +- tabbedActivity.getTabModelSelector().getModel(true).closeAllTabs( ++ chromeActivity.getTabModelSelector().getModel(true).closeAllTabs( + false, false); + } + } +@@ -229,4 +238,30 @@ public class IncognitoNotificationService extends IntentService { + return deletionSuccessful; + } + ++ public static int getIncognitoTabCount() { ++ int count = 0; ++ List> runningActivities = ++ ApplicationStatus.getRunningActivities(); ++ for (int i = 0; i < runningActivities.size(); i++) { ++ Activity activity = runningActivities.get(i).get(); ++ if (activity == null) continue; ++ if (!(activity instanceof ChromeTabbedActivity || ++ activity instanceof CustomTabActivity || ++ activity instanceof WebappActivity)) continue; ++ ++ ChromeActivity chromeActivity = (ChromeActivity) activity; ++ if (chromeActivity.isActivityDestroyed()) continue; ++ ++ count += chromeActivity.getTabModelSelector().getModel(true).getCount(); ++ } ++ // Count tabs that are moving between activities (e.g. a tab that was recently reparented ++ // and hasn't been attached to its new activity yet). ++ SparseArray asyncTabParams = AsyncTabParamsManager.getAsyncTabParams(); ++ for (int i = 0; i < asyncTabParams.size(); i++) { ++ Tab tab = asyncTabParams.valueAt(i).getTabToReparent(); ++ if (tab != null && tab.isIncognito()) count++; ++ } ++ return count; ++ } ++ + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoOnlyModeUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoOnlyModeUtil.java +new file mode 100644 +index 0000000..f6b17e7 +--- /dev/null ++++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoOnlyModeUtil.java +@@ -0,0 +1,73 @@ ++/* ++ * Copyright (c) 2016, The Linux Foundation. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following ++ * disclaimer in the documentation and/or other materials provided ++ * with the distribution. ++ * * Neither the name of The Linux Foundation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS ++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR ++ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ++ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE ++ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN ++ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++*/ ++ ++package org.chromium.chrome.browser.incognito; ++ ++import org.chromium.base.CommandLine; ++import org.chromium.base.ContextUtils; ++import org.chromium.chrome.browser.ChromeSwitches; ++import org.chromium.chrome.browser.preferences.privacy.PrivacyPreferencesManager; ++ ++/** ++ * Utility class for incognito only mode ++ */ ++public class IncognitoOnlyModeUtil { ++ private static IncognitoOnlyModeUtil sInstance; ++ ++ private IncognitoOnlyModeUtil() {} ++ ++ public static IncognitoOnlyModeUtil getInstance() { ++ if (sInstance == null) { ++ sInstance = new IncognitoOnlyModeUtil(); ++ } ++ return sInstance; ++ } ++ /** ++ * @return whether incognito only browser ++ */ ++ public boolean isIncognitoOnlyBrowser() { ++ return CommandLine.getInstance().hasSwitch(ChromeSwitches.INCOGNITO_ONLY_MODE); ++ } ++ ++ /** ++ * @return whether incognito only mode preference is enabled ++ */ ++ public boolean isIncognitoOnlyPreferenceEnabled() { ++ return PrivacyPreferencesManager.getInstance(ContextUtils.getApplicationContext()). ++ isIncognitoOnlyEnabled(); ++ } ++ ++ /** ++ * @return whether incognito only browser or incognito only mode preference is enabled ++ */ ++ public boolean isIncognitoOnlyModeEnabled() { ++ return isIncognitoOnlyBrowser() || isIncognitoOnlyPreferenceEnabled(); ++ } ++ ++} +\ No newline at end of file +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java +index e594c57..def79eb 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/LocationBarPhone.java +@@ -20,6 +20,7 @@ import android.widget.FrameLayout; + import org.chromium.base.ApiCompatibilityUtils; + import org.chromium.chrome.R; + import org.chromium.chrome.browser.WindowDelegate; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.ntp.NewTabPage; + import org.chromium.chrome.browser.toolbar.ToolbarDataProvider; + import org.chromium.chrome.browser.util.MathUtils; +@@ -271,7 +272,8 @@ public class LocationBarPhone extends LocationBarLayout { + + boolean showIncognitoBadge = + getToolbarDataProvider() != null && getToolbarDataProvider().isIncognito(); +- mIncognitoBadge.setVisibility(showIncognitoBadge ? VISIBLE : GONE); ++ mIncognitoBadge.setVisibility(showIncognitoBadge ++ && !IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled() ? VISIBLE : GONE); + updateIncognitoBadgePadding(); + } + +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java +index 0d78e7c..b1b9f67 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferences.java +@@ -4,6 +4,8 @@ + + package org.chromium.chrome.browser.preferences.privacy; + ++import android.app.AlertDialog; ++import android.content.DialogInterface; + import android.os.Bundle; + import android.preference.CheckBoxPreference; + import android.preference.Preference; +@@ -18,8 +20,11 @@ import org.chromium.base.SysUtils; + import org.chromium.base.metrics.RecordHistogram; + import org.chromium.chrome.R; + import org.chromium.chrome.browser.ChromeFeatureList; ++import org.chromium.chrome.browser.ApplicationLifetime; ++import org.chromium.chrome.browser.ChromeSwitches; + import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial; + import org.chromium.chrome.browser.help.HelpAndFeedback; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.physicalweb.PhysicalWeb; + import org.chromium.chrome.browser.preferences.ChromeBaseCheckBoxPreference; + import org.chromium.chrome.browser.preferences.ManagedPreferenceDelegate; +@@ -41,6 +46,7 @@ public class PrivacyPreferences extends PreferenceFragment + private static final String PREF_SAFE_BROWSING = "safe_browsing"; + private static final String PREF_CONTEXTUAL_SEARCH = "contextual_search"; + private static final String PREF_NETWORK_PREDICTIONS = "network_predictions"; ++ private static final String PREF_INCOGNITO_ONLY = "incognito_only"; + private static final String PREF_DO_NOT_TRACK = "do_not_track"; + private static final String PREF_USAGE_AND_CRASH_REPORTING = "usage_and_crash_reports"; + private static final String PREF_PHYSICAL_WEB = "physical_web"; +@@ -129,6 +135,18 @@ public class PrivacyPreferences extends PreferenceFragment + recordNetworkPredictionEnablingUMA((boolean) newValue); + } else if (PREF_NAVIGATION_ERROR.equals(key)) { + PrefServiceBridge.getInstance().setResolveNavigationErrorEnabled((boolean) newValue); ++ } else if (PREF_INCOGNITO_ONLY.equals(key)) { ++ incognitoOnlyPreferenceDialog(preference, (boolean) newValue).show(); ++ } ++ ++ boolean isIncognitoOnlyBrowser = IncognitoOnlyModeUtil.getInstance() ++ .isIncognitoOnlyBrowser(); ++ ChromeBaseCheckBoxPreference incognitoOnlyPref = ++ (ChromeBaseCheckBoxPreference) findPreference(PREF_INCOGNITO_ONLY); ++ if (isIncognitoOnlyBrowser) { ++ preferenceScreen.removePreference(incognitoOnlyPref); ++ } else { ++ incognitoOnlyPref.setOnPreferenceChangeListener(this); + } + + return true; +@@ -189,6 +207,13 @@ public class PrivacyPreferences extends PreferenceFragment + doNotTrackPref.setSummary(prefServiceBridge.isDoNotTrackEnabled() ? textOn : textOff); + } + ++ Preference incognitoOnlyPref = findPreference(PREF_INCOGNITO_ONLY); ++ if (incognitoOnlyPref != null) { ++ boolean isIncognitoOnlyEnabled = privacyPrefManager.isIncognitoOnlyEnabled(); ++ incognitoOnlyPref.setChecked(isIncognitoOnlyEnabled); ++ incognitoOnlyPref.setSummary(isIncognitoOnlyEnabled ? textOn : textOff); ++ } ++ + Preference contextualPref = findPreference(PREF_CONTEXTUAL_SEARCH); + if (contextualPref != null) { + boolean isContextualSearchEnabled = !prefServiceBridge.isContextualSearchDisabled(); +@@ -254,4 +279,38 @@ public class PrivacyPreferences extends PreferenceFragment + } + return false; + } ++ ++ private AlertDialog incognitoOnlyPreferenceDialog(final Preference preference, ++ final boolean isChecked) { ++ AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme) ++ .setTitle(isChecked ? R.string.incognito_only_turn_on_title ++ : R.string.incognito_only_turn_off_title) ++ .setMessage(isChecked ? R.string.incognito_only_turn_on_confirmation ++ : R.string.incognito_only_turn_off_confirmation) ++ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { ++ @Override ++ public void onClick(DialogInterface dialog, int id) { ++ ((ChromeBaseCheckBoxPreference) preference).setChecked(!isChecked); ++ dialog.dismiss(); ++ } ++ }) ++ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { ++ @Override ++ public void onClick(DialogInterface dialog, int which) { ++ PrivacyPreferencesManager.getInstance(getActivity()). ++ setIncognitoOnlyEnabled((boolean) isChecked); ++ dialog.dismiss(); ++ ApplicationLifetime.terminate(true); ++ } ++ }) ++ .setOnCancelListener(new DialogInterface.OnCancelListener() { ++ @Override ++ public void onCancel(DialogInterface dialog) { ++ ((ChromeBaseCheckBoxPreference) preference).setChecked(!isChecked); ++ } ++ }) ++ .create(); ++ ++ return dialog; ++ } + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java +index 4ddbd4d..9e2a163 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/PrivacyPreferencesManager.java +@@ -38,6 +38,7 @@ public class PrivacyPreferencesManager implements CrashReportingPermissionManage + private static final String PREF_BANDWIDTH_NO_CELLULAR_OLD = "prefetch_bandwidth_no_cellular"; + private static final String ALLOW_PRERENDER_OLD = "allow_prefetch"; + private static final String PREF_PHYSICAL_WEB = "physical_web"; ++ private static final String INCOGNITO_ONLY_PREFERENCE = "incognito_only_preference"; + private static final int PHYSICAL_WEB_OFF = 0; + private static final int PHYSICAL_WEB_ON = 1; + private static final int PHYSICAL_WEB_ONBOARDING = 2; +@@ -339,4 +340,23 @@ public class PrivacyPreferencesManager implements CrashReportingPermissionManage + int state = mSharedPreferences.getInt(PREF_PHYSICAL_WEB, PHYSICAL_WEB_ONBOARDING); + return (state == PHYSICAL_WEB_ON); + } ++ ++ /** ++ * Check whether Incognito Only preference is enabled ++ * ++ * @return whether Incognito Only preference is enabled, false by default ++ */ ++ public boolean isIncognitoOnlyEnabled() { ++ return mSharedPreferences.getBoolean(INCOGNITO_ONLY_PREFERENCE, false); ++ } ++ ++ /** ++ * Sets the Incognito Only preference, which enables the browser to switch to incognito only ++ * mode ++ * ++ * @param enabled A boolean indicating whether to switch to incognito only mode. ++ */ ++ public void setIncognitoOnlyEnabled(boolean enabled) { ++ mSharedPreferences.edit().putBoolean(INCOGNITO_ONLY_PREFERENCE, enabled).apply(); ++ } + } +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java +index 344a2ed..d54accf 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/SiteSettingsPreferences.java +@@ -17,6 +17,7 @@ import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; + import org.chromium.chrome.browser.preferences.LocationSettings; + import org.chromium.chrome.browser.preferences.PrefServiceBridge; + import org.chromium.chrome.browser.preferences.PreferenceUtils; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + + import java.util.ArrayList; + import java.util.List; +@@ -110,6 +111,13 @@ public class SiteSettingsPreferences extends PreferenceFragment + } + + private void configurePreferences() { ++ // Remove 'All sites' menu for incognito only browser ++ boolean isIncognitoOnlyBrowser = IncognitoOnlyModeUtil.getInstance() ++ .isIncognitoOnlyBrowser(); ++ if (isIncognitoOnlyBrowser) { ++ getPreferenceScreen().removePreference(findPreference(ALL_SITES_KEY)); ++ } ++ + if (mMediaSubMenu) { + // The Media sub-menu only contains Protected Content and Autoplay, so remove all other + // menus. +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java +index d90ccc3..ddc30f2 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/undo/UndoBarController.java +@@ -9,6 +9,7 @@ import android.content.Context; + import org.chromium.base.metrics.RecordHistogram; + import org.chromium.chrome.R; + import org.chromium.chrome.browser.device.DeviceClassManager; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.snackbar.Snackbar; + import org.chromium.chrome.browser.snackbar.SnackbarManager; + import org.chromium.chrome.browser.tab.Tab; +@@ -64,7 +65,8 @@ public class UndoBarController implements SnackbarManager.SnackbarController { + mTabModelObserver = new EmptyTabModelObserver() { + private boolean disableUndo() { + return AccessibilityUtil.isAccessibilityEnabled() +- || DeviceClassManager.enableAccessibilityLayout(); ++ || DeviceClassManager.enableAccessibilityLayout() ++ || IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); + } + + @Override +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java +index c3e7aa0..b1d758c 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/IncognitoTabModel.java +@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.tabmodel; + import org.chromium.base.ObserverList; + import org.chromium.base.ThreadUtils; + import org.chromium.chrome.browser.incognito.IncognitoNotificationManager; ++import org.chromium.chrome.browser.incognito.IncognitoNotificationService; + import org.chromium.chrome.browser.profiles.Profile; + import org.chromium.chrome.browser.tab.Tab; + +@@ -82,7 +83,7 @@ public class IncognitoTabModel implements TabModel { + + // Only delete the incognito profile if there are no incognito tabs open in any tab + // model selector as the profile is shared between them. +- if (profile != null && !mDelegate.doIncognitoTabsExist()) { ++ if (profile != null && !(IncognitoNotificationService.getIncognitoTabCount() > 0)) { + IncognitoNotificationManager.dismissIncognitoNotification(); + + profile.destroyWhenAppropriate(); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java +index 5221244..c976cd4 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorBase.java +@@ -5,6 +5,7 @@ + package org.chromium.chrome.browser.tabmodel; + + import org.chromium.base.ObserverList; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.tab.Tab; + import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType; + import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; +@@ -40,7 +41,8 @@ public abstract class TabModelSelectorBase implements TabModelSelector { + for (int i = 0; i < models.length; i++) { + tabModels.add(models[i]); + } +- mActiveModelIndex = startIncognito ? INCOGNITO_TAB_MODEL_INDEX : NORMAL_TAB_MODEL_INDEX; ++ mActiveModelIndex = (startIncognito || ++ IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled()) ? INCOGNITO_TAB_MODEL_INDEX : NORMAL_TAB_MODEL_INDEX; + mTabModels = Collections.unmodifiableList(tabModels); + + TabModelObserver tabModelObserver = new EmptyTabModelObserver() { +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelSelector.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelSelector.java +index e69d43c..71893ff 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelSelector.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/document/DocumentTabModelSelector.java +@@ -14,6 +14,7 @@ import android.os.Build; + import org.chromium.base.ContextUtils; + import org.chromium.base.VisibleForTesting; + import org.chromium.chrome.browser.document.DocumentUtils; ++import org.chromium.chrome.browser.incognito.IncognitoNotificationService; + import org.chromium.chrome.browser.tab.Tab; + import org.chromium.chrome.browser.tab.TabIdManager; + import org.chromium.chrome.browser.tabmodel.IncognitoTabModel.IncognitoTabModelDelegate; +@@ -105,7 +106,7 @@ public class DocumentTabModelSelector extends TabModelSelectorBase implements Ta + public boolean doIncognitoTabsExist() { + // TODO(dfalcantara): Devices in document mode do not trigger the TabWindowManager. + // Revisit this when we have a Samsung L multi-instance device. +- return mIncognitoTabModel.getCount() > 0; ++ return IncognitoNotificationService.getIncognitoTabCount() > 0; + } + }, mActivityDelegate); + initializeTabIdCounter(); +diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java +index 26bb763..b01a165 100644 +--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java ++++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java +@@ -52,6 +52,7 @@ import org.chromium.chrome.browser.customtabs.CustomTabsConnection; + import org.chromium.chrome.browser.document.ChromeLauncherActivity; + import org.chromium.chrome.browser.document.DocumentUtils; + import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; ++import org.chromium.chrome.browser.incognito.IncognitoOnlyModeUtil; + import org.chromium.chrome.browser.tab.EmptyTabObserver; + import org.chromium.chrome.browser.tab.Tab; + import org.chromium.chrome.browser.tab.TabDelegateFactory; +@@ -814,6 +815,12 @@ public class WebappActivity extends SingleTabActivity { + mBrandColor = (int) mWebappInfo.themeColor(); + } + ++ boolean incognitoOnly = IncognitoOnlyModeUtil.getInstance().isIncognitoOnlyModeEnabled(); ++ if (incognitoOnly) { ++ mBrandColor = ApiCompatibilityUtils.getColor(getResources(), ++ R.color.incognito_primary_color); ++ } ++ + int taskDescriptionColor = + ApiCompatibilityUtils.getColor(getResources(), R.color.default_primary_color); + +diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd +index a81975d..f14b395 100644 +--- a/chrome/android/java/strings/android_chrome_strings.grd ++++ b/chrome/android/java/strings/android_chrome_strings.grd +@@ -225,6 +225,26 @@ CHAR-LIMIT guidelines: + Retry + + ++ ++ ++ "Incognito Only" mode ++ ++ ++ Browse using only incognito tabs ++ ++ ++ Enable "Incognito Only" mode ++ ++ ++ Disable "Incognito Only" mode ++ ++ ++ Browser will <b>restart</b> in incognito-only mode. All opened tabs will be closed. ++ ++ ++ Browser will <b>restart</b> in regular mode. All opened tabs will be closed. ++ ++ + + + Settings +diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni +index ba24b98..a576f9d 100644 +--- a/chrome/android/java_sources.gni ++++ b/chrome/android/java_sources.gni +@@ -491,6 +491,7 @@ chrome_java_sources = [ + "java/src/org/chromium/chrome/browser/identity/UuidBasedUniqueIdentificationGenerator.java", + "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationManager.java", + "java/src/org/chromium/chrome/browser/incognito/IncognitoNotificationService.java", ++ "java/src/org/chromium/chrome/browser/incognito/IncognitoOnlyModeUtil.java", + "java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarAndroid.java", + "java/src/org/chromium/chrome/browser/infobar/AppBannerInfoBarDelegateAndroid.java", + "java/src/org/chromium/chrome/browser/infobar/AutofillCreditCardFillingInfoBar.java",