Ver código fonte

Release 90.0.4430.92

csagan5 4 anos atrás
pai
commit
54302084f1
29 arquivos alterados com 1287 adições e 89 exclusões
  1. 4 0
      CHANGELOG.md
  2. 1 1
      build/RELEASE
  3. 1 1
      build/bromite_patches_list.txt
  4. 1 1
      build/patches/AImageReader-CFI-crash-mitigations.patch
  5. 1 1
      build/patches/Add-a-flag-to-allow-screenshots-in-Incognito-mode.patch
  6. 1 1
      build/patches/Add-an-always-incognito-mode.patch
  7. 8 8
      build/patches/Add-bookmark-import-export-actions.patch
  8. 1 1
      build/patches/Add-flag-for-save-data-header.patch
  9. 1 1
      build/patches/Add-flag-to-configure-maximum-connections-per-host.patch
  10. 1 1
      build/patches/Add-flag-to-control-video-playback-resume-feature.patch
  11. 2 2
      build/patches/Add-flag-to-disable-IPv6-probes.patch
  12. 1 1
      build/patches/Add-flag-to-disable-WebGL.patch
  13. 1 1
      build/patches/Add-flags-to-disable-device-motion-orientation-APIs.patch
  14. 2 2
      build/patches/Add-menu-item-to-bookmark-all-tabs.patch
  15. 9 9
      build/patches/Automated-domain-substitution.patch
  16. 2 2
      build/patches/Bromite-AdBlockUpdaterService.patch
  17. 1 1
      build/patches/Disable-safe-browsing.patch
  18. 1 1
      build/patches/Disable-smart-selection-by-default.patch
  19. 2 2
      build/patches/Disable-text-fragments-by-default.patch
  20. 2 2
      build/patches/Enable-darken-websites-checkbox-in-themes.patch
  21. 1 1
      build/patches/Multiple-fingerprinting-mitigations.patch
  22. 1 1
      build/patches/Remove-signin-and-data-saver-integrations.patch
  23. 8 8
      build/patches/Restore-Search-Ready-Omnibox-flag.patch
  24. 8 8
      build/patches/Restore-Simplified-NTP-launch.patch
  25. 0 23
      build/patches/Restore-enable-horizontal-tab-switcher-flag.patch
  26. 1217 0
      build/patches/Revert-Remove-horizontal-tab-switcher-experiment-logic.patch
  27. 4 4
      build/patches/Revert-flags-remove-disable-pull-to-refresh-effect.patch
  28. 4 4
      build/patches/Revert-flags-remove-num-raster-threads.patch
  29. 1 1
      build/patches/User-agent-customization.patch

+ 4 - 0
CHANGELOG.md

