Add-an-always-incognito-mode.patch 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. From: Ryan Archer <ryan.bradley.archer@gmail.com>
  2. Date: Wed, 2 Aug 2017 01:41:28 -0400
  3. Subject: Add an always-incognito mode
  4. More specifically, add a preference that causes all new tabs and all
  5. clicked links to launch as incognito.
  6. Make sure initial incognito status is correctly recognized.
  7. Enable incognito custom tabs and fix crashes for incognito/custom tab intents (credits to @uazo)
  8. ---
  9. chrome/android/chrome_java_sources.gni | 1 +
  10. .../java/res/xml/privacy_preferences.xml | 5 ++
  11. .../AlwaysIncognitoLinkInterceptor.java | 80 +++++++++++++++++++
  12. .../chrome/browser/ChromeTabbedActivity.java | 6 +-
  13. .../chrome/browser/app/ChromeActivity.java | 4 +
  14. .../AppMenuPropertiesDelegateImpl.java | 6 ++
  15. .../ChromeContextMenuPopulator.java | 8 +-
  16. .../CustomTabActivityLifecycleUmaTracker.java | 25 ------
  17. .../CustomTabIntentDataProvider.java | 5 +-
  18. .../browser/init/StartupTabPreloader.java | 14 +++-
  19. .../privacy/settings/PrivacySettings.java | 37 ++++++++-
  20. .../browser/settings/SettingsActivity.java | 4 +
  21. .../tabbed_mode/TabbedRootUiCoordinator.java | 6 +-
  22. .../browser/tabmodel/ChromeTabCreator.java | 16 +++-
  23. .../browser/tabmodel/TabPersistentStore.java | 10 +++
  24. .../webapps/WebappIntentDataProvider.java | 14 ++++
  25. .../flags/android/chrome_feature_list.cc | 2 +-
  26. .../strings/android_chrome_strings.grd | 13 +++
  27. chrome/browser/ui/messages/android/BUILD.gn | 1 +
  28. .../snackbar/INeedSnackbarManager.java | 27 +++++++
  29. 20 files changed, 248 insertions(+), 36 deletions(-)
  30. create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/AlwaysIncognitoLinkInterceptor.java
  31. create mode 100644 chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java
  32. diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
  33. --- a/chrome/android/chrome_java_sources.gni
  34. +++ b/chrome/android/chrome_java_sources.gni
  35. @@ -3,6 +3,7 @@
  36. # found in the LICENSE file.
  37. chrome_java_sources = [
  38. + "java/src/org/chromium/chrome/browser/AlwaysIncognitoLinkInterceptor.java",
  39. "java/src/com/google/android/apps/chrome/appwidget/bookmarks/BookmarkThumbnailWidgetProvider.java",
  40. "java/src/org/chromium/chrome/browser/ActivityTabProvider.java",
  41. "java/src/org/chromium/chrome/browser/ActivityUtils.java",
  42. diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
  43. --- a/chrome/android/java/res/xml/privacy_preferences.xml
  44. +++ b/chrome/android/java/res/xml/privacy_preferences.xml
  45. @@ -37,6 +37,11 @@
  46. android:key="secure_dns"
  47. android:title="@string/settings_secure_dns_title"
  48. android:fragment="org.chromium.chrome.browser.privacy.secure_dns.SecureDnsSettings"/>
  49. + <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
  50. + android:key="always_incognito"
  51. + android:title="@string/always_incognito_title"
  52. + android:summary="@string/always_incognito_summary"
  53. + android:defaultValue="false" />
  54. <Preference
  55. android:fragment="org.chromium.chrome.browser.privacy.settings.DoNotTrackSettings"
  56. android:key="do_not_track"
  57. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AlwaysIncognitoLinkInterceptor.java b/chrome/android/java/src/org/chromium/chrome/browser/AlwaysIncognitoLinkInterceptor.java
  58. new file mode 100644
  59. --- /dev/null
  60. +++ b/chrome/android/java/src/org/chromium/chrome/browser/AlwaysIncognitoLinkInterceptor.java
  61. @@ -0,0 +1,80 @@
  62. +/* This Source Code Form is subject to the terms of the Mozilla Public
  63. + * License, v. 2.0. If a copy of the MPL was not distributed with this
  64. + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  65. +
  66. +package org.chromium.chrome.browser;
  67. +
  68. +import android.content.SharedPreferences;
  69. +
  70. +import org.chromium.chrome.browser.tab.EmptyTabObserver;
  71. +import org.chromium.chrome.browser.tab.Tab;
  72. +import org.chromium.chrome.browser.tab.TabImpl;
  73. +import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
  74. +import org.chromium.chrome.browser.tab.TabLaunchType;
  75. +import org.chromium.chrome.browser.tabmodel.TabModel;
  76. +import org.chromium.content_public.browser.LoadUrlParams;
  77. +import org.chromium.url.GURL;
  78. +
  79. +import java.util.HashMap;
  80. +import java.util.HashSet;
  81. +import java.util.Map;
  82. +import java.util.Set;
  83. +/**
  84. + * A {@link TabObserver} that implements the always-incognito preference behavior for links. When the preference is set
  85. + * to true, it intercepts links opened within observed {@link Tab}s and opens them in new incognito <code>Tab</code>s instead.
  86. + */
  87. +public class AlwaysIncognitoLinkInterceptor extends EmptyTabObserver {
  88. +
  89. + public static final String PREF_ALWAYS_INCOGNITO = "always_incognito";
  90. +
  91. + private final SharedPreferences alwaysIncognitoContainer;
  92. + private final Map<Tab, String> lastUrls;
  93. + private final Set<Tab> revertingTabs;
  94. +
  95. + public AlwaysIncognitoLinkInterceptor(final SharedPreferences alwaysIncognitoContainer) {
  96. +
  97. + assert alwaysIncognitoContainer != null;
  98. +
  99. + this.alwaysIncognitoContainer = alwaysIncognitoContainer;
  100. + lastUrls = new HashMap<Tab, String>();
  101. + revertingTabs = new HashSet<Tab>();
  102. + }
  103. +
  104. + @Override
  105. + public void onDestroyed(final Tab tab)
  106. + {
  107. + lastUrls.remove(tab);
  108. + revertingTabs.remove(tab);
  109. + }
  110. +
  111. + @Override
  112. + public void onUpdateUrl(Tab tab, GURL gurl) {
  113. +
  114. + if (tab == null) return;
  115. + if (gurl == null) return;
  116. + if (tab.isIncognito()) return;
  117. + if (alwaysIncognitoContainer == null) return;
  118. +
  119. + String spec = gurl.getValidSpecOrEmpty();
  120. + if (spec == null) return;
  121. +
  122. + final String lastUrl = lastUrls.put(tab, spec);
  123. +
  124. + if (!alwaysIncognitoContainer.getBoolean(PREF_ALWAYS_INCOGNITO, false)) return;
  125. + if (revertingTabs.contains(tab)) {
  126. + revertingTabs.remove(tab);
  127. + return;
  128. + }
  129. +
  130. + ((ChromeTabbedActivity)tab.getWindowAndroid().getActivity().get())
  131. + .getTabCreator(true)
  132. + .createNewTab(new LoadUrlParams(spec), TabLaunchType.FROM_LINK, tab);
  133. +
  134. + if ((spec.equals(lastUrl)) || (!tab.canGoBack())) {
  135. + // this call was triggered by a reload
  136. + } else {
  137. + revertingTabs.add(tab);
  138. + tab.goBack();
  139. + }
  140. + }
  141. +}
  142. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
  143. --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
  144. +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
  145. @@ -56,6 +56,7 @@ import org.chromium.base.supplier.UnownedUserDataSupplier;
  146. import org.chromium.base.task.PostTask;
  147. import org.chromium.cc.input.BrowserControlsState;
  148. import org.chromium.chrome.R;
  149. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  150. import org.chromium.chrome.browser.IntentHandler.IntentHandlerDelegate;
  151. import org.chromium.chrome.browser.IntentHandler.TabOpenType;
  152. import org.chromium.chrome.browser.accessibility_tab_switcher.OverviewListLayout;
  153. @@ -1770,8 +1771,9 @@ public class ChromeTabbedActivity extends ChromeActivity<ChromeActivityComponent
  154. Bundle savedInstanceState = getSavedInstanceState();
  155. // We determine the model as soon as possible so every systems get initialized coherently.
  156. - boolean startIncognito = savedInstanceState != null
  157. - && savedInstanceState.getBoolean(IS_INCOGNITO_SELECTED, false);
  158. + boolean startIncognito = ContextUtils.getAppSharedPreferences().getBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false)
  159. + || (savedInstanceState != null
  160. + && savedInstanceState.getBoolean(IS_INCOGNITO_SELECTED, false));
  161. mNextTabPolicySupplier = new ChromeNextTabPolicySupplier(mOverviewModeBehaviorSupplier);
  162. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
  163. --- a/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
  164. +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java
  165. @@ -101,6 +101,7 @@ import org.chromium.chrome.browser.contextualsearch.ContextualSearchFieldTrial;
  166. import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager;
  167. import org.chromium.chrome.browser.contextualsearch.ContextualSearchManager.ContextualSearchTabPromotionDelegate;
  168. import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule;
  169. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  170. import org.chromium.chrome.browser.dependency_injection.ChromeActivityComponent;
  171. import org.chromium.chrome.browser.dependency_injection.ModuleFactoryOverrides;
  172. import org.chromium.chrome.browser.device.DeviceClassManager;
  173. @@ -1891,6 +1892,9 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
  174. throw new IllegalStateException(
  175. "Attempting to access TabCreator before initialization");
  176. }
  177. + if (ContextUtils.getAppSharedPreferences().getBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false)) {
  178. + incognito = true;
  179. + }
  180. return mTabCreatorManagerSupplier.get().getTabCreator(incognito);
  181. }
  182. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
  183. --- a/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
  184. +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/appmenu/AppMenuPropertiesDelegateImpl.java
  185. @@ -538,6 +538,12 @@ public class AppMenuPropertiesDelegateImpl implements AppMenuPropertiesDelegate
  186. }
  187. private void prepareCommonMenuItems(Menu menu, @MenuGroup int menuGroup, boolean isIncognito) {
  188. + if (ContextUtils.getAppSharedPreferences().getBoolean("always_incognito", false)) {
  189. + final MenuItem newTabOption = menu.findItem(R.id.new_tab_menu_id);
  190. + if (newTabOption != null)
  191. + newTabOption.setVisible(false);
  192. + }
  193. +
  194. // We have to iterate all menu items since same menu item ID may be associated with more
  195. // than one menu items.
  196. boolean isOverviewModeMenu = menuGroup == MenuGroup.OVERVIEW_MODE_MENU;
  197. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
  198. --- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
  199. +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
  200. @@ -31,6 +31,7 @@ import org.chromium.base.ContextUtils;
  201. import org.chromium.base.metrics.RecordHistogram;
  202. import org.chromium.base.supplier.Supplier;
  203. import org.chromium.chrome.R;
  204. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  205. import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabCoordinator;
  206. import org.chromium.chrome.browser.contextmenu.ChromeContextMenuItem.Item;
  207. import org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator.ListItemType;
  208. @@ -408,6 +409,10 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
  209. boolean hasSaveImage = false;
  210. mShowEphemeralTabNewLabel = null;
  211. + boolean always_incognito =
  212. + ContextUtils.getAppSharedPreferences().getBoolean(
  213. + AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false);
  214. +
  215. List<Pair<Integer, ModelList>> groupedItems = new ArrayList<>();
  216. if (mParams.isAnchor()) {
  217. @@ -426,6 +431,7 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
  218. linkGroup.add(createListItem(Item.OPEN_IN_NEW_TAB_IN_GROUP));
  219. }
  220. }
  221. +
  222. if (!mItemDelegate.isIncognito() && mItemDelegate.isIncognitoSupported()) {
  223. linkGroup.add(createListItem(Item.OPEN_IN_INCOGNITO_TAB));
  224. }
  225. @@ -450,7 +456,7 @@ public class ChromeContextMenuPopulator implements ContextMenuPopulator {
  226. }
  227. }
  228. if (FirstRunStatus.getFirstRunFlowComplete()) {
  229. - if (!mItemDelegate.isIncognito()
  230. + if ((always_incognito || !mItemDelegate.isIncognito())
  231. && UrlUtilities.isDownloadableScheme(mParams.getLinkUrl())) {
  232. linkGroup.add(createListItem(Item.SAVE_LINK_AS));
  233. }
  234. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java
  235. --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java
  236. +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivityLifecycleUmaTracker.java
  237. @@ -39,31 +39,6 @@ public class CustomTabActivityLifecycleUmaTracker implements PauseResumeWithNati
  238. private boolean mIsInitialResume = true;
  239. private void recordIncognitoLaunchReason() {
  240. - IncognitoCustomTabIntentDataProvider incognitoProvider =
  241. - (IncognitoCustomTabIntentDataProvider) mIntentDataProvider;
  242. -
  243. - @IntentHandler.IncognitoCCTCallerId
  244. - int incognitoCCTCallerId = incognitoProvider.getFeatureIdForMetricsCollection();
  245. - RecordHistogram.recordEnumeratedHistogram("CustomTabs.IncognitoCCTCallerId",
  246. - incognitoCCTCallerId, IntentHandler.IncognitoCCTCallerId.NUM_ENTRIES);
  247. -
  248. - // Record which 1P app launched Incognito CCT.
  249. - if (incognitoCCTCallerId == IntentHandler.IncognitoCCTCallerId.GOOGLE_APPS) {
  250. - String sendersPackageName = incognitoProvider.getSendersPackageName();
  251. - @IntentHandler.ExternalAppId
  252. - int externalId = IntentHandler.mapPackageToExternalAppId(sendersPackageName);
  253. - if (externalId != IntentHandler.ExternalAppId.OTHER) {
  254. - RecordHistogram.recordEnumeratedHistogram("CustomTabs.ClientAppId.Incognito",
  255. - externalId, IntentHandler.ExternalAppId.NUM_ENTRIES);
  256. - } else {
  257. - // Using package name didn't give any meaningful insight on who launched the
  258. - // Incognito CCT, falling back to check if they provided EXTRA_APPLICATION_ID.
  259. - externalId =
  260. - IntentHandler.determineExternalIntentSource(incognitoProvider.getIntent());
  261. - RecordHistogram.recordEnumeratedHistogram("CustomTabs.ClientAppId.Incognito",
  262. - externalId, IntentHandler.ExternalAppId.NUM_ENTRIES);
  263. - }
  264. - }
  265. }
  266. private void recordUserAction() {
  267. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
  268. --- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
  269. +++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabIntentDataProvider.java
  270. @@ -49,6 +49,9 @@ import org.chromium.components.browser_ui.widget.TintedDrawable;
  271. import org.chromium.components.embedder_support.util.UrlConstants;
  272. import org.chromium.device.mojom.ScreenOrientationLockType;
  273. +import org.chromium.base.ContextUtils;
  274. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  275. +
  276. import java.lang.annotation.Retention;
  277. import java.lang.annotation.RetentionPolicy;
  278. import java.util.ArrayList;
  279. @@ -726,7 +729,7 @@ public class CustomTabIntentDataProvider extends BrowserServicesIntentDataProvid
  280. @Override
  281. public boolean isIncognito() {
  282. - return false;
  283. + return ContextUtils.getAppSharedPreferences().getBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false);
  284. }
  285. @Nullable
  286. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
  287. --- a/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
  288. +++ b/chrome/android/java/src/org/chromium/chrome/browser/init/StartupTabPreloader.java
  289. @@ -34,6 +34,9 @@ import org.chromium.content_public.browser.WebContents;
  290. import org.chromium.ui.base.WindowAndroid;
  291. import org.chromium.url.GURL;
  292. +import org.chromium.base.ContextUtils;
  293. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  294. +
  295. /**
  296. * This class attempts to preload the tab if the url is known from the intent when the profile
  297. * is created. This is done to improve startup latency.
  298. @@ -185,17 +188,22 @@ public class StartupTabPreloader implements ProfileManager.Observer, DestroyObse
  299. Intent intent = mIntentSupplier.get();
  300. GURL url = UrlFormatter.fixupUrl(getUrlFromIntent(intent));
  301. + boolean isIncognito = ContextUtils.getAppSharedPreferences().getBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false);
  302. +
  303. + Profile profile = Profile.getLastUsedRegularProfile();
  304. ChromeTabCreator chromeTabCreator =
  305. - (ChromeTabCreator) mTabCreatorManager.getTabCreator(false);
  306. + (ChromeTabCreator) mTabCreatorManager.getTabCreator(isIncognito);
  307. WebContents webContents =
  308. - WebContentsFactory.createWebContents(Profile.getLastUsedRegularProfile(), false);
  309. + WebContentsFactory.createWebContents(
  310. + isIncognito ? profile.getPrimaryOTRProfile(true /* createIfNeeded */) : profile,
  311. + false);
  312. mLoadUrlParams = mIntentHandler.createLoadUrlParamsForIntent(url.getSpec(), intent);
  313. // Create a detached tab, but don't add it to the tab model yet. We'll do that
  314. // later if the loadUrlParams etc... match.
  315. mTab = TabBuilder.createLiveTab(false)
  316. - .setIncognito(false)
  317. + .setIncognito(isIncognito)
  318. .setLaunchType(TabLaunchType.FROM_EXTERNAL_APP)
  319. .setWindow(mWindowAndroid)
  320. .setWebContents(webContents)
  321. 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
  322. --- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
  323. +++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
  324. @@ -29,6 +29,10 @@ import org.chromium.chrome.browser.profiles.Profile;
  325. import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
  326. import org.chromium.chrome.browser.settings.SettingsLauncherImpl;
  327. import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
  328. +import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
  329. +import org.chromium.chrome.browser.ui.messages.snackbar.INeedSnackbarManager;
  330. +import org.chromium.chrome.browser.ui.messages.snackbar.Snackbar;
  331. +import org.chromium.chrome.browser.ApplicationLifetime;
  332. import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
  333. import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
  334. import org.chromium.components.browser_ui.settings.SettingsLauncher;
  335. @@ -43,7 +47,12 @@ import org.chromium.ui.text.SpanApplier;
  336. * Fragment to keep track of the all the privacy related preferences.
  337. */
  338. public class PrivacySettings
  339. - extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener {
  340. + extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener,
  341. + INeedSnackbarManager {
  342. + private SnackbarManager mSnackbarManager;
  343. + private Snackbar mSnackbar;
  344. +
  345. + private static final String PREF_ALWAYS_INCOGNITO = "always_incognito";
  346. private static final String PREF_CAN_MAKE_PAYMENT = "can_make_payment";
  347. private static final String PREF_NETWORK_PREDICTIONS = "preload_pages";
  348. private static final String PREF_HTTPS_FIRST_MODE = "https_first_mode";
  349. @@ -96,6 +105,25 @@ public class PrivacySettings
  350. (ChromeSwitchPreference) findPreference(PREF_CAN_MAKE_PAYMENT);
  351. canMakePaymentPref.setOnPreferenceChangeListener(this);
  352. + ChromeSwitchPreference alwaysIncognitoPref =
  353. + (ChromeSwitchPreference) findPreference(PREF_ALWAYS_INCOGNITO);
  354. + alwaysIncognitoPref.setOnPreferenceChangeListener(this);
  355. +
  356. + mSnackbar = Snackbar.make(getActivity().getString(R.string.ui_relaunch_notice),
  357. + new SnackbarManager.SnackbarController() {
  358. + @Override
  359. + public void onDismissNoAction(Object actionData) { }
  360. +
  361. + @Override
  362. + public void onAction(Object actionData) {
  363. + ApplicationLifetime.terminate(true);
  364. + }
  365. + }, Snackbar.TYPE_NOTIFICATION, Snackbar.UMA_UNKNOWN)
  366. + .setSingleLine(false)
  367. + .setAction(getActivity().getString(R.string.relaunch),
  368. + /*actionData*/null)
  369. + .setDuration(/*durationMs*/70000);
  370. +
  371. ChromeSwitchPreference networkPredictionPref =
  372. (ChromeSwitchPreference) findPreference(PREF_NETWORK_PREDICTIONS);
  373. networkPredictionPref.setChecked(
  374. @@ -156,6 +184,9 @@ public class PrivacySettings
  375. } else if (PREF_NETWORK_PREDICTIONS.equals(key)) {
  376. PrivacyPreferencesManagerImpl.getInstance().setNetworkPredictionEnabled(
  377. (boolean) newValue);
  378. + } else if (PREF_ALWAYS_INCOGNITO.equals(key)) {
  379. + if (!mSnackbarManager.isShowing())
  380. + mSnackbarManager.showSnackbar(mSnackbar);
  381. } else if (PREF_HTTPS_FIRST_MODE.equals(key)) {
  382. UserPrefs.get(Profile.getLastUsedRegularProfile())
  383. .setBoolean(Pref.HTTPS_ONLY_MODE_ENABLED, (boolean) newValue);
  384. @@ -238,4 +269,8 @@ public class PrivacySettings
  385. }
  386. return false;
  387. }
  388. +
  389. + public void setSnackbarManager(SnackbarManager manager) {
  390. + mSnackbarManager = manager;
  391. + }
  392. }
  393. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
  394. --- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
  395. +++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
  396. @@ -51,6 +51,7 @@ import org.chromium.chrome.browser.safety_check.SafetyCheckSettingsFragment;
  397. import org.chromium.chrome.browser.safety_check.SafetyCheckUpdatesDelegateImpl;
  398. import org.chromium.chrome.browser.search_engines.settings.SearchEngineSettings;
  399. import org.chromium.chrome.browser.site_settings.ChromeSiteSettingsDelegate;
  400. +import org.chromium.chrome.browser.ui.messages.snackbar.INeedSnackbarManager;
  401. import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
  402. import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager.SnackbarManageable;
  403. import org.chromium.components.browser_ui.settings.FragmentSettingsLauncher;
  404. @@ -171,6 +172,9 @@ public class SettingsActivity extends ChromeBaseAppCompatActivity
  405. .getSiteSettingsDelegate());
  406. delegate.setSnackbarManager(mSnackbarManager);
  407. }
  408. + if (fragment instanceof INeedSnackbarManager) {
  409. + ((INeedSnackbarManager)fragment).setSnackbarManager(mSnackbarManager);
  410. + }
  411. }
  412. @Override
  413. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
  414. --- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
  415. +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
  416. @@ -121,6 +121,8 @@ import org.chromium.ui.base.DeviceFormFactor;
  417. import org.chromium.ui.base.IntentRequestTracker;
  418. import org.chromium.ui.modaldialog.ModalDialogManager;
  419. import org.chromium.ui.util.TokenHolder;
  420. +import org.chromium.base.ContextUtils;
  421. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  422. /**
  423. * A {@link RootUiCoordinator} variant that controls tabbed-mode specific UI.
  424. @@ -483,11 +485,13 @@ public class TabbedRootUiCoordinator extends RootUiCoordinator {
  425. // TODO(twellington): Supply TabModelSelector as well and move initialization earlier.
  426. if (DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity)) {
  427. + boolean tabModel = ContextUtils.getAppSharedPreferences().getBoolean(
  428. + AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false);
  429. AppMenuHandler appMenuHandler =
  430. mAppMenuCoordinator == null ? null : mAppMenuCoordinator.getAppMenuHandler();
  431. mEmptyBackgroundViewWrapper = new EmptyBackgroundViewWrapper(
  432. mTabModelSelectorSupplier.get(),
  433. - mTabCreatorManagerSupplier.get().getTabCreator(false), mActivity,
  434. + mTabCreatorManagerSupplier.get().getTabCreator(tabModel), mActivity,
  435. appMenuHandler, mSnackbarManagerSupplier.get(), mOverviewModeBehaviorSupplier);
  436. mEmptyBackgroundViewWrapper.initialize();
  437. }
  438. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
  439. --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
  440. +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreator.java
  441. @@ -44,6 +44,10 @@ import org.chromium.url.GURL;
  442. import java.nio.ByteBuffer;
  443. +import org.chromium.base.ContextUtils;
  444. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  445. +import org.chromium.chrome.browser.tab.TabObserver;
  446. +
  447. /**
  448. * This class creates various kinds of new tabs and adds them to the right {@link TabModel}.
  449. */
  450. @@ -74,6 +78,7 @@ public class ChromeTabCreator extends TabCreator {
  451. private final Activity mActivity;
  452. private final StartupTabPreloader mStartupTabPreloader;
  453. private final boolean mIncognito;
  454. + private final TabObserver mExtraLogic;
  455. private WindowAndroid mNativeWindow;
  456. private TabModel mTabModel;
  457. @@ -96,6 +101,10 @@ public class ChromeTabCreator extends TabCreator {
  458. mNativeWindow = nativeWindow;
  459. mTabDelegateFactorySupplier = tabDelegateFactory;
  460. mIncognito = incognito;
  461. + if (!mIncognito)
  462. + mExtraLogic = new AlwaysIncognitoLinkInterceptor(ContextUtils.getAppSharedPreferences());
  463. + else
  464. + mExtraLogic = null;
  465. mOverviewNTPCreator = overviewNTPCreator;
  466. mAsyncTabParamsManager = asyncTabParamsManager;
  467. mTabModelSelectorSupplier = tabModelSelectorSupplier;
  468. @@ -259,6 +268,8 @@ public class ChromeTabCreator extends TabCreator {
  469. if (creationState == TabCreationState.LIVE_IN_FOREGROUND && !openInForeground) {
  470. creationState = TabCreationState.LIVE_IN_BACKGROUND;
  471. }
  472. + if (mExtraLogic != null)
  473. + tab.addObserver(mExtraLogic);
  474. mTabModel.addTab(tab, position, type, creationState);
  475. return tab;
  476. } finally {
  477. @@ -293,6 +304,8 @@ public class ChromeTabCreator extends TabCreator {
  478. @TabCreationState
  479. int creationState = openInForeground ? TabCreationState.LIVE_IN_FOREGROUND
  480. : TabCreationState.LIVE_IN_BACKGROUND;
  481. + if (mExtraLogic != null)
  482. + tab.addObserver(mExtraLogic);
  483. mTabModel.addTab(tab, position, type, creationState);
  484. return true;
  485. }
  486. @@ -333,7 +346,6 @@ public class ChromeTabCreator extends TabCreator {
  487. // TODO(crbug.com/1081924): Clean up the launches from SearchActivity/Chrome.
  488. public Tab launchUrlFromExternalApp(
  489. LoadUrlParams loadUrlParams, String appId, boolean forceNewTab, Intent intent) {
  490. - assert !mIncognito;
  491. // Don't re-use tabs for intents from Chrome. Note that this can be spoofed so shouldn't be
  492. // relied on for anything security sensitive.
  493. boolean isLaunchedFromChrome = TextUtils.equals(appId, mActivity.getPackageName());
  494. @@ -428,6 +440,8 @@ public class ChromeTabCreator extends TabCreator {
  495. .setSerializedCriticalPersistedTabData(serializedCriticalPersistedTabData)
  496. .build();
  497. }
  498. + if (mExtraLogic != null)
  499. + tab.addObserver(mExtraLogic);
  500. if (isIncognito != mIncognito) {
  501. throw new IllegalStateException("Incognito state mismatch. TabState: "
  502. 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
  503. --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
  504. +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabPersistentStore.java
  505. @@ -18,6 +18,7 @@ import androidx.core.util.AtomicFile;
  506. import org.chromium.base.Callback;
  507. import org.chromium.base.CallbackController;
  508. +import org.chromium.base.ContextUtils;
  509. import org.chromium.base.Log;
  510. import org.chromium.base.ObserverList;
  511. import org.chromium.base.StreamUtil;
  512. @@ -55,6 +56,8 @@ import org.chromium.content_public.browser.LoadUrlParams;
  513. import org.chromium.content_public.browser.UiThreadTaskTraits;
  514. import org.chromium.url.GURL;
  515. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  516. +
  517. import java.io.BufferedInputStream;
  518. import java.io.ByteArrayInputStream;
  519. import java.io.ByteArrayOutputStream;
  520. @@ -643,6 +646,13 @@ public class TabPersistentStore {
  521. }
  522. }
  523. }
  524. + if (ContextUtils.getAppSharedPreferences().getBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false)) {
  525. + if (!isIncognito) {
  526. + Log.w(TAG, "Failed to restore tab: not in incognito mode.");
  527. + return;
  528. + }
  529. + }
  530. +
  531. TabModel model = mTabModelSelector.getModel(isIncognito);
  532. if (model.isIncognito() != isIncognito) {
  533. diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
  534. --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
  535. +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappIntentDataProvider.java
  536. @@ -29,6 +29,9 @@ import org.chromium.chrome.browser.flags.ActivityType;
  537. import org.chromium.components.browser_ui.widget.TintedDrawable;
  538. import org.chromium.device.mojom.ScreenOrientationLockType;
  539. +import org.chromium.base.ContextUtils;
  540. +import org.chromium.chrome.browser.AlwaysIncognitoLinkInterceptor;
  541. +
  542. /**
  543. * Stores info about a web app.
  544. */
  545. @@ -42,6 +45,8 @@ public class WebappIntentDataProvider extends BrowserServicesIntentDataProvider
  546. private final Intent mIntent;
  547. private final ColorProviderImpl mColorProvider;
  548. + private boolean mIsIncognito = false;
  549. +
  550. /**
  551. * Returns the toolbar color to use if a custom color is not specified by the webapp.
  552. */
  553. @@ -63,6 +68,10 @@ public class WebappIntentDataProvider extends BrowserServicesIntentDataProvider
  554. mWebappExtras = webappExtras;
  555. mWebApkExtras = webApkExtras;
  556. mActivityType = (webApkExtras != null) ? ActivityType.WEB_APK : ActivityType.WEBAPP;
  557. +
  558. + if (ContextUtils.getAppSharedPreferences().getBoolean(AlwaysIncognitoLinkInterceptor.PREF_ALWAYS_INCOGNITO, false)) {
  559. + mIsIncognito = true;
  560. + }
  561. }
  562. @Override
  563. @@ -151,6 +160,11 @@ public class WebappIntentDataProvider extends BrowserServicesIntentDataProvider
  564. return mWebApkExtras;
  565. }
  566. + @Override
  567. + public boolean isIncognito() {
  568. + return mIsIncognito;
  569. + }
  570. +
  571. @Override
  572. public @ScreenOrientationLockType.EnumType int getDefaultOrientation() {
  573. return mWebappExtras.orientation;
  574. diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
  575. --- a/chrome/browser/flags/android/chrome_feature_list.cc
  576. +++ b/chrome/browser/flags/android/chrome_feature_list.cc
  577. @@ -455,7 +455,7 @@ const base::Feature kCCTIncognito{"CCTIncognito",
  578. base::FEATURE_ENABLED_BY_DEFAULT};
  579. const base::Feature kCCTIncognitoAvailableToThirdParty{
  580. - "CCTIncognitoAvailableToThirdParty", base::FEATURE_DISABLED_BY_DEFAULT};
  581. + "CCTIncognitoAvailableToThirdParty", base::FEATURE_ENABLED_BY_DEFAULT};
  582. const base::Feature kCCTPostMessageAPI{"CCTPostMessageAPI",
  583. base::FEATURE_ENABLED_BY_DEFAULT};
  584. diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
  585. --- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
  586. +++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
  587. @@ -930,6 +930,19 @@ Your Google account may have other forms of browsing history like searches and a
  588. <message name="IDS_CLEAR_BROWSING_HISTORY_SUMMARY_SYNCED_NO_LINK" desc="A text for the basic tab explaining browsing history for users with history sync. This version is shown when the link to MyActivity is displayed separately.">
  589. Clears history from all synced devices.
  590. </message>
  591. + <!-- always incognito -->
  592. + <message name="IDS_ALWAYS_INCOGNITO_TITLE" desc="Title for always incognito mode">
  593. + Open links in incognito tabs always
  594. + </message>
  595. + <message name="IDS_ALWAYS_INCOGNITO_SUMMARY" desc="Summary for always incognito mode">
  596. + Opens links in incognito tabs when you click on new tab or on a link
  597. + </message>
  598. + <message name="IDS_RELAUNCH" desc="Summary for always incognito mode">
  599. + Relaunch
  600. + </message>
  601. + <message name="IDS_UI_RELAUNCH_NOTICE" desc="Summary for always incognito mode">
  602. + Your changes will take effect the next time you relaunch Bromite.
  603. + </message>
  604. <message name="IDS_CLEAR_BROWSING_HISTORY_SUMMARY_SIGNED_IN" desc="A text explaining other forms of activity for signed in users.">
  605. Clears history and autocompletions in the address bar. Your Google Account may have other forms of browsing history at <ph name="BEGIN_LINK">&lt;link&gt;</ph>myactivity.google.com<ph name="END_LINK">&lt;/link&gt;</ph>.
  606. </message>
  607. diff --git a/chrome/browser/ui/messages/android/BUILD.gn b/chrome/browser/ui/messages/android/BUILD.gn
  608. --- a/chrome/browser/ui/messages/android/BUILD.gn
  609. +++ b/chrome/browser/ui/messages/android/BUILD.gn
  610. @@ -22,6 +22,7 @@ android_resources("java_resources") {
  611. android_library("java") {
  612. sources = [
  613. "java/src/org/chromium/chrome/browser/ui/messages/infobar/SimpleConfirmInfoBarBuilder.java",
  614. + "java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java",
  615. "java/src/org/chromium/chrome/browser/ui/messages/snackbar/Snackbar.java",
  616. "java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarCollection.java",
  617. "java/src/org/chromium/chrome/browser/ui/messages/snackbar/SnackbarManager.java",
  618. diff --git a/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java
  619. new file mode 100644
  620. --- /dev/null
  621. +++ b/chrome/browser/ui/messages/android/java/src/org/chromium/chrome/browser/ui/messages/snackbar/INeedSnackbarManager.java
  622. @@ -0,0 +1,27 @@
  623. +/*
  624. + This file is part of Bromite.
  625. +
  626. + Bromite is free software: you can redistribute it and/or modify
  627. + it under the terms of the GNU General Public License as published by
  628. + the Free Software Foundation, either version 3 of the License, or
  629. + (at your option) any later version.
  630. +
  631. + Bromite is distributed in the hope that it will be useful,
  632. + but WITHOUT ANY WARRANTY; without even the implied warranty of
  633. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  634. + GNU General Public License for more details.
  635. +
  636. + You should have received a copy of the GNU General Public License
  637. + along with Bromite. If not, see <https://www.gnu.org/licenses/>.
  638. +*/
  639. +
  640. +package org.chromium.chrome.browser.ui.messages.snackbar;
  641. +
  642. +import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
  643. +
  644. +/**
  645. + * An interface that allows using snackbars in the settings
  646. + */
  647. +public interface INeedSnackbarManager {
  648. + void setSnackbarManager(SnackbarManager manager);
  649. +}
  650. --
  651. 2.20.1