@@ -1,3 +1,7 @@
+# 90.0.4430.92
+* restore horizontal tab switcher feature (fixes https://github.com/bromite/bromite/issues/1077)
+* fix a couple of bugs in proxy saving UI (thanks to @uazo, fixes https://github.com/bromite/bromite/issues/1072)
+
 # 90.0.4430.74
 * re-added patch for User agent customization (thanks to @uazo, fixes https://github.com/bromite/bromite/issues/1049)
 * fix always-incognito custom tab intents issues (thanks to @uazo, fixes https://github.com/bromite/bromite/issues/1047 and https://github.com/bromite/bromite/issues/1051)

+ 1 - 1
build/RELEASE

@@ -1 +1 @@
-90.0.4430.74
+90.0.4430.92

+ 1 - 1
build/bromite_patches_list.txt

@@ -107,7 +107,7 @@ Use-dummy-DFM-installer.patch
 Disable-feeds-support-by-default.patch
 Disable-autofill-assistant-by-default.patch
 Show-site-settings-for-cookies-javascript-and-ads.patch
-Restore-enable-horizontal-tab-switcher-flag.patch
+Revert-Remove-horizontal-tab-switcher-experiment-logic.patch
 Disable-DRM-media-origin-IDs-preprovisioning.patch
 Disable-smart-selection-by-default.patch
 Enable-user-agent-freeze-by-default.patch

+ 1 - 1
build/patches/AImageReader-CFI-crash-mitigations.patch

@@ -82,7 +82,7 @@ diff --git a/base/android/android_image_reader_compat.h b/base/android/android_i
 diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
 --- a/chrome/browser/flag-metadata.json
 +++ b/chrome/browser/flag-metadata.json
-@@ -1899,7 +1899,7 @@
+@@ -1909,7 +1909,7 @@
    {
      "name": "enable-image-reader",
      "owners": [ "vikassoni", "liberato" ],

+ 1 - 1
build/patches/Add-a-flag-to-allow-screenshots-in-Incognito-mode.patch

@@ -17,7 +17,7 @@ See also:
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -3100,6 +3100,12 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -3105,6 +3105,12 @@ const FeatureEntry kFeatureEntries[] = {
       FEATURE_VALUE_TYPE(media::kDeprecateLowUsageCodecs)},
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  

+ 1 - 1
build/patches/Add-an-always-incognito-mode.patch

@@ -431,7 +431,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappI
 diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
 --- a/chrome/browser/flags/android/chrome_feature_list.cc
 +++ b/chrome/browser/flags/android/chrome_feature_list.cc
-@@ -400,7 +400,7 @@ const base::Feature kCCTIncognito{"CCTIncognito",
+@@ -404,7 +404,7 @@ const base::Feature kCCTIncognito{"CCTIncognito",
                                    base::FEATURE_ENABLED_BY_DEFAULT};
  
  const base::Feature kCCTIncognitoAvailableToThirdParty{

+ 8 - 8
build/patches/Add-bookmark-import-export-actions.patch

@@ -739,7 +739,7 @@ diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -7303,6 +7303,12 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -7312,6 +7312,12 @@ const FeatureEntry kFeatureEntries[] = {
       FEATURE_VALUE_TYPE(
           optimization_guide::features::kOptimizationGuideModelDownloading)},
  
@@ -1257,7 +1257,7 @@ diff --git a/chrome/browser/download/android/java/src/org/chromium/chrome/browse
 diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
 --- a/chrome/browser/flag_descriptions.cc
 +++ b/chrome/browser/flag_descriptions.cc
-@@ -5179,6 +5179,11 @@ const char kWebrtcPipeWireCapturerDescription[] =
+@@ -5183,6 +5183,11 @@ const char kWebrtcPipeWireCapturerDescription[] =
      "capturing the desktop content on the Wayland display server.";
  #endif  // #if defined(WEBRTC_USE_PIPEWIRE)
  
@@ -1272,7 +1272,7 @@ diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descripti
 diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
 --- a/chrome/browser/flag_descriptions.h
 +++ b/chrome/browser/flag_descriptions.h
-@@ -3051,6 +3051,9 @@ extern const char kWebrtcPipeWireCapturerName[];
+@@ -3054,6 +3054,9 @@ extern const char kWebrtcPipeWireCapturerName[];
  extern const char kWebrtcPipeWireCapturerDescription[];
  #endif  // #if defined(WEBRTC_USE_PIPEWIRE)
  
@@ -1285,15 +1285,15 @@ diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptio
 diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
 --- a/chrome/browser/flags/android/chrome_feature_list.cc
 +++ b/chrome/browser/flags/android/chrome_feature_list.cc
-@@ -140,6 +140,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
-     &kAssistantIntentTranslateInfo,
+@@ -141,6 +141,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
      &kAppLaunchpad,
      &kBentoOffline,
+     &kBookmarkBottomSheet,
 +    &kBookmarksExportUseSaf,
      &kCastDeviceFilter,
      &kCloseTabSuggestions,
      &kCriticalPersistedTabData,
-@@ -742,6 +743,10 @@ const base::Feature kVoiceButtonInTopToolbar{"VoiceButtonInTopToolbar",
+@@ -746,6 +747,10 @@ const base::Feature kVoiceButtonInTopToolbar{"VoiceButtonInTopToolbar",
  const base::Feature kVrBrowsingFeedback{"VrBrowsingFeedback",
                                          base::FEATURE_ENABLED_BY_DEFAULT};
  
@@ -1307,7 +1307,7 @@ diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browse
 diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
 --- a/chrome/browser/flags/android/chrome_feature_list.h
 +++ b/chrome/browser/flags/android/chrome_feature_list.h
-@@ -154,6 +154,7 @@ extern const base::Feature kVoiceSearchAudioCapturePolicy;
+@@ -155,6 +155,7 @@ extern const base::Feature kVoiceSearchAudioCapturePolicy;
  extern const base::Feature kVoiceButtonInTopToolbar;
  extern const base::Feature kVrBrowsingFeedback;
  extern const base::Feature kPrefetchNotificationSchedulingIntegration;
@@ -1318,7 +1318,7 @@ diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser
 diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
 +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
-@@ -503,6 +503,7 @@ public abstract class ChromeFeatureList {
+@@ -504,6 +504,7 @@ public abstract class ChromeFeatureList {
      public static final String WEB_AUTH_PHONE_SUPPORT = "WebAuthenticationPhoneSupport";
      public static final String WEB_FEED = "WebFeed";
      public static final String XSURFACE_METRICS_REPORTING = "XsurfaceMetricsReporting";

+ 1 - 1
build/patches/Add-flag-for-save-data-header.patch

@@ -14,7 +14,7 @@ Subject: Add flag for save-data-header
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -4471,6 +4471,9 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -4476,6 +4476,9 @@ const FeatureEntry kFeatureEntries[] = {
  #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
  
  #if defined(OS_ANDROID)

+ 1 - 1
build/patches/Add-flag-to-configure-maximum-connections-per-host.patch

@@ -31,7 +31,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
  // Ensure that all effective connection types returned by Network Quality
  // Estimator (NQE) are also exposed via flags.
  static_assert(net::EFFECTIVE_CONNECTION_TYPE_LAST + 2 ==
-@@ -3750,6 +3755,9 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -3755,6 +3760,9 @@ const FeatureEntry kFeatureEntries[] = {
       flag_descriptions::kAndroidPictureInPictureAPIName,
       flag_descriptions::kAndroidPictureInPictureAPIDescription, kOsAndroid,
       FEATURE_VALUE_TYPE(media::kPictureInPictureAPI)},

+ 1 - 1
build/patches/Add-flag-to-control-video-playback-resume-feature.patch

@@ -13,7 +13,7 @@ Disable it by default on Android as it is everywhere else
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -2666,6 +2666,10 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -2671,6 +2671,10 @@ const FeatureEntry kFeatureEntries[] = {
       flag_descriptions::kWebRtcRemoteEventLogName,
       flag_descriptions::kWebRtcRemoteEventLogDescription, kOsDesktop,
       FEATURE_VALUE_TYPE(features::kWebRtcRemoteEventLog)},

+ 2 - 2
build/patches/Add-flag-to-disable-IPv6-probes.patch

@@ -16,7 +16,7 @@ Subject: Add flag to disable IPv6 probes
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -4751,6 +4751,11 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -4760,6 +4760,11 @@ const FeatureEntry kFeatureEntries[] = {
  #endif  // defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) ||
          // defined(OS_CHROMEOS)
  
@@ -31,7 +31,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
 --- a/chrome/browser/flag_descriptions.cc
 +++ b/chrome/browser/flag_descriptions.cc
-@@ -2922,6 +2922,10 @@ const char kContextualSearchRankerQueryDescription[] =
+@@ -2926,6 +2926,10 @@ const char kContextualSearchRankerQueryDescription[] =
  
  const char kContextualSearchSecondTapName[] =
      "Contextual Search second tap triggering";

+ 1 - 1
build/patches/Add-flag-to-disable-WebGL.patch

@@ -11,7 +11,7 @@ Subject: Add flag to disable WebGL
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -2619,6 +2619,9 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -2624,6 +2624,9 @@ const FeatureEntry kFeatureEntries[] = {
       flag_descriptions::kAccelerated2dCanvasName,
       flag_descriptions::kAccelerated2dCanvasDescription, kOsAll,
       SINGLE_DISABLE_VALUE_TYPE(switches::kDisableAccelerated2dCanvas)},

+ 1 - 1
build/patches/Add-flags-to-disable-device-motion-orientation-APIs.patch

@@ -20,7 +20,7 @@ legacy acceleration events.
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -2856,6 +2856,12 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -2861,6 +2861,12 @@ const FeatureEntry kFeatureEntries[] = {
      {"enable-gpu-rasterization", flag_descriptions::kGpuRasterizationName,
       flag_descriptions::kGpuRasterizationDescription, kOsAll,
       MULTI_VALUE_TYPE(kEnableGpuRasterizationChoices)},

+ 2 - 2
build/patches/Add-menu-item-to-bookmark-all-tabs.patch

@@ -244,7 +244,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/Bookm
 diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
 --- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
 +++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkUtils.java
-@@ -502,6 +502,7 @@ public class BookmarkUtils {
+@@ -503,6 +503,7 @@ public class BookmarkUtils {
          List<BookmarkId> topLevelFolders = new ArrayList<>();
          BookmarkId desktopNodeId = bookmarkModel.getDesktopFolderId();
          BookmarkId mobileNodeId = bookmarkModel.getMobileFolderId();
@@ -252,7 +252,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/Bookm
          BookmarkId othersNodeId = bookmarkModel.getOtherFolderId();
  
          List<BookmarkId> specialFoldersIds =
-@@ -527,6 +528,9 @@ public class BookmarkUtils {
+@@ -528,6 +529,9 @@ public class BookmarkUtils {
          if (bookmarkModel.isFolderVisible(mobileNodeId)) {
              topLevelFolders.add(mobileNodeId);
          }

+ 9 - 9
build/patches/Automated-domain-substitution.patch

@@ -4020,7 +4020,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
  
  const FeatureEntry::Choice kPassiveListenersChoices[] = {
      {flags_ui::kGenericExperimentChoiceDefault, "", ""},
-@@ -3607,7 +3607,7 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -3612,7 +3612,7 @@ const FeatureEntry kFeatureEntries[] = {
       flag_descriptions::kSyncSandboxDescription, kOsAll,
       SINGLE_VALUE_TYPE_AND_VALUE(
           switches::kSyncServiceURL,
@@ -4029,7 +4029,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
  #if !defined(OS_ANDROID)
      {"load-media-router-component-extension",
       flag_descriptions::kLoadMediaRouterComponentExtensionName,
-@@ -3853,7 +3853,7 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -3858,7 +3858,7 @@ const FeatureEntry kFeatureEntries[] = {
       flag_descriptions::kSetMarketUrlForTestingName,
       flag_descriptions::kSetMarketUrlForTestingDescription, kOsAndroid,
       SINGLE_VALUE_TYPE_AND_VALUE(switches::kMarketUrlForTesting,
@@ -4786,7 +4786,7 @@ diff --git a/chrome/browser/devtools/url_constants.cc b/chrome/browser/devtools/
 diff --git a/chrome/browser/download/mixed_content_download_blocking.cc b/chrome/browser/download/mixed_content_download_blocking.cc
 --- a/chrome/browser/download/mixed_content_download_blocking.cc
 +++ b/chrome/browser/download/mixed_content_download_blocking.cc
-@@ -315,7 +315,7 @@ void PrintConsoleMessage(const MixedContentDownloadData& data,
+@@ -322,7 +322,7 @@ void PrintConsoleMessage(const MixedContentDownloadData& data,
            "connection, but the file at '%s' was %s an insecure "
            "connection. This file should be served over HTTPS. "
            "This download %s. See "
@@ -5000,7 +5000,7 @@ diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descripti
  
  const char kDeviceDiscoveryNotificationsName[] =
      "Device Discovery Notifications";
-@@ -2121,7 +2121,7 @@ const char kIntensiveWakeUpThrottlingName[] =
+@@ -2127,7 +2127,7 @@ const char kIntensiveWakeUpThrottlingName[] =
  const char kIntensiveWakeUpThrottlingDescription[] =
      "When enabled, wake ups from DOM Timers are limited to 1 per minute in a "
      "page that has been hidden for 5 minutes. For additional details, see "
@@ -12739,7 +12739,7 @@ diff --git a/components/optimization_guide/core/optimization_guide_constants.cc
 diff --git a/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc
 --- a/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc
 +++ b/components/page_load_metrics/browser/observers/core/uma_page_load_metrics_observer.cc
-@@ -79,7 +79,7 @@ std::unique_ptr<base::trace_event::TracedValue> FirstInputDelayTraceData(
+@@ -82,7 +82,7 @@ std::unique_ptr<base::trace_event::TracedValue> FirstInputDelayTraceData(
  // language settings update fully launches.
  #if BUILDFLAG(IS_CHROMEOS_ASH)
  void RecordVisitToLanguageSettingsSupportPage(const GURL& url) {
@@ -23296,7 +23296,7 @@ diff --git a/content/browser/portal/portal_navigation_throttle.cc b/content/brow
 diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
 --- a/content/browser/renderer_host/navigation_request.cc
 +++ b/content/browser/renderer_host/navigation_request.cc
-@@ -3921,7 +3921,7 @@ NavigationRequest::CheckCredentialedSubresource() const {
+@@ -3937,7 +3937,7 @@ NavigationRequest::CheckCredentialedSubresource() const {
    const char* console_message =
        "Subresource requests whose URLs contain embedded credentials (e.g. "
        "`https://user:pass@host/`) are blocked. See "
@@ -23305,7 +23305,7 @@ diff --git a/content/browser/renderer_host/navigation_request.cc b/content/brows
        "details.";
    parent->AddMessageToConsole(blink::mojom::ConsoleMessageLevel::kWarning,
                                console_message);
-@@ -3949,7 +3949,7 @@ NavigationRequest::CheckLegacyProtocolInSubresource() const {
+@@ -3965,7 +3965,7 @@ NavigationRequest::CheckLegacyProtocolInSubresource() const {
    const char* console_message =
        "Subresource requests using legacy protocols (like `ftp:`) are blocked. "
        "Please deliver web-accessible resources over modern protocols like "
@@ -23314,7 +23314,7 @@ diff --git a/content/browser/renderer_host/navigation_request.cc b/content/brows
        "details.";
    parent->AddMessageToConsole(blink::mojom::ConsoleMessageLevel::kWarning,
                                console_message);
-@@ -4183,7 +4183,7 @@ void NavigationRequest::RecordDownloadUseCountersPrePolicyCheck(
+@@ -4199,7 +4199,7 @@ void NavigationRequest::RecordDownloadUseCountersPrePolicyCheck(
          base::StringPrintf(
              "Navigating a cross-origin opener to a download (%s) is "
              "deprecated, see "
@@ -28905,7 +28905,7 @@ diff --git a/third_party/blink/renderer/modules/manifest/fuzzer_seed_corpus/play
 diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
 --- a/third_party/blink/renderer/modules/payments/payment_request.cc
 +++ b/third_party/blink/renderer/modules/payments/payment_request.cc
-@@ -95,9 +95,9 @@ using ::payments::mojom::blink::PaymentValidationErrors;
+@@ -96,9 +96,9 @@ using ::payments::mojom::blink::PaymentValidationErrors;
  using ::payments::mojom::blink::PaymentValidationErrorsPtr;
  
  const char kHasEnrolledInstrumentDebugName[] = "hasEnrolledInstrument";

+ 2 - 2
build/patches/Bromite-AdBlockUpdaterService.patch

@@ -628,7 +628,7 @@ diff --git a/chrome/browser/flags/android/cached_feature_flags.cc b/chrome/brows
 diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
 +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
-@@ -260,6 +260,14 @@ public class CachedFeatureFlags {
+@@ -261,6 +261,14 @@ public class CachedFeatureFlags {
                          ChromeFeatureList.REACHED_CODE_PROFILER, "sampling_interval_us", 0));
      }
  
@@ -643,7 +643,7 @@ diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/f
      /**
       * Caches flags that must take effect on startup but are set via native code.
       */
-@@ -426,5 +434,7 @@ public class CachedFeatureFlags {
+@@ -427,5 +435,7 @@ public class CachedFeatureFlags {
      @NativeMethods
      interface Natives {
          boolean isNetworkServiceWarmUpEnabled();

+ 1 - 1
build/patches/Disable-safe-browsing.patch

@@ -410,7 +410,7 @@ diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -2150,7 +2150,7 @@ const FeatureEntry::FeatureParam
+@@ -2155,7 +2155,7 @@ const FeatureEntry::FeatureParam
          {QuietNotificationPermissionUiConfig::kEnableAbusiveRequestWarning,
           "true"},
          {QuietNotificationPermissionUiConfig::kEnableCrowdDenyTriggering,

+ 1 - 1
build/patches/Disable-smart-selection-by-default.patch

@@ -15,7 +15,7 @@ leak information through the TextClassifier set by OEM, if any
 diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
 --- a/chrome/browser/flags/android/chrome_feature_list.cc
 +++ b/chrome/browser/flags/android/chrome_feature_list.cc
-@@ -518,7 +518,7 @@ const base::Feature kDownloadProgressInfoBar{"DownloadProgressInfoBar",
+@@ -523,7 +523,7 @@ const base::Feature kDownloadProgressInfoBar{"DownloadProgressInfoBar",
                                               base::FEATURE_ENABLED_BY_DEFAULT};
  
  const base::Feature kDownloadFileProvider{"DownloadFileProvider",

+ 2 - 2
build/patches/Disable-text-fragments-by-default.patch

@@ -17,7 +17,7 @@ Revert "[Text Fragment] Unflag fragment directive removal."
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -5282,6 +5282,7 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -5296,6 +5296,7 @@ const FeatureEntry kFeatureEntries[] = {
           kHappinessTrackingSurveysForDesktopDevToolsIssuesCookiesSameSiteDescription,
       kOsDesktop,
       FEATURE_VALUE_TYPE(
@@ -28,7 +28,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
 --- a/chrome/browser/flag-metadata.json
 +++ b/chrome/browser/flag-metadata.json
-@@ -2755,7 +2755,7 @@
+@@ -2765,7 +2765,7 @@
    {
      "name": "ev-details-in-page-info",
      "owners": [ "cthomp" ],

+ 2 - 2
build/patches/Enable-darken-websites-checkbox-in-themes.patch

@@ -11,7 +11,7 @@ Unexpire #darken-websites-checkbox-in-themes-setting
 diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
 --- a/chrome/browser/flag-metadata.json
 +++ b/chrome/browser/flag-metadata.json
-@@ -1099,7 +1099,7 @@
+@@ -1109,7 +1109,7 @@
    {
      "name": "disable-keepalive-fetch",
      "owners": [ "yhirano" ],
@@ -23,7 +23,7 @@ diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.js
 diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
 --- a/chrome/browser/flags/android/chrome_feature_list.cc
 +++ b/chrome/browser/flags/android/chrome_feature_list.cc
-@@ -506,7 +506,7 @@ const base::Feature kContextualSearchTranslations{
+@@ -511,7 +511,7 @@ const base::Feature kContextualSearchTranslations{
      "ContextualSearchTranslations", base::FEATURE_DISABLED_BY_DEFAULT};
  
  const base::Feature kDarkenWebsitesCheckboxInThemesSetting{

+ 1 - 1
build/patches/Multiple-fingerprinting-mitigations.patch

@@ -75,7 +75,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
  #include "storage/browser/quota/quota_features.h"
  #include "third_party/blink/public/common/experiments/memory_ablation_experiment.h"
  #include "third_party/blink/public/common/features.h"
-@@ -2674,6 +2675,18 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -2679,6 +2680,18 @@ const FeatureEntry kFeatureEntries[] = {
      {"enable-webrtc-srtp-aes-gcm", flag_descriptions::kWebrtcSrtpAesGcmName,
       flag_descriptions::kWebrtcSrtpAesGcmDescription, kOsAll,
       SINGLE_VALUE_TYPE(switches::kEnableWebRtcSrtpAesGcm)},

+ 1 - 1
build/patches/Remove-signin-and-data-saver-integrations.patch

@@ -184,7 +184,7 @@ diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser
  import org.chromium.components.sync.ModelType;
  
  /**
-@@ -157,9 +156,7 @@ public class PriceTrackingUtilities {
+@@ -165,9 +164,7 @@ public class PriceTrackingUtilities {
      }
  
      private static boolean isOpenTabsSyncEnabled() {

+ 8 - 8
build/patches/Restore-Search-Ready-Omnibox-flag.patch

@@ -54,7 +54,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggest
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -3255,6 +3255,9 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -3260,6 +3260,9 @@ const FeatureEntry kFeatureEntries[] = {
       flag_descriptions::kAndroidAutofillAccessibilityName,
       flag_descriptions::kAndroidAutofillAccessibilityDescription, kOsAndroid,
       FEATURE_VALUE_TYPE(features::kAndroidAutofillAccessibility)},
@@ -67,7 +67,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
 --- a/chrome/browser/flag-metadata.json
 +++ b/chrome/browser/flag-metadata.json
-@@ -2161,6 +2161,11 @@
+@@ -2166,6 +2166,11 @@
      //  with neural net palm detection.
      "expiry_milestone": 90
    },
@@ -82,7 +82,7 @@ diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.js
 diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
 --- a/chrome/browser/flag_descriptions.cc
 +++ b/chrome/browser/flag_descriptions.cc
-@@ -3325,6 +3325,11 @@ const char kSafeBrowsingUseLocalBlacklistsV2Description[] =
+@@ -3329,6 +3329,11 @@ const char kSafeBrowsingUseLocalBlacklistsV2Description[] =
      "process to check the Safe Browsing reputation of URLs without calling "
      "into GmsCore for every URL.";
  
@@ -97,7 +97,7 @@ diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descripti
 diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
 --- a/chrome/browser/flag_descriptions.h
 +++ b/chrome/browser/flag_descriptions.h
-@@ -1920,6 +1920,9 @@ extern const char kSafeBrowsingSectionUiAndroidDescription[];
+@@ -1923,6 +1923,9 @@ extern const char kSafeBrowsingSectionUiAndroidDescription[];
  extern const char kSafeBrowsingUseLocalBlacklistsV2Name[];
  extern const char kSafeBrowsingUseLocalBlacklistsV2Description[];
  
@@ -110,7 +110,7 @@ diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptio
 diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
 --- a/chrome/browser/flags/android/chrome_feature_list.cc
 +++ b/chrome/browser/flags/android/chrome_feature_list.cc
-@@ -211,6 +211,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
+@@ -212,6 +212,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
      &kReachedCodeProfiler,
      &kReaderModeInCCT,
      &kReengagementNotification,
@@ -118,7 +118,7 @@ diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browse
      &kRelatedSearches,
      &kRelatedSearchesUi,
      &kSearchEnginePromoExistingDevice,
-@@ -602,6 +603,9 @@ const base::Feature kRelatedSearches{"RelatedSearches",
+@@ -606,6 +607,9 @@ const base::Feature kRelatedSearches{"RelatedSearches",
  const base::Feature kRelatedSearchesUi{"RelatedSearchesUi",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
  
@@ -131,7 +131,7 @@ diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browse
 diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
 --- a/chrome/browser/flags/android/chrome_feature_list.h
 +++ b/chrome/browser/flags/android/chrome_feature_list.h
-@@ -106,6 +106,7 @@ extern const base::Feature kRelatedSearches;
+@@ -107,6 +107,7 @@ extern const base::Feature kRelatedSearches;
  extern const base::Feature kRelatedSearchesUi;
  extern const base::Feature kSearchEnginePromoExistingDevice;
  extern const base::Feature kSearchEnginePromoNewDevice;
@@ -142,7 +142,7 @@ diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser
 diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
 +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
-@@ -374,6 +374,7 @@ public abstract class ChromeFeatureList {
+@@ -375,6 +375,7 @@ public abstract class ChromeFeatureList {
      public static final String OMNIBOX_ADAPTIVE_SUGGESTIONS_COUNT =
              "OmniboxAdaptiveSuggestionsCount";
      public static final String OMNIBOX_ASSISTANT_VOICE_SEARCH = "OmniboxAssistantVoiceSearch";

+ 8 - 8
build/patches/Restore-Simplified-NTP-launch.patch

@@ -39,7 +39,7 @@ diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_ja
 diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
 --- a/chrome/android/chrome_java_sources.gni
 +++ b/chrome/android/chrome_java_sources.gni
-@@ -591,6 +591,7 @@ chrome_java_sources = [
+@@ -592,6 +592,7 @@ chrome_java_sources = [
    "java/src/org/chromium/chrome/browser/feedback/FeedFeedbackCollector.java",
    "java/src/org/chromium/chrome/browser/feedback/HelpAndFeedbackLauncherImpl.java",
    "java/src/org/chromium/chrome/browser/feedback/ScreenshotTask.java",
@@ -306,7 +306,7 @@ diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/Sug
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -3831,6 +3831,9 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -3836,6 +3836,9 @@ const FeatureEntry kFeatureEntries[] = {
       SINGLE_VALUE_TYPE_AND_VALUE(
           switches::kForceShowUpdateMenuItemCustomSummary,
           "Custom Summary")},
@@ -319,7 +319,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
 --- a/chrome/browser/flag_descriptions.cc
 +++ b/chrome/browser/flag_descriptions.cc
-@@ -3364,6 +3364,9 @@ const char kStartSurfaceAndroidDescription[] =
+@@ -3374,6 +3374,9 @@ const char kStartSurfaceAndroidDescription[] =
      "Enable showing the start surface when launching Chrome via the "
      "launcher.";
  
@@ -332,7 +332,7 @@ diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descripti
 diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
 --- a/chrome/browser/flag_descriptions.h
 +++ b/chrome/browser/flag_descriptions.h
-@@ -1944,6 +1944,9 @@ extern const char kSiteIsolationForPasswordSitesDescription[];
+@@ -1950,6 +1950,9 @@ extern const char kSiteIsolationForPasswordSitesDescription[];
  extern const char kSmartSuggestionForLargeDownloadsName[];
  extern const char kSmartSuggestionForLargeDownloadsDescription[];
  
@@ -345,7 +345,7 @@ diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptio
 diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
 --- a/chrome/browser/flags/android/chrome_feature_list.cc
 +++ b/chrome/browser/flags/android/chrome_feature_list.cc
-@@ -193,6 +193,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
+@@ -194,6 +194,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
      &kEphemeralTabUsingBottomSheet,
      &kExperimentsForAgsa,
      &kExploreSites,
@@ -353,7 +353,7 @@ diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browse
      &kFocusOmniboxInIncognitoTabIntents,
      &kHandleMediaIntents,
      &kHomepagePromoCard,
-@@ -579,6 +580,9 @@ const base::Feature kOfflineIndicatorV2{"OfflineIndicatorV2",
+@@ -587,6 +588,9 @@ const base::Feature kOfflineIndicatorV2{"OfflineIndicatorV2",
  const base::Feature kOfflineMeasurementsBackgroundTask{
      "OfflineMeasurementsBackgroundTask", base::FEATURE_DISABLED_BY_DEFAULT};
  
@@ -366,7 +366,7 @@ diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browse
 diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
 --- a/chrome/browser/flags/android/chrome_feature_list.h
 +++ b/chrome/browser/flags/android/chrome_feature_list.h
-@@ -97,6 +97,7 @@ extern const base::Feature kNotificationSuspender;
+@@ -99,6 +99,7 @@ extern const base::Feature kNotificationSuspender;
  extern const base::Feature kOfflineIndicatorV2;
  extern const base::Feature kOfflineMeasurementsBackgroundTask;
  extern const base::Feature kOmniboxSpareRenderer;
@@ -377,7 +377,7 @@ diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser
 diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
 +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
-@@ -352,6 +352,7 @@ public abstract class ChromeFeatureList {
+@@ -354,6 +354,7 @@ public abstract class ChromeFeatureList {
      public static final String LOOKALIKE_NAVIGATION_URL_SUGGESTIONS_UI =
              "LookalikeUrlNavigationSuggestionsUI";
      public static final String MARK_HTTP_AS = "MarkHttpAs";

+ 0 - 23
build/patches/Restore-enable-horizontal-tab-switcher-flag.patch

@@ -1,23 +0,0 @@
-From: csagan5 <32685696+csagan5@users.noreply.github.com>
-Date: Sat, 4 Jan 2020 14:49:43 +0100
-Subject: Restore enable-horizontal-tab-switcher flag
-
----
- chrome/browser/flag-metadata.json | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
---- a/chrome/browser/flag-metadata.json
-+++ b/chrome/browser/flag-metadata.json
-@@ -1889,7 +1889,7 @@
-   {
-     "name": "enable-hosted-app-quit-notification",
-     "owners": [ "ccameron" ],
--    "expiry_milestone": 77
-+    "expiry_milestone": -1
-   },
-   {
-     "name": "enable-hostname-setting",
--- 
-2.17.1
-

+ 1217 - 0
build/patches/Revert-Remove-horizontal-tab-switcher-experiment-logic.patch

@@ -0,0 +1,1217 @@
+From: csagan5 <32685696+csagan5@users.noreply.github.com>
+Date: Sat, 4 Jan 2020 14:49:43 +0100
+Subject: Revert "Remove horizontal tab switcher experiment logic"
+
+This reverts commit 35156bbe110b1049894e7125e0ba2026340ada0a.
+---
+ chrome/android/chrome_java_sources.gni        |   1 +
+ .../browser/app/flags/ChromeCachedFlags.java  |   1 +
+ .../browser/compositor/LayerTitleCache.java   |   6 +-
+ .../compositor/layouts/phone/StackLayout.java |  43 +-
+ .../layouts/phone/StackLayoutBase.java        |  47 +-
+ .../phone/stack/NonOverlappingStack.java      | 504 ++++++++++++++++++
+ .../compositor/layouts/phone/stack/Stack.java |  12 +-
+ .../layouts/phone/stack/StackAnimation.java   |  56 +-
+ .../scene_layer/TabListSceneLayer.java        |   9 +-
+ .../survey/ChromeSurveyController.java        |  20 +-
+ .../chrome/browser/toolbar/ToolbarColors.java |   6 +-
+ .../toolbar/top/TabSwitcherModeTTPhone.java   |  20 +-
+ .../survey/ChromeSurveyControllerTest.java    |   2 +
+ chrome/browser/about_flags.cc                 |   5 +
+ chrome/browser/flag-metadata.json             |   5 +
+ chrome/browser/flag_descriptions.cc           |   6 +
+ chrome/browser/flag_descriptions.h            |   3 +
+ .../flags/android/chrome_feature_list.cc      |   4 +
+ .../flags/android/chrome_feature_list.h       |   1 +
+ .../browser/flags/CachedFeatureFlags.java     |   1 +
+ .../browser/flags/ChromeFeatureList.java      |   1 +
+ 21 files changed, 733 insertions(+), 20 deletions(-)
+ create mode 100644 chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/NonOverlappingStack.java
+
+diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
+--- a/chrome/android/chrome_java_sources.gni
++++ b/chrome/android/chrome_java_sources.gni
+@@ -324,6 +324,7 @@ chrome_java_sources = [
+   "java/src/org/chromium/chrome/browser/compositor/layouts/phone/SimpleAnimationLayout.java",
+   "java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayout.java",
+   "java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java",
++  "java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/NonOverlappingStack.java",
+   "java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/OverlappingStack.java",
+   "java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java",
+   "java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java",
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/app/flags/ChromeCachedFlags.java
+@@ -78,6 +78,7 @@ public class ChromeCachedFlags {
+                 ChromeFeatureList.CONDITIONAL_TAB_STRIP_ANDROID,
+                 ChromeFeatureList.DOWNLOADS_AUTO_RESUMPTION_NATIVE,
+                 ChromeFeatureList.EARLY_LIBRARY_LOAD,
++                ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID,
+                 ChromeFeatureList.IMMERSIVE_UI_MODE,
+                 ChromeFeatureList.INSTANT_START,
+                 ChromeFeatureList.INTEREST_FEED_V2,
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
+@@ -15,6 +15,7 @@ import org.chromium.base.annotations.JNINamespace;
+ import org.chromium.base.annotations.NativeMethods;
+ import org.chromium.chrome.R;
+ import org.chromium.chrome.browser.compositor.layouts.content.TitleBitmapFactory;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.profiles.Profile;
+ import org.chromium.chrome.browser.tab.Tab;
+ import org.chromium.chrome.browser.tab.TabFavicon;
+@@ -22,6 +23,7 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+ import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
+ import org.chromium.chrome.browser.ui.favicon.FaviconHelper.DefaultFaviconHelper;
+ import org.chromium.chrome.browser.ui.favicon.FaviconHelper.FaviconImageCallback;
++import org.chromium.ui.base.DeviceFormFactor;
+ import org.chromium.ui.base.LocalizationUtils;
+ import org.chromium.ui.resources.ResourceManager;
+ import org.chromium.ui.resources.dynamics.BitmapDynamicResource;
+@@ -117,7 +119,9 @@ public class LayerTitleCache implements TitleCache {
+     private String getUpdatedTitleInternal(Tab tab, String titleString,
+             boolean fetchFaviconFromHistory) {
+         final int tabId = tab.getId();
+-        boolean isDarkTheme = tab.isIncognito();
++        boolean isHTSEnabled = !DeviceFormFactor.isNonMultiDisplayContextOnTablet(tab.getContext())
++                && ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID);
++        boolean isDarkTheme = tab.isIncognito() && !isHTSEnabled;
+         Bitmap originalFavicon = TabFavicon.getBitmap(tab);
+         if (originalFavicon == null) {
+             originalFavicon = mDefaultFaviconHelper.getDefaultFaviconBitmap(
+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
+--- 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
+@@ -12,6 +12,7 @@ import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider
+ import org.chromium.chrome.browser.compositor.layouts.LayoutRenderHost;
+ import org.chromium.chrome.browser.compositor.layouts.LayoutUpdateHost;
+ import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
++import org.chromium.chrome.browser.compositor.layouts.phone.stack.NonOverlappingStack;
+ import org.chromium.chrome.browser.tab.Tab;
+ import org.chromium.chrome.browser.tabmodel.TabList;
+ import org.chromium.chrome.browser.tabmodel.TabModel;
+@@ -131,8 +132,38 @@ public class StackLayout extends StackLayoutBase {
+ 
+     @Override
+     public void onTabModelSwitched(boolean toIncognitoTabModel) {
+-        flingStacks(toIncognitoTabModel ? INCOGNITO_STACK_INDEX : NORMAL_STACK_INDEX);
+-        mFlingFromModelChange = true;
++        if (isHorizontalTabSwitcherFlagEnabled()) {
++            // Don't allow switching between normal and incognito again until the animations finish.
++            mAnimatingStackSwitch = true;
++
++            // Make sure we update the tab switcher's background color even if no tabs are open and
++            // therefore neither the switch away nor switch to animations run.
++            requestUpdate();
++
++            NonOverlappingStack oldStack = (NonOverlappingStack) mStacks.get(
++                    toIncognitoTabModel ? NORMAL_STACK_INDEX : INCOGNITO_STACK_INDEX);
++            oldStack.runSwitchAwayAnimation(toIncognitoTabModel
++                            ? NonOverlappingStack.SwitchDirection.LEFT
++                            : NonOverlappingStack.SwitchDirection.RIGHT);
++        } else {
++            flingStacks(toIncognitoTabModel ? INCOGNITO_STACK_INDEX : NORMAL_STACK_INDEX);
++            mFlingFromModelChange = true;
++        }
++    }
++
++    @Override
++    public void onSwitchAwayFinished() {
++        int newStackIndex = getTabStackIndex(Tab.INVALID_TAB_ID);
++        mRenderedScrollOffset = -newStackIndex;
++        NonOverlappingStack newStack = (NonOverlappingStack) mStacks.get(newStackIndex);
++        newStack.runSwitchToAnimation(newStackIndex == INCOGNITO_STACK_INDEX
++                        ? NonOverlappingStack.SwitchDirection.LEFT
++                        : NonOverlappingStack.SwitchDirection.RIGHT);
++    }
++
++    @Override
++    public void onSwitchToFinished() {
++        mAnimatingStackSwitch = false;
+     }
+ 
+     @Override
+@@ -147,6 +178,10 @@ public class StackLayout extends StackLayoutBase {
+ 
+     @Override
+     protected int getMinRenderedScrollOffset() {
++        // If the horizontal tab switcher flag is enabled, we let the user tap the incognito button
++        // to switch to incognito mode, even if no incognito tabs are open.
++        if (isHorizontalTabSwitcherFlagEnabled()) return -1;
++
+         // If there's at least one incognito tab open, or we're in the process of switching back
+         // from incognito to normal mode, return -1 so we don't cause any clamping. Otherwise,
+         // return 0 to prevent scrolling.
+@@ -170,6 +205,10 @@ public class StackLayout extends StackLayoutBase {
+ 
+     @Override
+     protected @SwipeMode int computeInputMode(long time, float x, float y, float dx, float dy) {
++        // If this experiment flag is enabled, we add an incognito toggle button to the toolbar, and
++        // disable swiping between the stacks.
++        if (isHorizontalTabSwitcherFlagEnabled()) return SwipeMode.SEND_TO_STACK;
++
+         if (mStacks.size() == 2 && !mStacks.get(1).isDisplayable()) return SwipeMode.SEND_TO_STACK;
+         return super.computeInputMode(time, x, y, dx, dy);
+     }
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/StackLayoutBase.java
+@@ -34,10 +34,13 @@ import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
+ import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+ import org.chromium.chrome.browser.compositor.layouts.eventfilter.GestureEventFilter;
+ import org.chromium.chrome.browser.compositor.layouts.eventfilter.GestureHandler;
++import org.chromium.chrome.browser.compositor.layouts.phone.stack.NonOverlappingStack;
+ import org.chromium.chrome.browser.compositor.layouts.phone.stack.OverlappingStack;
+ import org.chromium.chrome.browser.compositor.layouts.phone.stack.Stack;
+ import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackTab;
+ import org.chromium.chrome.browser.compositor.scene_layer.TabListSceneLayer;
++import org.chromium.chrome.browser.flags.CachedFeatureFlags;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.homepage.HomepageManager;
+ import org.chromium.chrome.browser.layouts.EventFilter;
+ import org.chromium.chrome.browser.layouts.LayoutType;
+@@ -446,12 +449,20 @@ public abstract class StackLayoutBase extends Layout {
+         mScrollIndexOffset = v;
+     }
+ 
++    /**
++     * Whether or not the HorizontalTabSwitcherAndroid flag (which enables the new horizontal tab
++     * switcher in both portrait and landscape mode) is enabled.
++     */
++    protected boolean isHorizontalTabSwitcherFlagEnabled() {
++        return CachedFeatureFlags.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID);
++    }
++
+     /**
+      * Whether or not we're currently having the tabs scroll horizontally (as opposed to
+      * vertically).
+      */
+     private boolean isUsingHorizontalLayout() {
+-        return getOrientation() == Orientation.LANDSCAPE;
++        return getOrientation() == Orientation.LANDSCAPE || isHorizontalTabSwitcherFlagEnabled();
+     }
+ 
+     /**
+@@ -464,7 +475,12 @@ public abstract class StackLayoutBase extends Layout {
+             mStacks.subList(lists.size(), mStacks.size()).clear();
+         }
+         while (mStacks.size() < lists.size()) {
+-            Stack stack = new OverlappingStack(getContext(), this);
++            Stack stack;
++            if (isHorizontalTabSwitcherFlagEnabled()) {
++                stack = new NonOverlappingStack(getContext(), this);
++            } else {
++                stack = new OverlappingStack(getContext(), this);
++            }
+             stack.notifySizeChanged(mWidth, mHeight, mOrientation);
+             mStacks.add(stack);
+         }
+@@ -751,6 +767,18 @@ public abstract class StackLayoutBase extends Layout {
+         }
+     }
+ 
++    /**
++     * Called by a NonOverlappingStack that's had switchAwayEffect() called on it, once the
++     * animation has finished.
++     */
++    public void onSwitchAwayFinished() {}
++
++    /**
++     * Called by a NonOverlappingStack that's had switchToEffect() called on it, once the
++     * animation has finished.
++     */
++    public void onSwitchToFinished() {}
++
+     /**
+      * Called when layout-specific actions are needed after the animation finishes.
+      */
+@@ -840,7 +868,7 @@ public abstract class StackLayoutBase extends Layout {
+         // Tabs don't overlap in the horizontal tab switcher experiment, so the order comparator
+         // already does what we want (the visibility comparator's logic actually doesn't compute
+         // visibility properly in this case).
+-        mSortingComparator = mVisibilityComparator;
++        if (!isHorizontalTabSwitcherFlagEnabled()) mSortingComparator = mVisibilityComparator;
+         doneShowing();
+     }
+ 
+@@ -1140,6 +1168,10 @@ public abstract class StackLayoutBase extends Layout {
+ 
+         @Override
+         float getInnerMargin() {
++            // If we're using the new horizontal tab switcher, don't show the edge of the other
++            // stack (normal if in incognito mode and incognito if in normal mode) on-screen.
++            if (isHorizontalTabSwitcherFlagEnabled()) return 0;
++
+             float margin = mInnerMarginPercent
+                     * Math.max(mMinMaxInnerMargin, mWidth * INNER_MARGIN_PERCENT_PERCENT);
+             return margin;
+@@ -1197,6 +1229,7 @@ public abstract class StackLayoutBase extends Layout {
+             // Need getHeight() for this case instead of getHeightMinusBrowserControls() so the
+             // normal stack goes up high enough to clear the status bar when the incognito stack is
+             // active.
++            if (isHorizontalTabSwitcherFlagEnabled()) return StackLayoutBase.this.getHeight();
+             return Math.round(mWidth - getInnerMargin());
+         }
+     }
+@@ -1350,7 +1383,8 @@ public abstract class StackLayoutBase extends Layout {
+             // If the non-overlapping horizontal tab switcher experiment is enabled, we pass -1 so
+             // NonOverlappingStack can use the scroll position to keep the index used for visibility
+             // prioritization up-to-date.
+-            final boolean useFixedIndex = mSortingComparator == mOrderComparator;
++            final boolean useFixedIndex =
++                    mSortingComparator == mOrderComparator && !isHorizontalTabSwitcherFlagEnabled();
+             mStacks.get(i).setStackFocusInfo(
+                     stackFocus, useFixedIndex ? mStacks.get(i).getTabList().index() : -1);
+         }
+@@ -1423,6 +1457,11 @@ public abstract class StackLayoutBase extends Layout {
+      * @return The distance between two neighboring tab stacks.
+      */
+     private float getFullScrollDistance() {
++        // For the horizontal tab switcher experiment, we use getHeight() instead of
++        // getHeightMinusBrowserControls() to make sure the normal stack goes up enough to clear the
++        // status bar when switching to incognito mode.
++        if (isHorizontalTabSwitcherFlagEnabled()) return getHeight();
++
+         float distance = isUsingHorizontalLayout() ? getHeightMinusContentOffsetsDp() : getWidth();
+         if (mStacks.size() > 2) {
+             return distance - getViewportParameters().getInnerMargin();
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/NonOverlappingStack.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/NonOverlappingStack.java
+new file mode 100644
+--- /dev/null
++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/NonOverlappingStack.java
+@@ -0,0 +1,504 @@
++// Copyright 2018 The Chromium Authors. All rights reserved.
++// Use of this source code is governed by a BSD-style license that can be
++// found in the LICENSE file.
++
++package org.chromium.chrome.browser.compositor.layouts.phone.stack;
++
++import android.animation.Animator;
++import android.animation.AnimatorListenerAdapter;
++import android.animation.AnimatorSet;
++import android.content.Context;
++
++import androidx.annotation.IntDef;
++
++import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
++import org.chromium.chrome.browser.compositor.layouts.phone.StackLayoutBase;
++import org.chromium.chrome.browser.layouts.animation.CompositorAnimationHandler;
++import org.chromium.chrome.browser.layouts.animation.CompositorAnimator;
++
++import java.lang.annotation.Retention;
++import java.lang.annotation.RetentionPolicy;
++import java.util.ArrayList;
++import java.util.Collection;
++
++/**
++ * The non-overlapping tab stack we use when the HorizontalTabSwitcherAndroid flag is enabled.
++ */
++public class NonOverlappingStack extends Stack {
++    @IntDef({SwitchDirection.LEFT, SwitchDirection.RIGHT})
++    @Retention(RetentionPolicy.SOURCE)
++    public @interface SwitchDirection {
++        int LEFT = 0;
++        int RIGHT = 1;
++    }
++
++    /**
++     * The scale the tabs should be shown at when there's exactly one tab open.
++     */
++    private static final float SCALE_FRACTION_SINGLE_TAB = 0.80f;
++
++    /**
++     * The scale the tabs should be shown at when there are two or more tabs open.
++     */
++    private static final float SCALE_FRACTION_MULTIPLE_TABS = 0.54f;
++
++    /**
++     * The percentage of the screen that defines the spacing between tabs by default (no pinch).
++     */
++    private static final float SPACING_SCREEN = 1.0f;
++
++    /**
++     * Controls how far we slide over the (up to) three visible tabs for the switch away and switch
++     * to animations (multiple of mSpacing).
++     */
++    private static final float SWITCH_ANIMATION_SPACING_MULTIPLE = 2.5f;
++
++    /**
++     * Duration of the switch away animation (in milliseconds).
++     */
++    private static final int SWITCH_AWAY_ANIMATION_DURATION = 250;
++
++    /**
++     * Duration of the switch to animation (in milliseconds).
++     */
++    private static final int SWITCH_TO_ANIMATION_DURATION = 250;
++
++    /**
++     * Adjustment to add a fixed amount of space between the tabs that's not based on a percentage
++     * of the screen (if were 0, the tab borders would actually overlap in the current
++     * implementation).
++     */
++    private static final float EXTRA_SPACE_BETWEEN_TABS_DP = 25.0f;
++
++    /**
++     * How much the stack should adjust the y position of each LayoutTab in portrait mode (as a
++     * fraction of the amount space that would be above and below the tab if it were centered).
++     */
++    private static final float STACK_PORTRAIT_Y_OFFSET_PROPORTION = 0.f;
++
++    /**
++     * How much the stack should adjust the x position of each LayoutTab in landscape mode (as a
++     * fraction of the amount space that would be to the left and right of the tab if it were
++     * centered).
++     */
++    private static final float STACK_LANDSCAPE_START_OFFSET_PROPORTION = 0.f;
++
++    /**
++     * How much the stack should adjust the x position of each LayoutTab in portrait mode (as a
++     * fraction of the amount space that would be above and below the tab if it were centered).
++     */
++    private static final float STACK_LANDSCAPE_Y_OFFSET_PROPORTION = 0.f;
++
++    /**
++     * Multiplier for adjusting the scrolling friction from the amount provided by
++     * ViewConfiguration.
++     */
++    private static final float FRICTION_MULTIPLIER = 0.6f;
++
++    /**
++     * For short scrolls of duration less than this (in milliseconds), we assume the user wants to
++     * scroll over to the next tab. If the scroll is longer in duration, we assume they're
++     * reconsidering their scroll, so we leave them on the current tab (unless they drag over far
++     * enough to center a new tab).
++     */
++    private static final int SCROLL_BOOST_TIMEOUT_MS = 250;
++
++    /**
++     * The minimum fraction of a tab the user has to scroll over by before we apply the boost to
++     * scroll them to the next tab.
++     */
++    private static final float SCROLL_BOOST_THRESHOLD = 0.05f;
++
++    /**
++     * Used to prevent mScrollOffset from being changed as a result of clamping during the switch
++     * away/switch to animations.
++     */
++    private boolean mSuppressScrollClamping;
++
++    /**
++     * Whether or not the current stack has been "switched away" by having runSwitchAwayAnimation()
++     * called. Calling runSwitchToAnimation() resets this back to false. Checking this variable lets
++     * us avoid re-playing animations if they're triggered multiple times.
++     */
++    private boolean mSwitchedAway;
++
++    /** Time at which the last touch down event occurred. */
++    private long mLastTouchDownTime;
++    /** Index of the tab that was centered when the last touch down event occurred. */
++    private int mCenteredTabAtTouchDown;
++
++    /**
++     * @param layout The parent layout.
++     */
++    public NonOverlappingStack(Context context, StackLayoutBase layout) {
++        super(context, layout);
++    }
++
++    private int getNonDyingTabCount() {
++        if (mStackTabs == null) return 0;
++
++        int dyingCount = 0;
++        for (int i = 0; i < mStackTabs.length; i++) {
++            if (mStackTabs[i].isDying()) dyingCount++;
++        }
++        return mStackTabs.length - dyingCount;
++    }
++
++    @Override
++    public float getScaleAmount() {
++        if (getNonDyingTabCount() > 1) return SCALE_FRACTION_MULTIPLE_TABS;
++        return SCALE_FRACTION_SINGLE_TAB;
++    }
++
++    @Override
++    protected void finishAnimation(long time) {
++        super.finishAnimation(time);
++        mSuppressScrollClamping = false;
++    }
++
++    @Override
++    protected boolean evenOutTabs(float amount, boolean allowReverseDirection) {
++        // Nothing to do here; tabs are always a fixed distance apart in NonOverlappingStack (except
++        // during tab close/un-close animations)
++        return false;
++    }
++
++    private void updateScrollSnap() {
++        mScroller.setFrictionMultiplier(FRICTION_MULTIPLIER);
++        // This is what computeSpacing() returns when there are >= 2 tabs
++        final int snapDistance =
++                (int) Math.round(getScrollDimensionSize() * SCALE_FRACTION_MULTIPLE_TABS
++                        + EXTRA_SPACE_BETWEEN_TABS_DP);
++        // Really we're scrolling in the x direction, but the scroller is always wired up to the y
++        // direction for both portrait and landscape mode.
++        mScroller.setYSnapDistance(snapDistance);
++    }
++
++    @Override
++    public void contextChanged(Context context) {
++        super.contextChanged(context);
++        updateScrollSnap();
++    }
++
++    /**
++     * @return The index of the currently centered tab. If we're not currently snapped to a tab
++     *         (e.g. we're in the process of animating a scroll or the user is currently dragging),
++     *         returns the index of the tab closest to the center.
++     */
++    public int getCenteredTabIndex() {
++        return Math.round(-mScrollOffset / mSpacing);
++    }
++
++    @Override
++    public void notifySizeChanged(float width, float height, int orientation) {
++        super.notifySizeChanged(width, height, orientation);
++
++        int centeredTab = getCenteredTabIndex();
++        mSpacing = computeSpacing(0);
++        updateScrollOffsets(centeredTab);
++
++        updateScrollSnap();
++    }
++
++    @Override
++    public void onDown(long time) {
++        super.onDown(time);
++        mLastTouchDownTime = time;
++        mCenteredTabAtTouchDown = getCenteredTabIndex();
++        mScroller.setCenteredYSnapIndexAtTouchDown(mCenteredTabAtTouchDown);
++    }
++
++    @Override
++    public void onLongPress(long time, float x, float y) {
++        // Ignore long presses
++    }
++
++    @Override
++    public void onPinch(long time, float x0, float y0, float x1, float y1, boolean firstEvent) {
++        return;
++    }
++
++    @Override
++    protected void springBack(long time) {
++        if (!mScroller.isFinished()) return;
++
++        int offsetAtTouchDown = -mCenteredTabAtTouchDown * mSpacing;
++        float scrollFractionToNextTab = (offsetAtTouchDown - mScrollOffset) / mSpacing;
++
++        int newCenteredTab;
++        // Make quick, short scrolls go over to the next tab (if a scroll is short but not quick, we
++        // assume the user might have decided to stay on the current tab).
++        if (time < mLastTouchDownTime + SCROLL_BOOST_TIMEOUT_MS
++                && Math.abs(scrollFractionToNextTab) > SCROLL_BOOST_THRESHOLD) {
++            newCenteredTab = mCenteredTabAtTouchDown + (int) Math.signum(scrollFractionToNextTab);
++        } else {
++            newCenteredTab = getCenteredTabIndex();
++        }
++
++        int newTarget = -newCenteredTab * mSpacing;
++        mScroller.flingYTo((int) mScrollTarget, newTarget, time);
++        setScrollTarget(newTarget, false);
++        mLayout.requestUpdate();
++    }
++
++    @Override
++    protected float getSpacingScreen() {
++        return SPACING_SCREEN;
++    }
++
++    @Override
++    protected boolean shouldStackTabsAtTop() {
++        return false;
++    }
++
++    @Override
++    protected boolean shouldStackTabsAtBottom() {
++        return false;
++    }
++
++    @Override
++    protected float getStackPortraitYOffsetProportion() {
++        return STACK_PORTRAIT_Y_OFFSET_PROPORTION;
++    }
++
++    @Override
++    protected float getStackLandscapeStartOffsetProportion() {
++        return STACK_LANDSCAPE_START_OFFSET_PROPORTION;
++    }
++
++    @Override
++    protected float getStackLandscapeYOffsetProportion() {
++        return STACK_LANDSCAPE_Y_OFFSET_PROPORTION;
++    }
++
++    @Override
++    protected void computeTabClippingVisibilityHelper() {
++        // Performance optimization: we don't need to draw any tab other than the centered one, the
++        // one immediately to the left, and the two immediately to the right (we need the second
++        // one for discard animations) since the others can't possibly be on screen.
++        int centeredTab = getCenteredTabIndex();
++        for (int i = 0; i < mStackTabs.length; i++) {
++            LayoutTab layoutTab = mStackTabs[i].getLayoutTab();
++            if (i < centeredTab - 1 || i > centeredTab + 2) {
++                layoutTab.setVisible(false);
++            } else {
++                layoutTab.setVisible(true);
++            }
++        }
++    }
++
++    @Override
++    protected int computeReferenceIndex() {
++        return getCenteredTabIndex();
++    }
++
++    @Override
++    protected boolean shouldCloseGapsBetweenTabs() {
++        return false;
++    }
++
++    @Override
++    protected float getMinScroll(boolean allowUnderScroll) {
++        if (mSuppressScrollClamping) return -Float.MAX_VALUE;
++
++        if (mStackTabs == null) return 0;
++        for (int i = mStackTabs.length - 1; i >= 0; i--) {
++            // The getScrollOffset() != 0 check avoids a bug when undiscarding the last tab, in
++            // which case the tab's scroll offset is initially set to 0, which would cause us to
++            // immediately center the first tab. If 0 is the correct offset to return, it's the
++            // default value anyway after going through all the tabs.
++            if (!mStackTabs[i].isDying() && mStackTabs[i].getScrollOffset() != 0) {
++                return -mStackTabs[i].getScrollOffset();
++            }
++        }
++
++        return 0;
++    }
++
++    @Override
++    protected boolean allowOverscroll() {
++        return false;
++    }
++
++    @Override
++    protected int computeSpacing(int layoutTabCount) {
++        return (int) Math.round(
++                getScrollDimensionSize() * getScaleAmount() + EXTRA_SPACE_BETWEEN_TABS_DP);
++    }
++
++    /**
++     * Updates the overall scroll offset and the scroll offsets for each tab based on the current
++     * value of mSpacing so that the tabs are in the proper locations and the specified tab is
++     * centered.
++     *
++     * @param centeredTab The index of the tab that should be centered.
++     */
++    private void updateScrollOffsets(int centeredTab) {
++        // Reset the tabs' scroll offsets.
++        if (mStackTabs != null) {
++            for (int i = 0; i < mStackTabs.length; i++) {
++                mStackTabs[i].setScrollOffset(i * mSpacing);
++            }
++        }
++
++        // Reset the overall scroll offset.
++        mScrollOffset = -centeredTab * mSpacing;
++        setScrollTarget(mScrollOffset, false);
++    }
++
++    @Override
++    protected void resetAllScrollOffset() {
++        if (mTabList == null) return;
++        updateScrollOffsets(mTabList.index());
++    }
++
++    // NonOverlappingStack uses linear scrolling, so screenToScroll() and scrollToScreen() are both
++    // just the identity function.
++    @Override
++    public float screenToScroll(float screenSpace) {
++        return screenSpace;
++    }
++
++    @Override
++    public float scrollToScreen(float scrollSpace) {
++        return scrollSpace;
++    }
++
++    @Override
++    public float getMaxTabHeight() {
++        // We want to maintain a constant tab height (via cropping) even as the width is changed as
++        // a result of changing the scale.
++        if (getNonDyingTabCount() > 1) return mLayout.getHeight();
++        return (SCALE_FRACTION_MULTIPLE_TABS / SCALE_FRACTION_SINGLE_TAB) * mLayout.getHeight();
++    }
++
++    /**
++     * This method sets mSuppressScrollClamping to true to allow an animation to animate the
++     * mScrollOffset outside the normal bounds. It will be reset to false when finishAnimation() is
++     * called.
++     */
++    public void suppressScrollClampingForAnimation() {
++        mSuppressScrollClamping = true;
++    }
++
++    /**
++     * Animates the (up to 3) visible tabs sliding off screen.
++     * @param direction Whether the tabs should slide off the left or right side of the screen.
++     */
++    public void runSwitchAwayAnimation(@SwitchDirection int direction) {
++        if (mStackTabs == null || mSwitchedAway) {
++            mSwitchedAway = true;
++            mLayout.onSwitchAwayFinished();
++            return;
++        }
++
++        mSwitchedAway = true;
++        mSuppressScrollClamping = true;
++
++        // Make sure we don't leave any tabs stuck in a partially-discarded state.
++        for (int i = 0; i < mStackTabs.length; i++) {
++            mStackTabs[i].setDiscardAmount(0);
++        }
++
++        // Make sure the tabs are not scrolling so the centered tab does not change between the
++        // "switch away" and "switch to" animations.
++        forceScrollStop();
++
++        CompositorAnimationHandler handler = mLayout.getAnimationHandler();
++        Collection<Animator> animationList = new ArrayList<>();
++
++        int centeredTab = getCenteredTabIndex();
++        for (int i = centeredTab - 1; i <= centeredTab + 1; i++) {
++            if (i < 0 || i >= mStackTabs.length) continue;
++            StackTab tab = mStackTabs[i];
++
++            float endOffset;
++            if (direction == SwitchDirection.LEFT) {
++                endOffset = -SWITCH_ANIMATION_SPACING_MULTIPLE * mSpacing + tab.getScrollOffset();
++            } else {
++                endOffset = SWITCH_ANIMATION_SPACING_MULTIPLE * mSpacing + tab.getScrollOffset();
++            }
++
++            CompositorAnimator animation =
++                    CompositorAnimator.ofFloatProperty(handler, tab, StackTab.SCROLL_OFFSET,
++                            tab.getScrollOffset(), endOffset, SWITCH_AWAY_ANIMATION_DURATION);
++            animationList.add(animation);
++        }
++
++        AnimatorSet set = new AnimatorSet();
++        set.playTogether(animationList);
++        set.addListener(new AnimatorListenerAdapter() {
++            @Override
++            public void onAnimationEnd(Animator a) {
++                // If the user pressed the incognito button with one finger while dragging the stack
++                // with another, we might not be centered on a tab. We therefore need to enforce
++                // this after the animation finishes to avoid odd behavior if/when the user returns
++                // to this stack.
++
++                mScrollOffset = Math.round(mScrollOffset / mSpacing) * mSpacing;
++                forceScrollStop();
++                mLayout.onSwitchAwayFinished();
++            }
++        });
++        set.start();
++    }
++
++    /**
++     * Animates the (up to 3) tabs were slid off the screen by runSwitchAwayAnimation() back onto
++     * the screen.
++     * @param direction Whether the tabs should slide in from the left or right side of the screen.
++     */
++    public void runSwitchToAnimation(@SwitchDirection int direction) {
++        if (mStackTabs == null || !mSwitchedAway) {
++            mSwitchedAway = false;
++            mLayout.onSwitchToFinished();
++            return;
++        }
++
++        mSwitchedAway = false;
++        mSuppressScrollClamping = true;
++
++        CompositorAnimationHandler handler = mLayout.getAnimationHandler();
++        Collection<Animator> animationList = new ArrayList<>();
++
++        int centeredTab = getCenteredTabIndex();
++        for (int i = centeredTab - 1; i <= centeredTab + 1; i++) {
++            if (i < 0 || i >= mStackTabs.length) continue;
++            StackTab tab = mStackTabs[i];
++
++            float startOffset;
++            if (direction == SwitchDirection.LEFT) {
++                startOffset = SWITCH_ANIMATION_SPACING_MULTIPLE * mSpacing + tab.getScrollOffset();
++            } else {
++                startOffset = -SWITCH_ANIMATION_SPACING_MULTIPLE * mSpacing + tab.getScrollOffset();
++            }
++
++            CompositorAnimator animation =
++                    CompositorAnimator.ofFloatProperty(handler, tab, StackTab.SCROLL_OFFSET,
++                            startOffset, i * mSpacing, SWITCH_TO_ANIMATION_DURATION);
++            animationList.add(animation);
++        }
++
++        AnimatorSet set = new AnimatorSet();
++        set.playTogether(animationList);
++        set.addListener(new AnimatorListenerAdapter() {
++            @Override
++            public void onAnimationEnd(Animator a) {
++                // There are some oddball cases, e.g. a tab was immediately closed before triggering
++                // the toggle animation, where the tab offsets might get left in an undesired state.
++                // Resetting all the scroll offsets here limits the effect of these bugs to just
++                // making the animation look funny vs. leaving the tabs e.g. stuck with gaps between
++                // them or overlapping each other.
++                if (mStackTabs != null) {
++                    for (int i = 0; i < mStackTabs.length; i++) {
++                        mStackTabs[i].setScrollOffset(i * mSpacing);
++                    }
++                }
++
++                mSuppressScrollClamping = false;
++                mLayout.onSwitchToFinished();
++            }
++        });
++        set.start();
++    }
++}
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/Stack.java
+@@ -21,6 +21,8 @@ import org.chromium.chrome.browser.compositor.layouts.Layout.Orientation;
+ import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
+ import org.chromium.chrome.browser.compositor.layouts.phone.StackLayoutBase;
+ import org.chromium.chrome.browser.compositor.layouts.phone.stack.StackAnimation.OverviewAnimationType;
++import org.chromium.chrome.browser.flags.CachedFeatureFlags;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.layouts.animation.CompositorAnimationHandler;
+ import org.chromium.chrome.browser.layouts.animation.FloatProperty;
+ import org.chromium.chrome.browser.tab.Tab;
+@@ -357,6 +359,10 @@ public abstract class Stack {
+      *              we should put it on the left. This method already accounts for RTL flipping.
+      */
+     private boolean isCloseButtonOnRight() {
++        if (ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)) {
++            return !LocalizationUtils.isLayoutRtl();
++        }
++
+         return mCurrentMode == Orientation.PORTRAIT ^ LocalizationUtils.isLayoutRtl();
+     }
+ 
+@@ -2025,7 +2031,11 @@ public abstract class Stack {
+     }
+ 
+     protected void updateCurrentMode(@Orientation int orientation) {
+-        mCurrentMode = orientation;
++        if (CachedFeatureFlags.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)) {
++            mCurrentMode = Orientation.LANDSCAPE;
++        } else {
++            mCurrentMode = orientation;
++        }
+ 
+         mDiscardDirection = getDefaultDiscardDirection();
+         final float opaqueTopPadding = mBorderTopPadding - mBorderTransparentTop;
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/stack/StackAnimation.java
+@@ -13,6 +13,7 @@ import androidx.annotation.IntDef;
+ import org.chromium.base.MathUtils;
+ import org.chromium.chrome.browser.compositor.layouts.Layout.Orientation;
+ import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.layouts.animation.CompositorAnimationHandler;
+ import org.chromium.chrome.browser.layouts.animation.CompositorAnimator;
+ import org.chromium.chrome.browser.layouts.animation.FloatProperty;
+@@ -353,6 +354,11 @@ public class StackAnimation {
+         }
+     }
+ 
++    // If this flag is enabled, we're using the non-overlapping tab switcher.
++    private boolean isHorizontalTabSwitcherFlagEnabled() {
++        return ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID);
++    }
++
+     private void createPortraitEnterStackAnimatorSet(
+             StackAnimatorSet stackAnimatorSet, StackTab[] tabs, int focusIndex, int spacing) {
+         final float initialScrollOffset = mStack.screenToScroll(0);
+@@ -531,8 +537,11 @@ public class StackAnimation {
+             if (mOrientation == Orientation.LANDSCAPE) {
+                 stackAnimatorSet.addToAnimation(tab, StackTab.X_IN_STACK_INFLUENCE,
+                         tab.getXInStackInfluence(), 0.0f, TAB_FOCUSED_ANIMATION_DURATION_MS, null);
+-                stackAnimatorSet.addToAnimation(tab, StackTab.SCROLL_OFFSET, tab.getScrollOffset(),
+-                        mStack.screenToScroll(0), TAB_FOCUSED_ANIMATION_DURATION_MS, null);
++                if (!isHorizontalTabSwitcherFlagEnabled()) {
++                    stackAnimatorSet.addToAnimation(tab, StackTab.SCROLL_OFFSET,
++                            tab.getScrollOffset(), mStack.screenToScroll(0),
++                            TAB_FOCUSED_ANIMATION_DURATION_MS, null);
++                }
+             } else { // mOrientation == Orientation.PORTRAIT
+                 stackAnimatorSet.addToAnimation(tab, StackTab.SCROLL_OFFSET, tab.getScrollOffset(),
+                         Math.max(0.0f, tab.getScrollOffset() - mWidth - spacing),
+@@ -610,10 +619,12 @@ public class StackAnimation {
+             // If the non-overlapping horizontal tab switcher is enabled, we shift all the
+             // tabs over simultaneously. Otherwise we stagger the animation start times to
+             // create a ripple effect.
+-            long startTime = (long) Math.max(0,
+-                    TAB_REORDER_START_SPAN / screenSizeInScrollDirection
+-                            * (getLandscapePortraitScreenPositionInScrollDirection(tab)
+-                                    - firstDyingTabOffset));
++            long startTime = isHorizontalTabSwitcherFlagEnabled()
++                    ? 0
++                    : (long) Math.max(0,
++                            TAB_REORDER_START_SPAN / screenSizeInScrollDirection
++                                    * (getLandscapePortraitScreenPositionInScrollDirection(tab)
++                                            - firstDyingTabOffset));
+             if (tab.isDying()) {
+                 float discard = tab.getDiscardAmount();
+                 if (discard == 0.0f) discard = defaultDiscardDirectionPositive ? 0.0f : -0.0f;
+@@ -653,6 +664,39 @@ public class StackAnimation {
+                 newIndex++;
+             }
+         }
++
++        // Scroll offset animation for non-overlapping horizontal tab switcher (if enabled)
++        if (isHorizontalTabSwitcherFlagEnabled()) {
++            NonOverlappingStack nonOverlappingStack = (NonOverlappingStack) stack;
++            int centeredTabIndex = nonOverlappingStack.getCenteredTabIndex();
++
++            // For all tab closures (except for the last one), we slide the remaining tabs
++            // in to fill the gap.
++            //
++            // There are two cases where we also need to animate the NonOverlappingStack's
++            // overall scroll position over by one tab:
++            //
++            // - Closing the last tab while centered on it (since we don't have a tab we can
++            //   slide over to replace it)
++            //
++            // - Closing any tab prior to the currently centered one (so we can keep the
++            //   same tab centered). Together with animating the individual scroll offsets for
++            //   each tab, this has the visual appearance of sliding in the prior tabs from the
++            //   left (in LTR mode) to fill the gap.
++            boolean closingLastTabWhileCentered =
++                    firstDyingTabIndex == tabs.length - 1 && firstDyingTabIndex == centeredTabIndex;
++            boolean closingPriorTab =
++                    firstDyingTabIndex != -1 && firstDyingTabIndex < centeredTabIndex;
++
++            boolean shouldAnimateStackScrollOffset = closingLastTabWhileCentered || closingPriorTab;
++
++            if (shouldAnimateStackScrollOffset) {
++                nonOverlappingStack.suppressScrollClampingForAnimation();
++                stackAnimatorSet.addToAnimation(nonOverlappingStack, Stack.SCROLL_OFFSET,
++                        stack.getScrollOffset(), -(centeredTabIndex - 1) * stack.getSpacing(),
++                        TAB_REORDER_DURATION_MS, null);
++            }
++        }
+     }
+ 
+     /**
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabListSceneLayer.java
+@@ -18,6 +18,7 @@ import org.chromium.chrome.browser.compositor.LayerTitleCache;
+ import org.chromium.chrome.browser.compositor.layouts.Layout;
+ import org.chromium.chrome.browser.compositor.layouts.components.LayoutTab;
+ import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.layouts.scene_layer.SceneLayer;
+ import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+ import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
+@@ -97,6 +98,9 @@ public class TabListSceneLayer extends SceneLayer {
+                     backgroundResourceId, backgroundAlpha, backgroundTopOffset);
+         }
+ 
++        boolean isHTSEnabled =
++                ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID);
++
+         final float shadowAlpha = ColorUtils.shouldUseLightForegroundOnBackground(tabListBgColor)
+                 ? LayoutTab.SHADOW_ALPHA_ON_DARK_BG
+                 : LayoutTab.SHADOW_ALPHA_ON_LIGHT_BG;
+@@ -107,7 +111,7 @@ public class TabListSceneLayer extends SceneLayer {
+             final float decoration = t.getDecorationAlpha();
+ 
+             int urlBarBackgroundId = R.drawable.modern_location_bar;
+-            boolean useIncognitoColors = t.isIncognito();
++            boolean useIncognitoColors = t.isIncognito() && !isHTSEnabled;
+ 
+             int defaultThemeColor = ChromeColors.getDefaultThemeColor(res, useIncognitoColors);
+ 
+@@ -161,7 +165,8 @@ public class TabListSceneLayer extends SceneLayer {
+     protected int getTabListBackgroundColor(Context context) {
+         int colorId = R.color.default_bg_color;
+ 
+-        if (TabUiFeatureUtilities.isGridTabSwitcherEnabled()) {
++        if (ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)
++                || TabUiFeatureUtilities.isGridTabSwitcherEnabled()) {
+             if (mTabModelSelector != null && mTabModelSelector.isIncognitoSelected()) {
+                 colorId = R.color.default_bg_color_dark;
+             } else {
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java b/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/survey/ChromeSurveyController.java
+@@ -18,6 +18,7 @@ import org.chromium.base.metrics.RecordHistogram;
+ import org.chromium.base.metrics.RecordUserAction;
+ import org.chromium.base.task.AsyncTask;
+ import org.chromium.chrome.R;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.flags.ChromeSwitches;
+ import org.chromium.chrome.browser.infobar.InfoBarContainer;
+ import org.chromium.chrome.browser.infobar.InfoBarIdentifier;
+@@ -138,7 +139,10 @@ public class ChromeSurveyController implements InfoBarAnimationListener {
+             }
+         };
+ 
+-        String siteContext = ChromeVersionInfo.getProductVersion() + ",NotHorizontalTabSwitcher";
++        String siteContext = ChromeVersionInfo.getProductVersion() + ","
++                + (ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)
++                                  ? "HorizontalTabSwitcher"
++                                  : "NotHorizontalTabSwitcher");
+         surveyController.downloadSurvey(context, siteId, onSuccessRunnable, siteContext);
+     }
+ 
+@@ -317,6 +321,20 @@ public class ChromeSurveyController implements InfoBarAnimationListener {
+         }
+ 
+         int maxNumber = getMaxNumber();
++
++        int maxForHorizontalTabSwitcher = -1;
++        if (ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)) {
++            maxForHorizontalTabSwitcher = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
++                    ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID, MAX_NUMBER, -1);
++        }
++        if (maxForHorizontalTabSwitcher != -1) {
++            if (maxNumber == -1) {
++                maxNumber = maxForHorizontalTabSwitcher;
++            } else {
++                maxNumber = Math.min(maxNumber, maxForHorizontalTabSwitcher);
++            }
++        }
++
+         if (maxNumber == -1) {
+             recordSurveyFilteringResult(FilteringResult.MAX_NUMBER_MISSING);
+             return false;
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarColors.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarColors.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarColors.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarColors.java
+@@ -5,6 +5,7 @@
+ package org.chromium.chrome.browser.toolbar;
+ 
+ import org.chromium.chrome.browser.device.DeviceClassManager;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.tasks.tab_management.TabUiFeatureUtilities;
+ import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
+ 
+@@ -17,8 +18,11 @@ public class ToolbarColors {
+      */
+     public static boolean canUseIncognitoToolbarThemeColorInOverview() {
+         final boolean isAccessibilityEnabled = DeviceClassManager.enableAccessibilityLayout();
++        final boolean isHorizontalTabSwitcherEnabled = ChromeFeatureList.isInitialized()
++                && ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID);
+         final boolean isTabGridEnabled = TabUiFeatureUtilities.isGridTabSwitcherEnabled();
+         final boolean isStartSurfaceEnabled = StartSurfaceConfiguration.isStartSurfaceEnabled();
+-        return (isAccessibilityEnabled || isTabGridEnabled || isStartSurfaceEnabled);
++        return (isAccessibilityEnabled || isHorizontalTabSwitcherEnabled || isTabGridEnabled
++                || isStartSurfaceEnabled);
+     }
+ }
+diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
+--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
++++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/TabSwitcherModeTTPhone.java
+@@ -18,6 +18,7 @@ import androidx.appcompat.content.res.AppCompatResources;
+ 
+ import org.chromium.chrome.R;
+ import org.chromium.chrome.browser.device.DeviceClassManager;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.incognito.IncognitoUtils;
+ import org.chromium.chrome.browser.tabmodel.IncognitoStateProvider;
+ import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+@@ -241,6 +242,11 @@ public class TabSwitcherModeTTPhone extends OptimizedFrameLayout
+     void onAccessibilityStatusChanged(boolean enabled) {
+         if (mNewTabImageButton != null) mNewTabImageButton.onAccessibilityStatusChanged();
+ 
++        if (ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)
++                && IncognitoUtils.isIncognitoModeEnabled()) {
++            updateTabSwitchingElements(!enabled);
++        }
++
+         updatePrimaryColorAndTint();
+     }
+ 
+@@ -291,7 +297,8 @@ public class TabSwitcherModeTTPhone extends OptimizedFrameLayout
+             // the tab switcher, which is the standard mode background. Note that horizontal tab
+             // switcher is an exception, which uses the correspond background color for standard
+             // and incognito mode.
+-            int backgroundColor = ChromeColors.getPrimaryBackgroundColor(getResources(), false);
++            int backgroundColor = ChromeColors.getPrimaryBackgroundColor(
++                    getResources(), usingHorizontalTabSwitcher() && mIsIncognito);
+             useLightIcons = ColorUtils.shouldUseLightForegroundOnBackground(backgroundColor);
+         } else {
+             useLightIcons = ColorUtils.shouldUseLightForegroundOnBackground(primaryColor);
+@@ -322,6 +329,14 @@ public class TabSwitcherModeTTPhone extends OptimizedFrameLayout
+         return Color.TRANSPARENT;
+     }
+ 
++    private boolean usingHorizontalTabSwitcher() {
++        // The horizontal tab switcher flag does not affect the accessibility switcher. We do the
++        // enableAccessibilityLayout() check first here to avoid logging an experiment exposure for
++        // these users.
++        return !DeviceClassManager.enableAccessibilityLayout()
++                && ChromeFeatureList.isEnabled(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID);
++    }
++
+     private void inflateIncognitoToggle() {
+         ViewStub incognitoToggleTabsStub = findViewById(R.id.incognito_tabs_stub);
+         mIncognitoToggleTabLayout = (IncognitoToggleTabLayout) incognitoToggleTabsStub.inflate();
+@@ -358,6 +373,7 @@ public class TabSwitcherModeTTPhone extends OptimizedFrameLayout
+      *         and incognito status.
+      */
+     private boolean shouldShowIncognitoToggle() {
+-        return mIsGridTabSwitcherEnabled && IncognitoUtils.isIncognitoModeEnabled();
++        return (usingHorizontalTabSwitcher() || mIsGridTabSwitcherEnabled)
++                && IncognitoUtils.isIncognitoModeEnabled();
+     }
+ }
+diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
+--- a/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
++++ b/chrome/android/junit/src/org/chromium/chrome/browser/survey/ChromeSurveyControllerTest.java
+@@ -21,6 +21,7 @@ import org.mockito.MockitoAnnotations;
+ import org.robolectric.annotation.Config;
+ 
+ import org.chromium.base.test.BaseRobolectricTestRunner;
++import org.chromium.chrome.browser.flags.ChromeFeatureList;
+ import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+ import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+ import org.chromium.chrome.browser.tab.Tab;
+@@ -33,6 +34,7 @@ import org.chromium.content_public.browser.WebContents;
+  */
+ @RunWith(BaseRobolectricTestRunner.class)
+ @Config(manifest = Config.NONE)
++@Features.EnableFeatures(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID)
+ public class ChromeSurveyControllerTest {
+     private static final String STUDY_NAME = "HorizontalTabSwitcherStudyName";
+     private static final String GROUP_NAME = "HorizontalTabSwitcherGroupName";
+diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
+--- a/chrome/browser/about_flags.cc
++++ b/chrome/browser/about_flags.cc
+@@ -5024,6 +5024,11 @@ const FeatureEntry kFeatureEntries[] = {
+      flag_descriptions::kCriticalPersistedTabDataDescription, kOsAndroid,
+      FEATURE_VALUE_TYPE(chrome::android::kCriticalPersistedTabData)},
+ 
++    {"enable-horizontal-tab-switcher",
++     flag_descriptions::kHorizontalTabSwitcherAndroidName,
++     flag_descriptions::kHorizontalTabSwitcherAndroidDescription, kOsAndroid,
++     FEATURE_VALUE_TYPE(chrome::android::kHorizontalTabSwitcherAndroid)},
++
+     {"enable-tab-grid-layout", flag_descriptions::kTabGridLayoutAndroidName,
+      flag_descriptions::kTabGridLayoutAndroidDescription, kOsAndroid,
+      FEATURE_WITH_PARAMS_VALUE_TYPE(chrome::android::kTabGridLayoutAndroid,
+diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
+--- a/chrome/browser/flag-metadata.json
++++ b/chrome/browser/flag-metadata.json
+@@ -78,6 +78,11 @@
+     "owners": [ "zentaro" ],
+     "expiry_milestone": 82
+   },
++  {
++    "name": "enable-horizontal-tab-switcher",
++    "owners": [ "memex-team@google.com" ],
++    "expiry_milestone": -1
++  },
+   {
+     "name": "allow-insecure-localhost",
+     "owners": [ "security-dev" ],
+diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
+--- a/chrome/browser/flag_descriptions.cc
++++ b/chrome/browser/flag_descriptions.cc
+@@ -1334,6 +1334,12 @@ const char kHideShelfControlsInTabletModeDescription[] =
+     "Hides home, back, and overview button from the shelf while the device is "
+     "in tablet mode. Predicated on shelf-hotseat feature being enabled.";
+ 
++const char kHorizontalTabSwitcherAndroidName[] =
++    "Enable horizontal tab switcher";
++const char kHorizontalTabSwitcherAndroidDescription[] =
++    "Changes the layout of the Android tab switcher so tabs scroll "
++    "horizontally instead of vertically.";
++
+ const char kTabSwitcherOnReturnName[] = "Tab switcher on return";
+ const char kTabSwitcherOnReturnDescription[] =
+     "Enable tab switcher on return after specified time has elapsed";
+diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
+--- a/chrome/browser/flag_descriptions.h
++++ b/chrome/browser/flag_descriptions.h
+@@ -789,6 +789,9 @@ extern const char kHeavyAdPrivacyMitigationsDescription[];
+ extern const char kHeavyAdInterventionName[];
+ extern const char kHeavyAdInterventionDescription[];
+ 
++extern const char kHorizontalTabSwitcherAndroidName[];
++extern const char kHorizontalTabSwitcherAndroidDescription[];
++
+ extern const char kTabSwitcherOnReturnName[];
+ extern const char kTabSwitcherOnReturnDescription[];
+ 
+diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
+--- a/chrome/browser/flags/android/chrome_feature_list.cc
++++ b/chrome/browser/flags/android/chrome_feature_list.cc
+@@ -197,6 +197,7 @@ const base::Feature* kFeaturesExposedToJava[] = {
+     &kFocusOmniboxInIncognitoTabIntents,
+     &kHandleMediaIntents,
+     &kHomepagePromoCard,
++    &kHorizontalTabSwitcherAndroid,
+     &kImmersiveUiMode,
+     &kIncognitoScreenshot,
+     &kInlineUpdateFlow,
+@@ -551,6 +552,9 @@ const base::Feature kHandleMediaIntents{"HandleMediaIntents",
+ const base::Feature kHomepagePromoCard{"HomepagePromoCard",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
+ 
++const base::Feature kHorizontalTabSwitcherAndroid{
++    "HorizontalTabSwitcherAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
++
+ const base::Feature kImmersiveUiMode{"ImmersiveUiMode",
+                                      base::FEATURE_DISABLED_BY_DEFAULT};
+ 
+diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
+--- a/chrome/browser/flags/android/chrome_feature_list.h
++++ b/chrome/browser/flags/android/chrome_feature_list.h
+@@ -86,6 +86,7 @@ extern const base::Feature kExploreSites;
+ extern const base::Feature kFocusOmniboxInIncognitoTabIntents;
+ extern const base::Feature kHandleMediaIntents;
+ extern const base::Feature kHomepagePromoCard;
++extern const base::Feature kHorizontalTabSwitcherAndroid;
+ extern const base::Feature kImmersiveUiMode;
+ extern const base::Feature kIncognitoScreenshot;
+ extern const base::Feature kImprovedA2HS;
+diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
++++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/CachedFeatureFlags.java
+@@ -54,6 +54,7 @@ public class CachedFeatureFlags {
+             put(ChromeFeatureList.CHROME_STARTUP_DELEGATE, false);
+             put(ChromeFeatureList.CONDITIONAL_TAB_STRIP_ANDROID, false);
+             put(ChromeFeatureList.LENS_CAMERA_ASSISTED_SEARCH, false);
++            put(ChromeFeatureList.HORIZONTAL_TAB_SWITCHER_ANDROID, false);
+             put(ChromeFeatureList.SERVICE_MANAGER_FOR_DOWNLOAD, true);
+             put(ChromeFeatureList.SERVICE_MANAGER_FOR_BACKGROUND_PREFETCH, true);
+             put(ChromeFeatureList.COMMAND_LINE_ON_NON_ROOTED, false);
+diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
++++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+@@ -335,6 +335,7 @@ public abstract class ChromeFeatureList {
+     public static final String HANDLE_MEDIA_INTENTS = "HandleMediaIntents";
+     public static final String HIDE_FROM_API_3_TRANSITIONS_FROM_HISTORY =
+             "HideFromApi3TransitionsFromHistory";
++    public static final String HORIZONTAL_TAB_SWITCHER_ANDROID = "HorizontalTabSwitcherAndroid";
+     public static final String IMMERSIVE_UI_MODE = "ImmersiveUiMode";
+     public static final String INCOGNITO_SCREENSHOT = "IncognitoScreenshot";
+     public static final String INLINE_UPDATE_FLOW = "InlineUpdateFlow";
+-- 
+2.17.1
+

+ 4 - 4
build/patches/Revert-flags-remove-disable-pull-to-refresh-effect.patch

@@ -13,7 +13,7 @@ This reverts commit 4e598f38a0e6dd3dbede009c6a99b2a520a94e1f.
 diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 --- a/chrome/browser/about_flags.cc
 +++ b/chrome/browser/about_flags.cc
-@@ -3280,6 +3280,10 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -3285,6 +3285,10 @@ const FeatureEntry kFeatureEntries[] = {
       SINGLE_VALUE_TYPE(switches::kHostedAppQuitNotification)},
  #endif  // OS_MAC
  #if defined(OS_ANDROID)
@@ -27,7 +27,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
 --- a/chrome/browser/flag-metadata.json
 +++ b/chrome/browser/flag-metadata.json
-@@ -1091,6 +1091,11 @@
+@@ -1096,6 +1096,11 @@
      // enable-javascript-harmony.
      "expiry_milestone": -1
    },
@@ -42,7 +42,7 @@ diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.js
 diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
 --- a/chrome/browser/flag_descriptions.cc
 +++ b/chrome/browser/flag_descriptions.cc
-@@ -3241,6 +3241,10 @@ const char kReadLaterDescription[] =
+@@ -3245,6 +3245,10 @@ const char kReadLaterDescription[] =
      "Allow users to save tabs for later. Enables a new button and menu for "
      "accessing tabs saved for later.";
  
@@ -56,7 +56,7 @@ diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descripti
 diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
 --- a/chrome/browser/flag_descriptions.h
 +++ b/chrome/browser/flag_descriptions.h
-@@ -1876,6 +1876,9 @@ extern const char kQueryTilesMoreTrendingDescription[];
+@@ -1879,6 +1879,9 @@ extern const char kQueryTilesMoreTrendingDescription[];
  extern const char kQueryTilesSwapTrendingName[];
  extern const char kQueryTilesSwapTrendingDescription[];
  

+ 4 - 4
build/patches/Revert-flags-remove-num-raster-threads.patch

@@ -29,7 +29,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
  const FeatureEntry::Choice kTouchTextSelectionStrategyChoices[] = {
      {flags_ui::kGenericExperimentChoiceDefault, "", ""},
      {flag_descriptions::kTouchSelectionStrategyCharacter,
-@@ -3378,6 +3387,9 @@ const FeatureEntry kFeatureEntries[] = {
+@@ -3383,6 +3392,9 @@ const FeatureEntry kFeatureEntries[] = {
           feature_engagement::kIPHDemoMode,
           feature_engagement::kIPHDemoModeChoiceVariations,
           "IPH_DemoMode")},
@@ -42,7 +42,7 @@ diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
 diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
 --- a/chrome/browser/flag-metadata.json
 +++ b/chrome/browser/flag-metadata.json
-@@ -3560,6 +3560,11 @@
+@@ -3570,6 +3570,11 @@
      ],
      "expiry_milestone": 95
    },
@@ -57,7 +57,7 @@ diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.js
 diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
 --- a/chrome/browser/flag_descriptions.cc
 +++ b/chrome/browser/flag_descriptions.cc
-@@ -1601,6 +1601,14 @@ const char kUseMultiloginEndpointName[] = "Use Multilogin endpoint.";
+@@ -1607,6 +1607,14 @@ const char kUseMultiloginEndpointName[] = "Use Multilogin endpoint.";
  const char kUseMultiloginEndpointDescription[] =
      "Use Gaia OAuth multilogin for identity consistency.";
  
@@ -75,7 +75,7 @@ diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descripti
 diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
 --- a/chrome/browser/flag_descriptions.h
 +++ b/chrome/browser/flag_descriptions.h
-@@ -959,6 +959,13 @@ extern const char kNotificationsSystemFlagDescription[];
+@@ -962,6 +962,13 @@ extern const char kNotificationsSystemFlagDescription[];
  extern const char kUseMultiloginEndpointName[];
  extern const char kUseMultiloginEndpointDescription[];
  

+ 1 - 1
build/patches/User-agent-customization.patch

@@ -80,7 +80,7 @@ diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_ja
 diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
 --- a/chrome/android/chrome_java_sources.gni
 +++ b/chrome/android/chrome_java_sources.gni
-@@ -1145,6 +1145,7 @@ chrome_java_sources = [
+@@ -1146,6 +1146,7 @@ chrome_java_sources = [
    "java/src/org/chromium/chrome/browser/payments/ui/DimmingDialog.java",
    "java/src/org/chromium/chrome/browser/payments/ui/LineItem.java",
    "java/src/org/chromium/chrome/browser/payments/ui/PaymentAppComparator.java",