diff --git a/BUILD.gn b/BUILD.gn
index 3bf0898..19b5031 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -775,6 +775,13 @@
       "//tools/accessibility/inspect:ax_dump_tree",
     ]
   }
+
+  # PFFFT.
+  deps += [
+    "//third_party/pffft:fuzzers",
+    "//third_party/pffft:pffft_benchmark",
+    "//third_party/pffft:pffft_unittest",
+  ]
 }
 
 if ((is_linux || is_win) && enable_remoting && !use_ozone) {
diff --git a/DEPS b/DEPS
index a49b25c..bff83c86 100644
--- a/DEPS
+++ b/DEPS
@@ -126,11 +126,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '26490759e46483558627c9132e31f4b97cf052ac',
+  'skia_revision': 'a41c6858da8a27d0c8e7a295c5d9f2bf206997db',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '054df48cc27e6c74b54c9aac7a4386e9aed99a28',
+  'v8_revision': 'b8e007d8fe213fe1eea595eea03bc63d938d4284',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -138,7 +138,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'cd4f1fbaabce8edb9e0cbe04c5bd976c65fa776b',
+  'angle_revision': 'e03498f2d25ae2c196cff80cef0ee0aa9796a4d1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -754,7 +754,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '545f0d025ef1f93aa276c4da765f5450013014dc',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '610a4c6ce76cc6e9c2dec19fe8f5e721f1adcbf1',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1089,7 +1089,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'ee4a5ccc89d4c97d5128ca9df51ecfdbbb3643ee',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '56445ec1e273a9be91b4fc620f8bd0f4df37e77a',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1293,7 +1293,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@79ffba7a9de64764b6690629fcb9f467074ed56f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b1dbb0c6184fcc16397609ddfbe3c3571ee182b7',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/wm/splitview/split_view_divider.cc b/ash/wm/splitview/split_view_divider.cc
index c0243b1..8b3587e 100644
--- a/ash/wm/splitview/split_view_divider.cc
+++ b/ash/wm/splitview/split_view_divider.cc
@@ -23,6 +23,7 @@
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/gfx/animation/slide_animation.h"
+#include "ui/gfx/animation/tween.h"
 #include "ui/gfx/canvas.h"
 #include "ui/views/view.h"
 #include "ui/views/view_targeter_delegate.h"
@@ -125,6 +126,7 @@
     SetEventTargeter(
         std::unique_ptr<views::ViewTargeter>(new views::ViewTargeter(this)));
     white_bar_animation_.SetSlideDuration(kWhiteBarBoundsChangeDurationMs);
+    white_bar_animation_.SetTweenType(gfx::Tween::EASE_IN);
   }
   ~DividerView() override { white_bar_animation_.Stop(); }
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 39ca8f18..47f434f 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3146,6 +3146,7 @@
     ]
 
     java_files = [
+      "test/android/javatests/src/org/chromium/base/test/ReachedCodeProfiler.java",
       "test/android/javatests/src/org/chromium/base/test/BaseJUnit4ClassRunner.java",
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumAndroidJUnitRunner.java",
       "test/android/javatests/src/org/chromium/base/test/BaseChromiumRunnerCommon.java",
diff --git a/base/android/java/src/org/chromium/base/BaseSwitches.java b/base/android/java/src/org/chromium/base/BaseSwitches.java
index fe47cdd..b0b1726 100644
--- a/base/android/java/src/org/chromium/base/BaseSwitches.java
+++ b/base/android/java/src/org/chromium/base/BaseSwitches.java
@@ -27,6 +27,9 @@
     // Default country code to be used for search engine localization.
     public static final String DEFAULT_COUNTRY_CODE_AT_INSTALL = "default-country-code";
 
+    // Enables the reached code profiler.
+    public static final String ENABLE_REACHED_CODE_PROFILER = "enable-reached-code-profiler";
+
     // Prevent instantiation.
     private BaseSwitches() {}
 }
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index 0ee9aa9..b5a8824 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -17,6 +17,7 @@
 import android.support.v4.content.ContextCompat;
 import android.system.Os;
 
+import org.chromium.base.BaseSwitches;
 import org.chromium.base.BuildConfig;
 import org.chromium.base.BuildInfo;
 import org.chromium.base.CommandLine;
@@ -24,6 +25,7 @@
 import org.chromium.base.FileUtils;
 import org.chromium.base.Log;
 import org.chromium.base.StreamUtil;
+import org.chromium.base.StrictModeContext;
 import org.chromium.base.SysUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.VisibleForTesting;
@@ -85,6 +87,9 @@
     // SharedPreferences key for "don't prefetch libraries" flag
     private static final String DONT_PREFETCH_LIBRARIES_KEY = "dont_prefetch_libraries";
 
+    // Shared preferences key for the reached code profiler.
+    private static final String REACHED_CODE_PROFILER_ENABLED_KEY = "reached_code_profiler_enabled";
+
     private static final EnumeratedHistogramSample sRelinkerCountHistogram =
             new EnumeratedHistogramSample("ChromiumAndroidLinker.RelinkerFallbackCount", 2);
 
@@ -306,6 +311,33 @@
         }
     }
 
+    /**
+     * Enables the reached code profiler. The value comes from "ReachedCodeProfiler"
+     * finch experiment, and is pushed on every run. I.e. the effect of the finch experiment
+     * lags by one run, which is the best we can do considering that the profiler has to be enabled
+     * before finch is initialized. Note that since LibraryLoader is in //base, it can't depend
+     * on ChromeFeatureList, and has to rely on external code pushing the value.
+     *
+     * @param enabled whether to enable the reached code profiler.
+     */
+    public static void setReachedCodeProfilerEnabledOnNextRuns(boolean enabled) {
+        ContextUtils.getAppSharedPreferences()
+                .edit()
+                .putBoolean(REACHED_CODE_PROFILER_ENABLED_KEY, enabled)
+                .apply();
+    }
+
+    /**
+     * @return whether to enable reached code profiler (see
+     *         setReachedCodeProfilerEnabledOnNextRuns()).
+     */
+    private static boolean isReachedCodeProfilerEnabled() {
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+            return ContextUtils.getAppSharedPreferences().getBoolean(
+                    REACHED_CODE_PROFILER_ENABLED_KEY, false);
+        }
+    }
+
     /** Prefetches the native libraries in a background thread.
      *
      * Launches an AsyncTask that, through a short-lived forked process, reads a
@@ -585,6 +617,13 @@
         }
         mLibraryProcessType = processType;
 
+        // Add a switch for the reached code profiler as late as possible since it requires a read
+        // from the shared preferences. At this point the shared preferences are usually warmed up.
+        if (mLibraryProcessType == LibraryProcessType.PROCESS_BROWSER
+                && isReachedCodeProfilerEnabled()) {
+            CommandLine.getInstance().appendSwitch(BaseSwitches.ENABLE_REACHED_CODE_PROFILER);
+        }
+
         ensureCommandLineSwitchedAlreadyLocked();
 
         if (!nativeLibraryLoaded(mLibraryProcessType)) {
diff --git a/base/android/reached_code_profiler.cc b/base/android/reached_code_profiler.cc
index 0513500c..07e6d5e 100644
--- a/base/android/reached_code_profiler.cc
+++ b/base/android/reached_code_profiler.cc
@@ -13,8 +13,10 @@
 
 #include "base/android/library_loader/anchor_functions.h"
 #include "base/android/orderfile/orderfile_buildflags.h"
+#include "base/base_switches.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/important_file_writer.h"
@@ -40,6 +42,19 @@
 
 namespace {
 
+#if !defined(NDEBUG) || defined(COMPONENT_BUILD)
+// Always disabled for debug builds to avoid hitting a limit of signal
+// interrupts that can get delivered into a single HANDLE_EINTR. Also
+// debugging experience would be bad if there are a lot of signals flying
+// around.
+// Always disabled for component builds because in this case the code is not
+// organized in one contiguous region which is required for the reached code
+// profiler.
+constexpr const bool kConfigurationSupported = false;
+#else
+constexpr const bool kConfigurationSupported = true;
+#endif
+
 constexpr const char kDumpToFileFlag[] = "reached-code-profiler-dump-to-file";
 
 // Enough for 1 << 29 bytes of code, 512MB.
@@ -290,20 +305,11 @@
 };
 
 bool ShouldEnableReachedCodeProfiler() {
-#if !defined(NDEBUG) || defined(COMPONENT_BUILD)
-  // Always disabled for debug builds to avoid hitting a limit of signal
-  // interrupts that can get delivered into a single HANDLE_EINTR. Also
-  // debugging experience would be bad if there are a lot of signals flying
-  // around.
-  // Always disabled for component builds because in this case the code is not
-  // organized in one contiguous region which is required for the reached code
-  // profiler.
-  return false;
-#else
-  // TODO(crbug.com/916263): this should be set up according to the finch
-  // experiment.
-  return false;
-#endif
+  if (!kConfigurationSupported)
+    return false;
+
+  const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  return cmdline->HasSwitch(switches::kEnableReachedCodeProfiler);
 }
 
 }  // namespace
@@ -323,5 +329,9 @@
   return ReachedCodeProfiler::GetInstance()->IsEnabled();
 }
 
+bool IsReachedCodeProfilerSupported() {
+  return kConfigurationSupported;
+}
+
 }  // namespace android
 }  // namespace base
diff --git a/base/android/reached_code_profiler.h b/base/android/reached_code_profiler.h
index 8d18e09f..435600e 100644
--- a/base/android/reached_code_profiler.h
+++ b/base/android/reached_code_profiler.h
@@ -24,6 +24,10 @@
 // Returns whether the reached code profiler is enabled.
 BASE_EXPORT bool IsReachedCodeProfilerEnabled();
 
+// Returns whether the reached code profiler can be possibly enabled for the
+// current build configuration.
+BASE_EXPORT bool IsReachedCodeProfilerSupported();
+
 }  // namespace android
 }  // namespace base
 
diff --git a/base/android/reached_code_profiler_stub.cc b/base/android/reached_code_profiler_stub.cc
index 56f0fb00..f99df5c 100644
--- a/base/android/reached_code_profiler_stub.cc
+++ b/base/android/reached_code_profiler_stub.cc
@@ -14,5 +14,9 @@
   return false;
 }
 
+bool IsReachedCodeProfilerSupported() {
+  return false;
+}
+
 }  // namespace android
 }  // namespace base
diff --git a/base/base_switches.cc b/base/base_switches.cc
index ff781a3..1567a3e 100644
--- a/base/base_switches.cc
+++ b/base/base_switches.cc
@@ -123,6 +123,10 @@
 #endif
 
 #if defined(OS_ANDROID)
+// Enables the reached code profiler that samples all threads in all processes
+// to determine which functions are almost never executed.
+const char kEnableReachedCodeProfiler[] = "enable-reached-code-profiler";
+
 // Specifies optimization of memory layout of the native library using the
 // orderfile symbols given in base/android/library_loader/anchor_functions.h,
 // via madvise and changing the library prefetch behavior.
diff --git a/base/base_switches.h b/base/base_switches.h
index 5967c1a..49650dc 100644
--- a/base/base_switches.h
+++ b/base/base_switches.h
@@ -45,6 +45,7 @@
 #endif
 
 #if defined(OS_ANDROID)
+extern const char kEnableReachedCodeProfiler[];
 extern const char kOrderfileMemoryOptimization[];
 #endif
 
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 7c3632a..dea5aa2b 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -93,6 +93,7 @@
     "perf_time_logger.h",
     "power_monitor_test_base.cc",
     "power_monitor_test_base.h",
+    "reached_code_profiler_android.cc",
     "scoped_command_line.cc",
     "scoped_command_line.h",
     "scoped_environment_variable_override.cc",
@@ -435,6 +436,7 @@
     sources = [
       "android/java/src/org/chromium/base/MainReturnCodeResult.java",
       "android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java",
+      "android/javatests/src/org/chromium/base/test/ReachedCodeProfiler.java",
       "android/javatests/src/org/chromium/base/test/task/TaskSchedulerTestHelpers.java",
       "android/javatests/src/org/chromium/base/test/util/UrlUtils.java",
     ]
diff --git a/base/test/android/javatests/src/org/chromium/base/test/ReachedCodeProfiler.java b/base/test/android/javatests/src/org/chromium/base/test/ReachedCodeProfiler.java
new file mode 100644
index 0000000..1b1a5c0
--- /dev/null
+++ b/base/test/android/javatests/src/org/chromium/base/test/ReachedCodeProfiler.java
@@ -0,0 +1,33 @@
+// Copyright 2019 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.base.test;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Class containing only static methods for querying the status of the reached code profiler.
+ */
+@JNINamespace("base::android")
+public class ReachedCodeProfiler {
+    private ReachedCodeProfiler() {}
+
+    /**
+     * @return Whether the reached code profiler is enabled.
+     */
+    public static boolean isEnabled() {
+        return nativeIsReachedCodeProfilerEnabled();
+    }
+
+    /**
+     * @return Whether the currently used version of native library supports the reached code
+     *         profiler.
+     */
+    public static boolean isSupported() {
+        return nativeIsReachedCodeProfilerSupported();
+    }
+
+    private static native boolean nativeIsReachedCodeProfilerEnabled();
+    private static native boolean nativeIsReachedCodeProfilerSupported();
+}
diff --git a/base/test/reached_code_profiler_android.cc b/base/test/reached_code_profiler_android.cc
new file mode 100644
index 0000000..2def7fc
--- /dev/null
+++ b/base/test/reached_code_profiler_android.cc
@@ -0,0 +1,25 @@
+// Copyright 2019 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.
+
+#include "base/android/jni_android.h"
+#include "base/android/reached_code_profiler.h"
+#include "jni/ReachedCodeProfiler_jni.h"
+
+// This file provides functions to query the state of the reached code profiler
+// from Java. It's used only for tests.
+namespace base {
+namespace android {
+
+static jboolean JNI_ReachedCodeProfiler_IsReachedCodeProfilerEnabled(
+    JNIEnv* env) {
+  return IsReachedCodeProfilerEnabled();
+}
+
+static jboolean JNI_ReachedCodeProfiler_IsReachedCodeProfilerSupported(
+    JNIEnv* env) {
+  return IsReachedCodeProfilerSupported();
+}
+
+}  // namespace android
+}  // namespace base
diff --git a/build/android/apk_operations.py b/build/android/apk_operations.py
index 4b59443..616f291 100755
--- a/build/android/apk_operations.py
+++ b/build/android/apk_operations.py
@@ -96,13 +96,18 @@
     'keystore_alias')
 
 
-def _GenerateBundleApks(info, output_path, minimal=False, universal=False):
+def _GenerateBundleApks(info,
+                        output_path,
+                        minimal=False,
+                        minimal_sdk_version=None,
+                        universal=False):
   """Generate an .apks archive from a bundle on demand.
 
   Args:
     info: A BundleGenerationInfo instance.
     output_path: Path of output .apks archive.
     minimal: Create the minimal set of apks possible (english-only).
+    minimal_sdk_version: When minimal=True, use this sdkVersion.
     universal: Whether to create a single APK that contains the contents of all
         modules.
   """
@@ -114,7 +119,8 @@
       info.keystore_password,
       info.keystore_alias,
       universal=universal,
-      minimal=minimal)
+      minimal=minimal,
+      minimal_sdk_version=minimal_sdk_version)
 
 
 def _InstallBundle(devices, bundle_apks, package_name, command_line_flags_file,
@@ -1425,6 +1431,9 @@
         action='store_true',
         help='Build .apks archive that targets the bundle\'s minSdkVersion and '
         'contains only english splits. It still contains optional splits.')
+    group.add_argument(
+        '--sdk-version',
+        help='Implies --minimal. The sdkVersion to build the .apks for.')
     group.add_argument('--universal', action='store_true',
                        help='Build .apks archive containing single APK with '
                             'contents of all splits. NOTE: Won\'t add modules '
@@ -1434,7 +1443,8 @@
     _GenerateBundleApks(
         self.bundle_generation_info,
         self.args.output_apks,
-        minimal=self.args.minimal,
+        minimal=self.args.sdk_version is not None or self.args.minimal,
+        minimal_sdk_version=self.args.sdk_version,
         universal=self.args.universal)
 
 
diff --git a/build/android/gyp/create_app_bundle.py b/build/android/gyp/create_app_bundle.py
index 5d238df..a1f2eb6e 100755
--- a/build/android/gyp/create_app_bundle.py
+++ b/build/android/gyp/create_app_bundle.py
@@ -71,9 +71,10 @@
       '--rtxt-out-path', help='Path to combined R.txt file for bundle.')
   parser.add_argument('--uncompressed-assets', action='append',
                       help='GN-list of uncompressed assets.')
-  parser.add_argument('--uncompress-shared-libraries', action='append',
-                      help='Whether to store native libraries uncompressed. '
-                      'This is a string to allow @FileArg usage.')
+  parser.add_argument(
+      '--compress-shared-libraries',
+      action='store_true',
+      help='Whether to store native libraries compressed.')
   parser.add_argument('--split-dimensions',
                       help="GN-list of split dimensions to support.")
   parser.add_argument(
@@ -119,13 +120,6 @@
 
   options.uncompressed_assets = set(uncompressed_list)
 
-  # Merge uncompressed native libs flags, they all must have the same value.
-  if options.uncompress_shared_libraries:
-    uncompressed_libs = set(options.uncompress_shared_libraries)
-    if len(uncompressed_libs) > 1:
-      parser.error('Inconsistent uses of --uncompress-native-libs!')
-    options.uncompress_shared_libraries = 'True' in uncompressed_libs
-
   # Check that all split dimensions are valid
   if options.split_dimensions:
     options.split_dimensions = build_utils.ParseGnList(options.split_dimensions)
@@ -157,14 +151,14 @@
   return {'value': value, 'negate': not enabled}
 
 
-def _GenerateBundleConfigJson(uncompressed_assets, uncompress_shared_libraries,
+def _GenerateBundleConfigJson(uncompressed_assets, compress_shared_libraries,
                               split_dimensions, base_master_resource_ids):
   """Generate a dictionary that can be written to a JSON BuildConfig.
 
   Args:
     uncompressed_assets: A list or set of file paths under assets/ that always
       be stored uncompressed.
-    uncompress_shared_libraries: Boolean, whether to uncompress all native libs.
+    compress_shared_libraries: Boolean, whether to compress native libs.
     split_dimensions: list of split dimensions.
     base_master_resource_ids: Optional list of 32-bit resource IDs to keep
       inside the base module, even when split dimensions are enabled.
@@ -180,29 +174,25 @@
   split_dimensions = [ _MakeSplitDimension(dim, dim in split_dimensions)
                        for dim in _ALL_SPLIT_DIMENSIONS ]
 
-  # Compute uncompressedGlob list.
-  if uncompress_shared_libraries:
-    uncompressed_globs = [
-      'lib/*/*.so',        # All native libraries.
-    ]
-  else:
-    uncompressed_globs = [
-      'lib/*/crazy.*',     # Native libraries loaded by the crazy linker.
-    ]
-
+  # Native libraries loaded by the crazy linker.
+  # Whether other .so files are compressed is controlled by
+  # "uncompressNativeLibraries".
+  uncompressed_globs = ['lib/*/crazy.*']
   uncompressed_globs.extend('assets/' + x for x in uncompressed_assets)
-
-  uncompressed_globs.extend(['*.' + ext for ext in _UNCOMPRESSED_FILE_EXTS])
+  uncompressed_globs.extend('*.' + ext for ext in _UNCOMPRESSED_FILE_EXTS)
 
   data = {
-    'optimizations': {
-      'splitsConfig': {
-        'splitDimension': split_dimensions,
+      'optimizations': {
+          'splitsConfig': {
+              'splitDimension': split_dimensions,
+          },
+          'uncompressNativeLibraries': {
+              'enabled': not compress_shared_libraries,
+          },
       },
-    },
-    'compression': {
-       'uncompressedGlob': sorted(uncompressed_globs),
-    },
+      'compression': {
+          'uncompressedGlob': sorted(uncompressed_globs),
+      },
   }
 
   if base_master_resource_ids:
@@ -330,7 +320,7 @@
           options.base_module_rtxt_path, options.base_whitelist_rtxt_path)
 
     bundle_config = _GenerateBundleConfigJson(
-        options.uncompressed_assets, options.uncompress_shared_libraries,
+        options.uncompressed_assets, options.compress_shared_libraries,
         split_dimensions, base_master_resource_ids)
 
     tmp_bundle = os.path.join(tmp_dir, 'tmp_bundle')
diff --git a/build/android/pylib/utils/app_bundle_utils.py b/build/android/pylib/utils/app_bundle_utils.py
index 2a5e023..7b6d0c6 100644
--- a/build/android/pylib/utils/app_bundle_utils.py
+++ b/build/android/pylib/utils/app_bundle_utils.py
@@ -20,18 +20,19 @@
 _ALL_ABIS = ['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64']
 
 
-def _CreateMinimalDeviceSpec(bundle_path):
+def _CreateMinimalDeviceSpec(bundle_path, sdk_version):
   # Could also use "bundletool dump resources", but reading directly is faster.
-  with zipfile.ZipFile(bundle_path) as f:
-    manifest_data = f.read('base/manifest/AndroidManifest.xml')
-  min_sdk_version = int(
-      re.search(r'minSdkVersion.*?(\d+)', manifest_data).group(1))
+  if not sdk_version:
+    with zipfile.ZipFile(bundle_path) as f:
+      manifest_data = f.read('base/manifest/AndroidManifest.xml')
+    sdk_version = int(
+        re.search(r'minSdkVersion.*?(\d+)', manifest_data).group(1))
 
   # Setting sdkVersion=minSdkVersion prevents multiple per-minSdkVersion .apk
   # files from being created within the .apks file.
   return {
       'screenDensity': 1000,  # Ignored since we don't split on density.
-      'sdkVersion': min_sdk_version,
+      'sdkVersion': sdk_version,
       'supportedAbis': _ALL_ABIS,  # Our .aab files are already split on abi.
       'supportedLocales': ['en'],
   }
@@ -45,6 +46,7 @@
                        keystore_alias,
                        universal=False,
                        minimal=False,
+                       minimal_sdk_version=None,
                        check_for_noop=True):
   """Generate an .apks archive from a an app bundle if needed.
 
@@ -59,11 +61,12 @@
     universal: Whether to create a single APK that contains the contents of all
       modules.
     minimal: Create the minimal set of apks possible (english-only).
+    minimal_sdk_version: When minimal=True, use this sdkVersion.
     check_for_noop: Use md5_check to short-circuit when inputs have not changed.
   """
   device_spec = None
   if minimal:
-    device_spec = _CreateMinimalDeviceSpec(bundle_path)
+    device_spec = _CreateMinimalDeviceSpec(bundle_path, minimal_sdk_version)
 
   def rebuild():
     logging.info('Building %s', bundle_apks_path)
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index fcaca2b..9cb11ab8 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -3862,6 +3862,10 @@
   #    proguard_android_sdk_dep: Optional. android_system_java_prebuilt() target
   #      used as a library jar for synchronized proguarding.
   #
+  #    compress_shared_libraries: Optional. Whether to compress shared libraries
+  #      such that they are extracted upon install. Libraries prefixed with
+  #      "crazy." are never compressed.
+  #
   # Example:
   #   android_app_bundle("chrome_public_bundle") {
   #      base_module_target = "//chrome/android:chrome_public_apk"
@@ -4094,6 +4098,10 @@
       if (_split_dimensions != []) {
         args += [ "--split-dimensions=$_split_dimensions" ]
       }
+      if (defined(invoker.compress_shared_libraries) &&
+          invoker.compress_shared_libraries) {
+        args += [ "--compress-shared-libraries" ]
+      }
 
       if (_enable_language_splits) {
         args += [
@@ -4107,8 +4115,6 @@
         args += [
           "--uncompressed-assets=@FileArg(" +
               "$_rebased_build_config:uncompressed_assets)",
-          "--uncompress-shared-libraries=@FileArg(" +
-              "$_rebased_build_config:native:uncompress_shared_libraries)",
           "--rtxt-in-paths=@FileArg(" +
               "$_rebased_build_config:deps_info:module_rtxt_path)",
         ]
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index d777d71..b56c759 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -92,13 +92,15 @@
   # the needed gcov profiling data.
   auto_profile_path = ""
 
-  # Optimize symbol files for maximizing goma cache hit rate. This is on by
-  # default for Android, and when goma is enabled on Linux and Windows.
-  # But setting this to true may make it harder to debug binaries on Linux.
-  # See below reference for detail.
+  # Use relative paths for debug info. This is important to make the build
+  # results independent of the checkout and build directory names, which
+  # in turn is important for goma compile hit rate.
+  # Setting this to true may make it harder to debug binaries on Linux, see
   # https://chromium.googlesource.com/chromium/src/+/master/docs/linux_debugging.md#Source-level-debug-with-fdebug_compilation_dir
+  # So only enable this on linux if use_goma.  Elsewhere, there are no
+  # drawbacks to using it, so always enable it there.
   strip_absolute_paths_from_debug_symbols =
-      is_android || is_nacl || (use_goma && (is_linux || (is_win && use_lld)))
+      is_android || is_nacl || (is_win && use_lld) || (use_goma && is_linux)
 
   # Allow projects that wish to stay on C++11 to override Chromium's default.
   use_cxx11 = false
diff --git a/build/config/gcc/BUILD.gn b/build/config/gcc/BUILD.gn
index 3f5756e..5499644 100644
--- a/build/config/gcc/BUILD.gn
+++ b/build/config/gcc/BUILD.gn
@@ -70,13 +70,9 @@
       ldflags = [
         # Want to pass "\$". GN will re-escape as required for ninja.
         "-Wl,-rpath=\$ORIGIN/${rpath_link}",
-        "-Wl,-rpath-link=${rpath_link}",
       ]
     } else {
-      ldflags = [
-        "-Wl,-rpath=${gcc_target_rpath}",
-        "-Wl,-rpath-link=${rpath_link}",
-      ]
+      ldflags = [ "-Wl,-rpath=${gcc_target_rpath}" ]
     }
     if (current_toolchain == default_toolchain && ldso_path != "") {
       ldflags += [ "-Wl,--dynamic-linker=${ldso_path}" ]
@@ -105,13 +101,7 @@
   }
 
   if (!is_android && current_os != "aix") {
-    # Find the path containing shared libraries for this toolchain
-    # relative to the build directory. ${root_out_dir} will be a
-    # subdirectory of ${root_build_dir} when cross compiling.
-    _rpath_link = rebase_path(root_out_dir, root_build_dir)
     ldflags += [
-      "-Wl,-rpath-link=$_rpath_link",
-
       # TODO(GYP): Do we need a check on the binutils version here?
       #
       # Newer binutils don't set DT_RPATH unless you disable "new" dtags
diff --git a/build/config/posix/BUILD.gn b/build/config/posix/BUILD.gn
index 9a1c34f..6aa12bc 100644
--- a/build/config/posix/BUILD.gn
+++ b/build/config/posix/BUILD.gn
@@ -157,10 +157,7 @@
                            "list lines")
     foreach(ld_path, ld_paths) {
       ld_path = rebase_path(ld_path, root_build_dir)
-      ldflags += [
-        "-L" + ld_path,
-        "-Wl,-rpath-link=" + ld_path,
-      ]
+      ldflags += [ "-L" + ld_path ]
     }
   }
 }
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 500ab89..e8626f8 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -110,10 +110,7 @@
     bool bubble;
   };
 
-  enum ScrollInputType {
-    TOUCHSCREEN,
-    WHEEL,
-  };
+  enum ScrollInputType { TOUCHSCREEN, WHEEL, AUTOSCROLL, SCROLL_INPUT_UNKNOWN };
 
   enum class TouchStartOrMoveEventListenerType {
     NO_HANDLER,
diff --git a/cc/layers/heads_up_display_layer_impl.cc b/cc/layers/heads_up_display_layer_impl.cc
index 5c1e3156..5a89b217 100644
--- a/cc/layers/heads_up_display_layer_impl.cc
+++ b/cc/layers/heads_up_display_layer_impl.cc
@@ -77,10 +77,10 @@
  public:
   DummyImageProvider() = default;
   ~DummyImageProvider() override = default;
-  ScopedDecodedDrawImage GetDecodedDrawImage(
+  ImageProvider::ScopedResult GetRasterContent(
       const DrawImage& draw_image) override {
     NOTREACHED();
-    return ScopedDecodedDrawImage();
+    return ScopedResult();
   }
 };
 
diff --git a/cc/paint/decode_stashing_image_provider.cc b/cc/paint/decode_stashing_image_provider.cc
index f8b4f94..e8abd07 100644
--- a/cc/paint/decode_stashing_image_provider.cc
+++ b/cc/paint/decode_stashing_image_provider.cc
@@ -12,17 +12,19 @@
 }
 DecodeStashingImageProvider::~DecodeStashingImageProvider() = default;
 
-ImageProvider::ScopedDecodedDrawImage
-DecodeStashingImageProvider::GetDecodedDrawImage(const DrawImage& draw_image) {
-  auto decode = source_provider_->GetDecodedDrawImage(draw_image);
+ImageProvider::ScopedResult DecodeStashingImageProvider::GetRasterContent(
+    const DrawImage& draw_image) {
+  // TODO(xidachen): Ensure this function works with paint worklet generated
+  // images.
+  auto decode = source_provider_->GetRasterContent(draw_image);
   if (!decode.needs_unlock())
     return decode;
 
   // No need to add any destruction callback to the returned image. The images
   // decoded here match the lifetime of this provider.
-  auto image_to_return = ScopedDecodedDrawImage(decode.decoded_image());
+  auto result = ScopedResult(decode.decoded_image());
   decoded_images_->push_back(std::move(decode));
-  return image_to_return;
+  return result;
 }
 
 void DecodeStashingImageProvider::Reset() {
diff --git a/cc/paint/decode_stashing_image_provider.h b/cc/paint/decode_stashing_image_provider.h
index 2509ce3f..99a45d9 100644
--- a/cc/paint/decode_stashing_image_provider.h
+++ b/cc/paint/decode_stashing_image_provider.h
@@ -20,7 +20,7 @@
   ~DecodeStashingImageProvider() override;
 
   // ImageProvider implementation.
-  ScopedDecodedDrawImage GetDecodedDrawImage(
+  ImageProvider::ScopedResult GetRasterContent(
       const DrawImage& draw_image) override;
 
   // Releases all stashed images. The caller must ensure that it is safe to
@@ -29,7 +29,7 @@
 
  private:
   ImageProvider* source_provider_;
-  base::StackVector<ScopedDecodedDrawImage, 1> decoded_images_;
+  base::StackVector<ScopedResult, 1> decoded_images_;
 
   DISALLOW_COPY_AND_ASSIGN(DecodeStashingImageProvider);
 };
diff --git a/cc/paint/decoded_draw_image.h b/cc/paint/decoded_draw_image.h
index 63bc1ca..afc0773 100644
--- a/cc/paint/decoded_draw_image.h
+++ b/cc/paint/decoded_draw_image.h
@@ -54,6 +54,7 @@
     return transfer_cache_entry_needs_mips_;
   }
   bool is_budgeted() const { return is_budgeted_; }
+  operator bool() const { return image_ || transfer_cache_entry_id_; }
 
  private:
   sk_sp<const SkImage> image_;
diff --git a/cc/paint/discardable_image_map.cc b/cc/paint/discardable_image_map.cc
index eeda858..23ccb20 100644
--- a/cc/paint/discardable_image_map.cc
+++ b/cc/paint/discardable_image_map.cc
@@ -132,12 +132,11 @@
         : generator_(generator), op_rect_(op_rect) {}
     ~ImageGatheringProvider() override = default;
 
-    ScopedDecodedDrawImage GetDecodedDrawImage(
-        const DrawImage& draw_image) override {
+    ScopedResult GetRasterContent(const DrawImage& draw_image) override {
       generator_->AddImage(draw_image.paint_image(),
                            SkRect::Make(draw_image.src_rect()), op_rect_,
                            SkMatrix::I(), draw_image.filter_quality());
-      return ScopedDecodedDrawImage();
+      return ScopedResult();
     }
 
    private:
diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc
index 24731ac..5a467fb 100644
--- a/cc/paint/display_item_list.cc
+++ b/cc/paint/display_item_list.cc
@@ -69,10 +69,8 @@
 
 DisplayItemList::~DisplayItemList() = default;
 
-void DisplayItemList::Raster(
-    SkCanvas* canvas,
-    ImageProvider* image_provider,
-    PaintWorkletImageProvider* paint_worklet_image_provider) const {
+void DisplayItemList::Raster(SkCanvas* canvas,
+                             ImageProvider* image_provider) const {
   DCHECK(usage_hint_ == kTopLevelDisplayItemList);
   gfx::Rect canvas_playback_rect;
   if (!GetCanvasClipBounds(canvas, &canvas_playback_rect))
@@ -80,9 +78,7 @@
 
   std::vector<size_t> offsets;
   rtree_.Search(canvas_playback_rect, &offsets);
-  paint_op_buffer_.Playback(
-      canvas, PlaybackParams(image_provider, paint_worklet_image_provider),
-      &offsets);
+  paint_op_buffer_.Playback(canvas, PlaybackParams(image_provider), &offsets);
 }
 
 void DisplayItemList::CaptureContent(const gfx::Rect& rect,
diff --git a/cc/paint/display_item_list.h b/cc/paint/display_item_list.h
index 4b285f8..a61ed5c 100644
--- a/cc/paint/display_item_list.h
+++ b/cc/paint/display_item_list.h
@@ -41,7 +41,6 @@
 }
 
 namespace cc {
-class PaintWorkletImageProvider;
 
 // DisplayItemList is a container of paint operations. One can populate the list
 // using StartPaint, followed by push{,_with_data,_with_array} functions
@@ -60,9 +59,7 @@
 
   explicit DisplayItemList(UsageHint = kTopLevelDisplayItemList);
 
-  void Raster(SkCanvas* canvas,
-              ImageProvider* image_provider = nullptr,
-              PaintWorkletImageProvider* = nullptr) const;
+  void Raster(SkCanvas* canvas, ImageProvider* image_provider = nullptr) const;
 
   // Captures the DrawTextBlobOp within |rect| and returns the associated
   // NodeHolder in |content|.
diff --git a/cc/paint/image_provider.cc b/cc/paint/image_provider.cc
index f4b2805a..fa224ad5 100644
--- a/cc/paint/image_provider.cc
+++ b/cc/paint/image_provider.cc
@@ -4,39 +4,48 @@
 
 #include "cc/paint/image_provider.h"
 
+#include "cc/paint/paint_record.h"
+
 namespace cc {
 
-ImageProvider::ScopedDecodedDrawImage::ScopedDecodedDrawImage() = default;
+ImageProvider::ScopedResult::ScopedResult() = default;
 
-ImageProvider::ScopedDecodedDrawImage::ScopedDecodedDrawImage(
-    DecodedDrawImage image)
+ImageProvider::ScopedResult::ScopedResult(DecodedDrawImage image)
     : image_(std::move(image)) {}
 
-ImageProvider::ScopedDecodedDrawImage::ScopedDecodedDrawImage(
-    DecodedDrawImage image,
-    DestructionCallback callback)
+ImageProvider::ScopedResult::ScopedResult(DecodedDrawImage image,
+                                          DestructionCallback callback)
     : image_(std::move(image)), destruction_callback_(std::move(callback)) {}
 
-ImageProvider::ScopedDecodedDrawImage::ScopedDecodedDrawImage(
-    ScopedDecodedDrawImage&& other) {
-  image_ = std::move(other.image_);
-  destruction_callback_ = std::move(other.destruction_callback_);
+ImageProvider::ScopedResult::ScopedResult(const PaintRecord* record,
+                                          DestructionCallback callback)
+    : record_(record), destruction_callback_(std::move(callback)) {
+  DCHECK(!destruction_callback_.is_null());
 }
 
-ImageProvider::ScopedDecodedDrawImage& ImageProvider::ScopedDecodedDrawImage::
-operator=(ScopedDecodedDrawImage&& other) {
+ImageProvider::ScopedResult::ScopedResult(ScopedResult&& other)
+    : image_(std::move(other.image_)),
+      record_(other.record_),
+      destruction_callback_(std::move(other.destruction_callback_)) {
+  other.record_ = nullptr;
+}
+
+ImageProvider::ScopedResult& ImageProvider::ScopedResult::operator=(
+    ScopedResult&& other) {
   DestroyDecode();
 
   image_ = std::move(other.image_);
+  record_ = other.record_;
   destruction_callback_ = std::move(other.destruction_callback_);
+  other.record_ = nullptr;
   return *this;
 }
 
-ImageProvider::ScopedDecodedDrawImage::~ScopedDecodedDrawImage() {
+ImageProvider::ScopedResult::~ScopedResult() {
   DestroyDecode();
 }
 
-void ImageProvider::ScopedDecodedDrawImage::DestroyDecode() {
+void ImageProvider::ScopedResult::DestroyDecode() {
   if (!destruction_callback_.is_null())
     std::move(destruction_callback_).Run();
 }
diff --git a/cc/paint/image_provider.h b/cc/paint/image_provider.h
index 2a0948b..9ec5b3b 100644
--- a/cc/paint/image_provider.h
+++ b/cc/paint/image_provider.h
@@ -6,6 +6,7 @@
 #define CC_PAINT_IMAGE_PROVIDER_H_
 
 #include "base/callback.h"
+#include "base/optional.h"
 #include "cc/paint/decoded_draw_image.h"
 #include "cc/paint/draw_image.h"
 #include "cc/paint/paint_export.h"
@@ -19,40 +20,44 @@
 // rasterization.
 class CC_PAINT_EXPORT ImageProvider {
  public:
-  class CC_PAINT_EXPORT ScopedDecodedDrawImage {
+  class CC_PAINT_EXPORT ScopedResult {
    public:
     using DestructionCallback = base::OnceClosure;
 
-    ScopedDecodedDrawImage();
-    explicit ScopedDecodedDrawImage(DecodedDrawImage image);
-    ScopedDecodedDrawImage(DecodedDrawImage image,
-                           DestructionCallback callback);
-    ~ScopedDecodedDrawImage();
+    ScopedResult();
+    explicit ScopedResult(DecodedDrawImage image);
+    ScopedResult(DecodedDrawImage image, DestructionCallback callback);
+    ScopedResult(const PaintRecord* record, DestructionCallback callback);
+    ~ScopedResult();
 
-    ScopedDecodedDrawImage(ScopedDecodedDrawImage&& other);
-    ScopedDecodedDrawImage& operator=(ScopedDecodedDrawImage&& other);
+    ScopedResult(ScopedResult&& other);
+    ScopedResult& operator=(ScopedResult&& other);
 
-    operator bool() const {
-      return image_.image() || image_.transfer_cache_entry_id();
-    }
+    operator bool() const { return image_ || record_; }
     const DecodedDrawImage& decoded_image() const { return image_; }
     bool needs_unlock() const { return !destruction_callback_.is_null(); }
+    const PaintRecord* paint_record() {
+      DCHECK(record_);
+      return record_;
+    }
 
    private:
     void DestroyDecode();
 
     DecodedDrawImage image_;
+    const PaintRecord* record_ = nullptr;
     DestructionCallback destruction_callback_;
 
-    DISALLOW_COPY_AND_ASSIGN(ScopedDecodedDrawImage);
+    DISALLOW_COPY_AND_ASSIGN(ScopedResult);
   };
 
   virtual ~ImageProvider() {}
 
-  // Returns the DecodedDrawImage to use for this PaintImage. If no image is
-  // provided, the draw for this image will be skipped during raster.
-  virtual ScopedDecodedDrawImage GetDecodedDrawImage(
-      const DrawImage& draw_image) = 0;
+  // Returns either:
+  // 1. The DecodedDrawImage to use for this PaintImage. If no image is
+  // provided, the draw for this image will be skipped during raster. Or,
+  // 2. The PaintRecord produced by paint worklet JS paint callback.
+  virtual ScopedResult GetRasterContent(const DrawImage& draw_image) = 0;
 };
 
 }  // namespace cc
diff --git a/cc/paint/paint_filter.cc b/cc/paint/paint_filter.cc
index 2cd34c8..d4c2e34 100644
--- a/cc/paint/paint_filter.cc
+++ b/cc/paint/paint_filter.cc
@@ -691,12 +691,12 @@
     ImageProvider* image_provider) const {
   DrawImage draw_image(image_, SkIRect::MakeWH(image_.width(), image_.height()),
                        filter_quality_, SkMatrix::I());
-  auto scoped_decoded_image = image_provider->GetDecodedDrawImage(draw_image);
-  if (!scoped_decoded_image)
+  auto scoped_result = image_provider->GetRasterContent(draw_image);
+  if (!scoped_result)
     return nullptr;
 
   auto decoded_sk_image = sk_ref_sp<SkImage>(
-      const_cast<SkImage*>(scoped_decoded_image.decoded_image().image().get()));
+      const_cast<SkImage*>(scoped_result.decoded_image().image().get()));
   PaintImage decoded_paint_image =
       PaintImageBuilder::WithDefault()
           .set_id(image_.stable_id())
diff --git a/cc/paint/paint_filter_unittest.cc b/cc/paint/paint_filter_unittest.cc
index a834386..a7380d221 100644
--- a/cc/paint/paint_filter_unittest.cc
+++ b/cc/paint/paint_filter_unittest.cc
@@ -17,10 +17,10 @@
   MockImageProvider() = default;
   ~MockImageProvider() override = default;
 
-  ScopedDecodedDrawImage GetDecodedDrawImage(
-      const DrawImage& draw_image) override {
+  ScopedResult GetRasterContent(const DrawImage& draw_image) override {
+    DCHECK(!draw_image.paint_image().IsPaintWorklet());
     image_count_++;
-    return ScopedDecodedDrawImage(DecodedDrawImage(
+    return ScopedResult(DecodedDrawImage(
         CreateBitmapImage(gfx::Size(10, 10)).GetSkImage(), SkSize::MakeEmpty(),
         SkSize::Make(1.0f, 1.0f), draw_image.filter_quality(), true));
   }
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index a1f407a..1a6d63a7 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -302,11 +302,8 @@
   return sizeof(T);
 }
 
-PlaybackParams::PlaybackParams(
-    ImageProvider* image_provider,
-    PaintWorkletImageProvider* paint_worklet_image_provider)
+PlaybackParams::PlaybackParams(ImageProvider* image_provider)
     : image_provider(image_provider),
-      paint_worklet_image_provider(paint_worklet_image_provider),
       original_ctm(SkMatrix::I()),
       custom_callback(CustomDataRasterCallback()),
       did_draw_op_callback(DidDrawOpCallback()) {}
@@ -1225,12 +1222,11 @@
       op->image, SkIRect::MakeWH(op->image.width(), op->image.height()),
       flags ? flags->getFilterQuality() : kNone_SkFilterQuality,
       canvas->getTotalMatrix());
-  auto scoped_decoded_draw_image =
-      params.image_provider->GetDecodedDrawImage(draw_image);
-  if (!scoped_decoded_draw_image)
+  auto scoped_result = params.image_provider->GetRasterContent(draw_image);
+  if (!scoped_result)
     return;
 
-  const auto& decoded_image = scoped_decoded_draw_image.decoded_image();
+  const auto& decoded_image = scoped_result.decoded_image();
   DCHECK(decoded_image.image());
 
   DCHECK_EQ(0, static_cast<int>(decoded_image.src_rect_offset().width()));
@@ -1275,12 +1271,11 @@
   DrawImage draw_image(
       op->image, int_src_rect,
       flags ? flags->getFilterQuality() : kNone_SkFilterQuality, matrix);
-  auto scoped_decoded_draw_image =
-      params.image_provider->GetDecodedDrawImage(draw_image);
-  if (!scoped_decoded_draw_image)
+  auto scoped_result = params.image_provider->GetRasterContent(draw_image);
+  if (!scoped_result)
     return;
 
-  const auto& decoded_image = scoped_decoded_draw_image.decoded_image();
+  const auto& decoded_image = scoped_result.decoded_image();
   DCHECK(decoded_image.image());
 
   SkSize scale_adjustment = SkSize::Make(
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index d51f317..938b060 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -42,7 +42,6 @@
 class ClientPaintCache;
 class ImageProvider;
 class ServicePaintCache;
-class PaintWorkletImageProvider;
 
 class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix {
  public:
@@ -109,9 +108,7 @@
       base::RepeatingCallback<void(SkCanvas* canvas, uint32_t id)>;
   using DidDrawOpCallback = base::RepeatingCallback<void()>;
 
-  explicit PlaybackParams(
-      ImageProvider* image_provider,
-      PaintWorkletImageProvider* paint_worklet_image_provider = nullptr);
+  explicit PlaybackParams(ImageProvider* image_provider);
   PlaybackParams(
       ImageProvider* image_provider,
       const SkMatrix& original_ctm,
@@ -123,7 +120,6 @@
   PlaybackParams& operator=(const PlaybackParams& other);
 
   ImageProvider* image_provider;
-  PaintWorkletImageProvider* paint_worklet_image_provider;
   SkMatrix original_ctm;
   CustomDataRasterCallback custom_callback;
   DidDrawOpCallback did_draw_op_callback;
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index 37401df..a682307 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -2711,18 +2711,19 @@
 
   ~MockImageProvider() override = default;
 
-  ScopedDecodedDrawImage GetDecodedDrawImage(
+  ImageProvider::ScopedResult GetRasterContent(
       const DrawImage& draw_image) override {
+    DCHECK(!draw_image.paint_image().IsPaintWorklet());
     if (fail_all_decodes_)
-      return ScopedDecodedDrawImage();
+      return ImageProvider::ScopedResult();
 
     SkBitmap bitmap;
     bitmap.allocPixelsFlags(SkImageInfo::MakeN32Premul(10, 10),
                             SkBitmap::kZeroPixels_AllocFlag);
     sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
     size_t i = index_++;
-    return ScopedDecodedDrawImage(DecodedDrawImage(
-        image, src_rect_offset_[i], scale_[i], quality_[i], true));
+    return ScopedResult(DecodedDrawImage(image, src_rect_offset_[i], scale_[i],
+                                         quality_[i], true));
   }
 
  private:
diff --git a/cc/paint/paint_op_writer.cc b/cc/paint/paint_op_writer.cc
index 492e0fbc..8c2f1f8 100644
--- a/cc/paint/paint_op_writer.cc
+++ b/cc/paint/paint_op_writer.cc
@@ -247,7 +247,7 @@
   }
 
   // Default mode uses the transfer cache.
-  auto decoded_image = options_.image_provider->GetDecodedDrawImage(draw_image);
+  auto decoded_image = options_.image_provider->GetRasterContent(draw_image);
   DCHECK(!decoded_image.decoded_image().image())
       << "Use transfer cache for image serialization";
   const DecodedDrawImage& decoded_draw_image = decoded_image.decoded_image();
diff --git a/cc/paint/paint_shader.cc b/cc/paint/paint_shader.cc
index 9c5ce1c7..23a677a 100644
--- a/cc/paint/paint_shader.cc
+++ b/cc/paint/paint_shader.cc
@@ -335,7 +335,7 @@
   SkIRect int_src_rect;
   src_rect.roundOut(&int_src_rect);
   DrawImage draw_image(image_, int_src_rect, quality, total_image_matrix);
-  auto decoded_draw_image = image_provider->GetDecodedDrawImage(draw_image);
+  auto decoded_draw_image = image_provider->GetRasterContent(draw_image);
   if (!decoded_draw_image)
     return nullptr;
 
diff --git a/cc/paint/paint_shader_unittest.cc b/cc/paint/paint_shader_unittest.cc
index e097e43..83107481 100644
--- a/cc/paint/paint_shader_unittest.cc
+++ b/cc/paint/paint_shader_unittest.cc
@@ -37,17 +37,18 @@
   MockImageProvider() = default;
   ~MockImageProvider() override = default;
 
-  ScopedDecodedDrawImage GetDecodedDrawImage(
+  ImageProvider::ScopedResult GetRasterContent(
       const DrawImage& draw_image) override {
+    DCHECK(!draw_image.paint_image().IsPaintWorklet());
     draw_image_ = draw_image;
 
     SkBitmap bitmap;
     bitmap.allocN32Pixels(10, 10);
     bitmap.eraseColor(SK_ColorBLACK);
     sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
-    return ScopedDecodedDrawImage(
-        DecodedDrawImage(image, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f),
-                         draw_image.filter_quality(), true));
+    return ScopedResult(DecodedDrawImage(image, SkSize::MakeEmpty(),
+                                         SkSize::Make(1.0f, 1.0f),
+                                         draw_image.filter_quality(), true));
   }
 
   const DrawImage& draw_image() const { return draw_image_; }
diff --git a/cc/paint/scoped_raster_flags_unittest.cc b/cc/paint/scoped_raster_flags_unittest.cc
index 801d6cf..3c8ffcb 100644
--- a/cc/paint/scoped_raster_flags_unittest.cc
+++ b/cc/paint/scoped_raster_flags_unittest.cc
@@ -17,15 +17,15 @@
   MockImageProvider() = default;
   ~MockImageProvider() override { EXPECT_EQ(ref_count_, 0); }
 
-  ScopedDecodedDrawImage GetDecodedDrawImage(
-      const DrawImage& draw_image) override {
+  ScopedResult GetRasterContent(const DrawImage& draw_image) override {
+    DCHECK(!draw_image.paint_image().IsPaintWorklet());
     ref_count_++;
 
     SkBitmap bitmap;
     bitmap.allocN32Pixels(10, 10);
     sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
 
-    return ScopedDecodedDrawImage(
+    return ScopedResult(
         DecodedDrawImage(image, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f),
                          draw_image.filter_quality(), true),
         base::BindOnce(&MockImageProvider::UnrefImage, base::Unretained(this)));
diff --git a/cc/raster/paint_worklet_image_provider.cc b/cc/raster/paint_worklet_image_provider.cc
index 38feceb..f33424d 100644
--- a/cc/raster/paint_worklet_image_provider.cc
+++ b/cc/raster/paint_worklet_image_provider.cc
@@ -4,6 +4,7 @@
 
 #include "cc/raster/paint_worklet_image_provider.h"
 
+#include <utility>
 #include "cc/tiles/paint_worklet_image_cache.h"
 
 namespace cc {
@@ -22,4 +23,12 @@
 PaintWorkletImageProvider& PaintWorkletImageProvider::operator=(
     PaintWorkletImageProvider&& other) = default;
 
+ImageProvider::ScopedResult PaintWorkletImageProvider::GetPaintRecordResult(
+    PaintWorkletInput* input) {
+  std::pair<PaintRecord*, base::OnceCallback<void()>> record_and_callback =
+      cache_->GetPaintRecordAndRef(input);
+  return ImageProvider::ScopedResult(record_and_callback.first,
+                                     std::move(record_and_callback.second));
+}
+
 }  // namespace cc
diff --git a/cc/raster/paint_worklet_image_provider.h b/cc/raster/paint_worklet_image_provider.h
index 0712b12b..fe67887 100644
--- a/cc/raster/paint_worklet_image_provider.h
+++ b/cc/raster/paint_worklet_image_provider.h
@@ -10,6 +10,7 @@
 
 namespace cc {
 class PaintWorkletImageCache;
+class PaintWorkletInput;
 
 // PaintWorkletImageProvider is a bridge between PaintWorkletImageCache and its
 // rasterization.
@@ -21,6 +22,8 @@
   PaintWorkletImageProvider(PaintWorkletImageProvider&& other);
   PaintWorkletImageProvider& operator=(PaintWorkletImageProvider&& other);
 
+  ImageProvider::ScopedResult GetPaintRecordResult(PaintWorkletInput* input);
+
  private:
   PaintWorkletImageCache* cache_;
 
diff --git a/cc/raster/playback_image_provider.cc b/cc/raster/playback_image_provider.cc
index 9a41e8d..4e8cff91 100644
--- a/cc/raster/playback_image_provider.cc
+++ b/cc/raster/playback_image_provider.cc
@@ -33,17 +33,18 @@
 PlaybackImageProvider& PlaybackImageProvider::operator=(
     PlaybackImageProvider&& other) = default;
 
-ImageProvider::ScopedDecodedDrawImage
-PlaybackImageProvider::GetDecodedDrawImage(const DrawImage& draw_image) {
+ImageProvider::ScopedResult PlaybackImageProvider::GetRasterContent(
+    const DrawImage& draw_image) {
+  DCHECK(!draw_image.paint_image().IsPaintWorklet());
   // Return an empty decoded image if we are skipping all images during this
   // raster.
   if (!settings_.has_value())
-    return ScopedDecodedDrawImage();
+    return ScopedResult();
 
   const PaintImage& paint_image = draw_image.paint_image();
   if (settings_->images_to_skip.count(paint_image.stable_id()) != 0) {
     DCHECK(paint_image.GetSkImage()->isLazyGenerated());
-    return ScopedDecodedDrawImage();
+    return ScopedResult();
   }
 
   const auto& it =
@@ -54,13 +55,13 @@
 
   DrawImage adjusted_image(draw_image, 1.f, frame_index);
   if (!cache_->UseCacheForDrawImage(adjusted_image)) {
-    return ScopedDecodedDrawImage(DecodedDrawImage(
+    return ScopedResult(DecodedDrawImage(
         paint_image.GetSkImage(), SkSize::Make(0, 0), SkSize::Make(1.f, 1.f),
         draw_image.filter_quality(), true /* is_budgeted */));
   }
 
   auto decoded_draw_image = cache_->GetDecodedImageForDraw(adjusted_image);
-  return ScopedDecodedDrawImage(
+  return ScopedResult(
       decoded_draw_image,
       base::BindOnce(&UnrefImageFromCache, std::move(adjusted_image), cache_,
                      decoded_draw_image));
diff --git a/cc/raster/playback_image_provider.h b/cc/raster/playback_image_provider.h
index 990835c..5e76746e 100644
--- a/cc/raster/playback_image_provider.h
+++ b/cc/raster/playback_image_provider.h
@@ -42,7 +42,7 @@
   PlaybackImageProvider& operator=(PlaybackImageProvider&& other);
 
   // ImageProvider implementation.
-  ScopedDecodedDrawImage GetDecodedDrawImage(
+  ImageProvider::ScopedResult GetRasterContent(
       const DrawImage& draw_image) override;
 
  private:
diff --git a/cc/raster/playback_image_provider_unittest.cc b/cc/raster/playback_image_provider_unittest.cc
index dbc5ba0c..c599509 100644
--- a/cc/raster/playback_image_provider_unittest.cc
+++ b/cc/raster/playback_image_provider_unittest.cc
@@ -71,7 +71,7 @@
   SkIRect rect = SkIRect::MakeWH(10, 10);
   SkMatrix matrix = SkMatrix::I();
 
-  EXPECT_FALSE(provider.GetDecodedDrawImage(DrawImage(
+  EXPECT_FALSE(provider.GetRasterContent(DrawImage(
       PaintImageBuilder::WithDefault()
           .set_id(PaintImage::GetNextId())
           .set_image(CreateRasterImage(), PaintImage::GetNextContentId())
@@ -79,7 +79,7 @@
       rect, kMedium_SkFilterQuality, matrix)));
   EXPECT_EQ(cache.images_decoded(), 0);
 
-  EXPECT_FALSE(provider.GetDecodedDrawImage(
+  EXPECT_FALSE(provider.GetRasterContent(
       CreateDiscardableDrawImage(gfx::Size(10, 10), nullptr, SkRect::Make(rect),
                                  kMedium_SkFilterQuality, matrix)));
   EXPECT_EQ(cache.images_decoded(), 0);
@@ -97,7 +97,7 @@
 
   SkIRect rect = SkIRect::MakeWH(10, 10);
   SkMatrix matrix = SkMatrix::I();
-  EXPECT_FALSE(provider.GetDecodedDrawImage(
+  EXPECT_FALSE(provider.GetRasterContent(
       DrawImage(skip_image, rect, kMedium_SkFilterQuality, matrix)));
   EXPECT_EQ(cache.images_decoded(), 0);
 }
@@ -112,7 +112,7 @@
   {
     SkRect rect = SkRect::MakeWH(10, 10);
     SkMatrix matrix = SkMatrix::I();
-    auto decode = provider.GetDecodedDrawImage(CreateDiscardableDrawImage(
+    auto decode = provider.GetRasterContent(CreateDiscardableDrawImage(
         gfx::Size(10, 10), nullptr, rect, kMedium_SkFilterQuality, matrix));
     EXPECT_TRUE(decode);
     EXPECT_EQ(cache.refed_image_count(), 1);
@@ -140,7 +140,7 @@
   SkIRect rect = SkIRect::MakeWH(10, 10);
   SkMatrix matrix = SkMatrix::I();
   DrawImage draw_image(image, rect, kMedium_SkFilterQuality, matrix);
-  provider.GetDecodedDrawImage(draw_image);
+  provider.GetRasterContent(draw_image);
   ASSERT_TRUE(cache.last_image().paint_image());
   ASSERT_EQ(cache.last_image().paint_image(), image);
   ASSERT_EQ(cache.last_image().frame_index(), 1u);
@@ -158,7 +158,7 @@
     SkMatrix matrix = SkMatrix::I();
     auto draw_image = DrawImage(CreateBitmapImage(gfx::Size(10, 10)), rect,
                                 kMedium_SkFilterQuality, matrix);
-    auto decode = provider.GetDecodedDrawImage(draw_image);
+    auto decode = provider.GetRasterContent(draw_image);
     EXPECT_TRUE(decode);
     EXPECT_EQ(cache.refed_image_count(), 1);
   }
@@ -178,7 +178,7 @@
     SkMatrix matrix = SkMatrix::I();
     auto draw_image = DrawImage(CreateBitmapImage(gfx::Size(10, 10)), rect,
                                 kMedium_SkFilterQuality, matrix);
-    auto decode = provider.GetDecodedDrawImage(draw_image);
+    auto decode = provider.GetRasterContent(draw_image);
     EXPECT_TRUE(decode);
     EXPECT_EQ(cache.refed_image_count(), 0);
   }
diff --git a/cc/raster/raster_source.cc b/cc/raster/raster_source.cc
index 1ab4da2..8f510fa 100644
--- a/cc/raster/raster_source.cc
+++ b/cc/raster/raster_source.cc
@@ -169,21 +169,17 @@
     raster_canvas->clear(SK_ColorTRANSPARENT);
   }
 
-  PlaybackToCanvas(raster_canvas, settings.image_provider,
-                   settings.paint_worklet_image_provider);
+  PlaybackToCanvas(raster_canvas, settings.image_provider);
   raster_canvas->restore();
 }
 
-void RasterSource::PlaybackToCanvas(
-    SkCanvas* raster_canvas,
-    ImageProvider* image_provider,
-    PaintWorkletImageProvider* paint_worklet_image_provider) const {
+void RasterSource::PlaybackToCanvas(SkCanvas* raster_canvas,
+                                    ImageProvider* image_provider) const {
   // TODO(enne): Temporary CHECK debugging for http://crbug.com/823835
   CHECK(display_list_.get());
   int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
   for (int i = 0; i < repeat_count; ++i)
-    display_list_->Raster(raster_canvas, image_provider,
-                          paint_worklet_image_provider);
+    display_list_->Raster(raster_canvas, image_provider);
 }
 
 sk_sp<SkPicture> RasterSource::GetFlattenedPicture() {
@@ -193,7 +189,7 @@
   SkCanvas* canvas = recorder.beginRecording(size_.width(), size_.height());
   if (!size_.IsEmpty()) {
     canvas->clear(SK_ColorTRANSPARENT);
-    PlaybackToCanvas(canvas, nullptr, nullptr);
+    PlaybackToCanvas(canvas, nullptr);
   }
 
   return recorder.finishRecordingAsPicture();
diff --git a/cc/raster/raster_source.h b/cc/raster/raster_source.h
index 1b8b623..b73992a 100644
--- a/cc/raster/raster_source.h
+++ b/cc/raster/raster_source.h
@@ -27,7 +27,6 @@
 class DisplayItemList;
 class DrawImage;
 class ImageProvider;
-class PaintWorkletImageProvider;
 
 class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
  public:
@@ -40,13 +39,8 @@
     // If set to true, we should use LCD text.
     bool use_lcd_text = true;
 
-    // The ImageProvider used to replace images during playback.
     ImageProvider* image_provider = nullptr;
 
-    // The PaintWorkletImageProvider is a bridge connecting the playback and the
-    // paint worklet image cache.
-    PaintWorkletImageProvider* paint_worklet_image_provider = nullptr;
-
     RasterColorSpace raster_color_space;
   };
 
@@ -75,10 +69,8 @@
   //
   // Note that this should only be called after the image decode controller has
   // been set, which happens during commit.
-  virtual void PlaybackToCanvas(
-      SkCanvas* canvas,
-      ImageProvider* image_provider,
-      PaintWorkletImageProvider* paint_worklet_image_provider) const;
+  virtual void PlaybackToCanvas(SkCanvas* canvas,
+                                ImageProvider* image_provider) const;
 
   // Returns whether the given rect at given scale is of solid color in
   // this raster source, as well as the solid color value.
diff --git a/cc/test/fake_raster_source.cc b/cc/test/fake_raster_source.cc
index e845c9f..09e369b 100644
--- a/cc/test/fake_raster_source.cc
+++ b/cc/test/fake_raster_source.cc
@@ -155,14 +155,11 @@
 
 FakeRasterSource::~FakeRasterSource() = default;
 
-void FakeRasterSource::PlaybackToCanvas(
-    SkCanvas* canvas,
-    ImageProvider* image_provider,
-    PaintWorkletImageProvider* paint_worklet_image_provider) const {
+void FakeRasterSource::PlaybackToCanvas(SkCanvas* canvas,
+                                        ImageProvider* image_provider) const {
   if (playback_allowed_event_)
     playback_allowed_event_->Wait();
-  RasterSource::PlaybackToCanvas(canvas, image_provider,
-                                 paint_worklet_image_provider);
+  RasterSource::PlaybackToCanvas(canvas, image_provider);
 }
 
 }  // namespace cc
diff --git a/cc/test/fake_raster_source.h b/cc/test/fake_raster_source.h
index 26c15268..7e6119d 100644
--- a/cc/test/fake_raster_source.h
+++ b/cc/test/fake_raster_source.h
@@ -37,10 +37,8 @@
       const RecordingSource* recording_source,
       base::WaitableEvent* playback_allowed_event);
 
-  void PlaybackToCanvas(
-      SkCanvas* canvas,
-      ImageProvider* image_provider,
-      PaintWorkletImageProvider* paint_worklet_image_provider) const override;
+  void PlaybackToCanvas(SkCanvas* canvas,
+                        ImageProvider* image_provider) const override;
 
  protected:
   explicit FakeRasterSource(const RecordingSource* recording_source);
diff --git a/cc/test/test_options_provider.cc b/cc/test/test_options_provider.cc
index e59e324..fa3d846d 100644
--- a/cc/test/test_options_provider.cc
+++ b/cc/test/test_options_provider.cc
@@ -61,13 +61,14 @@
   CHECK(strike_client_.readStrikeData(font_data.data(), font_data.size()));
 }
 
-ImageProvider::ScopedDecodedDrawImage TestOptionsProvider::GetDecodedDrawImage(
+ImageProvider::ScopedResult TestOptionsProvider::GetRasterContent(
     const DrawImage& draw_image) {
+  DCHECK(!draw_image.paint_image().IsPaintWorklet());
   uint32_t image_id = draw_image.paint_image().GetSkImage()->uniqueID();
   // Lock and reuse the entry if possible.
   const EntryKey entry_key(TransferCacheEntryType::kImage, image_id);
   if (LockEntryDirect(entry_key)) {
-    return ScopedDecodedDrawImage(
+    return ScopedResult(
         DecodedDrawImage(image_id, SkSize::MakeEmpty(), draw_image.scale(),
                          draw_image.filter_quality(), false, true));
   }
@@ -86,12 +87,12 @@
   std::vector<uint8_t> data;
   data.resize(cache_entry.SerializedSize());
   if (!cache_entry.Serialize(base::span<uint8_t>(data.data(), data.size()))) {
-    return ScopedDecodedDrawImage();
+    return ScopedResult();
   }
 
   CreateEntryDirect(entry_key, base::span<uint8_t>(data.data(), data.size()));
 
-  return ScopedDecodedDrawImage(
+  return ScopedResult(
       DecodedDrawImage(image_id, SkSize::MakeEmpty(), draw_image.scale(),
                        draw_image.filter_quality(), false, true));
 }
diff --git a/cc/test/test_options_provider.h b/cc/test/test_options_provider.h
index 5488898..14fd1d5 100644
--- a/cc/test/test_options_provider.h
+++ b/cc/test/test_options_provider.h
@@ -61,7 +61,7 @@
   class DiscardableManager;
 
   // ImageProvider implementation.
-  ScopedDecodedDrawImage GetDecodedDrawImage(
+  ImageProvider::ScopedResult GetRasterContent(
       const DrawImage& draw_image) override;
 
   testing::StrictMock<MockCanvas> canvas_;
diff --git a/cc/tiles/paint_worklet_image_cache.cc b/cc/tiles/paint_worklet_image_cache.cc
index 0ad7ccf..8a5db88 100644
--- a/cc/tiles/paint_worklet_image_cache.cc
+++ b/cc/tiles/paint_worklet_image_cache.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include "cc/tiles/paint_worklet_image_cache.h"
+
+#include "base/bind.h"
 #include "cc/paint/paint_worklet_layer_painter.h"
 
 namespace cc {
@@ -52,9 +54,25 @@
       std::make_pair(std::move(record), 0);
 }
 
-PaintRecord* PaintWorkletImageCache::GetPaintRecordForTest(
-    PaintWorkletInput* input) {
-  return records_[input].first.get();
+std::pair<PaintRecord*, base::OnceCallback<void()>>
+PaintWorkletImageCache::GetPaintRecordAndRef(PaintWorkletInput* input) {
+  records_[input].second++;
+  // The PaintWorkletImageCache object lives as long as the LayerTreeHostImpl,
+  // and that ensures that this pointer and the input will be alive when this
+  // callback is executed.
+  auto callback =
+      base::BindOnce(&PaintWorkletImageCache::DecrementCacheRefCount,
+                     base::Unretained(this), base::Unretained(input));
+  return std::make_pair(records_[input].first.get(), std::move(callback));
+}
+
+void PaintWorkletImageCache::DecrementCacheRefCount(PaintWorkletInput* input) {
+  auto it = records_.find(input);
+  DCHECK(it != records_.end());
+
+  auto& pair = it->second;
+  DCHECK_GT(pair.second, 0u);
+  pair.second--;
 }
 
 }  // namespace cc
diff --git a/cc/tiles/paint_worklet_image_cache.h b/cc/tiles/paint_worklet_image_cache.h
index 7b7b23df..4fb4835 100644
--- a/cc/tiles/paint_worklet_image_cache.h
+++ b/cc/tiles/paint_worklet_image_cache.h
@@ -33,7 +33,10 @@
 
   void PaintImageInTask(const PaintImage& paint_image);
 
-  PaintRecord* GetPaintRecordForTest(PaintWorkletInput* input);
+  // Returns a callback to decrement the ref count for the corresponding entry.
+  std::pair<PaintRecord*, base::OnceCallback<void()>> GetPaintRecordAndRef(
+      PaintWorkletInput* input);
+
   const base::flat_map<PaintWorkletInput*,
                        std::pair<sk_sp<PaintRecord>, size_t>>&
   GetRecordsForTest() {
@@ -41,10 +44,12 @@
   }
 
  private:
+  void DecrementCacheRefCount(PaintWorkletInput* input);
   // This is a map of paint worklet inputs to a pair of paint record and a
   // reference count. The paint record is the representation of the worklet
   // output based on the input, and the reference count is the number of times
   // that it is used for tile rasterization.
+  // TODO(xidachen): use a struct instead of std::pair.
   base::flat_map<PaintWorkletInput*, std::pair<sk_sp<PaintRecord>, size_t>>
       records_;
   // The PaintWorkletImageCache is owned by ImageController, which has the same
diff --git a/cc/tiles/paint_worklet_image_cache_unittest.cc b/cc/tiles/paint_worklet_image_cache_unittest.cc
index 5b448d6..2b70839 100644
--- a/cc/tiles/paint_worklet_image_cache_unittest.cc
+++ b/cc/tiles/paint_worklet_image_cache_unittest.cc
@@ -7,6 +7,7 @@
 #include "cc/tiles/paint_worklet_image_cache.h"
 
 #include "cc/paint/draw_image.h"
+#include "cc/raster/paint_worklet_image_provider.h"
 #include "cc/test/skia_common.h"
 #include "cc/test/test_paint_worklet_input.h"
 #include "cc/test/test_paint_worklet_layer_painter.h"
@@ -52,7 +53,7 @@
   return cache->GetTaskForPaintWorkletImage(draw_image);
 }
 
-void TestPaintRecord(PaintRecord* record) {
+void TestPaintRecord(const PaintRecord* record) {
   EXPECT_EQ(record->total_op_count(), 1u);
 
   // GetOpAtForTesting check whether the type is the same as DrawImageOp or not.
@@ -67,12 +68,49 @@
   scoped_refptr<TileTask> task =
       GetTaskForPaintWorkletImage(paint_image, &cache);
   EXPECT_TRUE(task);
+  PaintWorkletImageProvider provider(&cache);
 
   TestTileTaskRunner::ProcessTask(task.get());
 
-  PaintRecord* record =
-      cache.GetPaintRecordForTest(paint_image.paint_worklet_input());
-  TestPaintRecord(record);
+  {
+    ImageProvider::ScopedResult result =
+        provider.GetPaintRecordResult(paint_image.paint_worklet_input());
+    EXPECT_TRUE(result.paint_record());
+    TestPaintRecord(result.paint_record());
+
+    base::flat_map<PaintWorkletInput*, std::pair<sk_sp<PaintRecord>, size_t>>
+        records = cache.GetRecordsForTest();
+    // Test the ref count.
+    EXPECT_EQ(records[paint_image.paint_worklet_input()].second, 1u);
+  }
+  base::flat_map<PaintWorkletInput*, std::pair<sk_sp<PaintRecord>, size_t>>
+      records = cache.GetRecordsForTest();
+  // Test the ref count, which should have been decremented when the result
+  // goes out of the scope.
+  EXPECT_EQ(records[paint_image.paint_worklet_input()].second, 0u);
+
+  {
+    ImageProvider::ScopedResult result =
+        provider.GetPaintRecordResult(paint_image.paint_worklet_input());
+
+    base::flat_map<PaintWorkletInput*, std::pair<sk_sp<PaintRecord>, size_t>>
+        records = cache.GetRecordsForTest();
+    // Test the ref count.
+    EXPECT_EQ(records[paint_image.paint_worklet_input()].second, 1u);
+
+    ImageProvider::ScopedResult moved_result = std::move(result);
+
+    EXPECT_FALSE(result);
+
+    EXPECT_TRUE(moved_result.paint_record());
+    TestPaintRecord(moved_result.paint_record());
+
+    // Once moved, the ref count from |result| should have been transferred to
+    // |moved_result|, so there should be only one un-ref when they both go out
+    // of scope.
+    EXPECT_EQ(records[paint_image.paint_worklet_input()].second, 1u);
+  }
+  EXPECT_EQ(records[paint_image.paint_worklet_input()].second, 0u);
 }
 
 TEST(PaintWorkletImageCacheTest, MultipleRecordsInCache) {
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 6970e507..493f5b0 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -72,6 +72,36 @@
   base::Optional<ScopedGpuRasterTaskTimer> gpu_timer_;
 };
 
+// This class is wrapper for both ImageProvider and PaintWorkletImageProvider,
+// which is used in RasterSource::PlaybackSettings. It looks at the draw image
+// and decides which one of the two providers to dispatch the request to.
+class DispatchingImageProvider : public ImageProvider {
+ public:
+  DispatchingImageProvider(
+      PlaybackImageProvider playback_image_provider,
+      PaintWorkletImageProvider paint_worklet_image_provider)
+      : playback_image_provider_(std::move(playback_image_provider)),
+        paint_worklet_image_provider_(std::move(paint_worklet_image_provider)) {
+  }
+  ~DispatchingImageProvider() override = default;
+
+  DispatchingImageProvider(DispatchingImageProvider&& other) = default;
+
+  ImageProvider::ScopedResult GetRasterContent(
+      const DrawImage& draw_image) override {
+    return draw_image.paint_image().IsPaintWorklet()
+               ? paint_worklet_image_provider_.GetPaintRecordResult(
+                     draw_image.paint_image().paint_worklet_input())
+               : playback_image_provider_.GetRasterContent(draw_image);
+  }
+
+ private:
+  PlaybackImageProvider playback_image_provider_;
+  PaintWorkletImageProvider paint_worklet_image_provider_;
+
+  DISALLOW_COPY_AND_ASSIGN(DispatchingImageProvider);
+};
+
 class RasterTaskImpl : public TileTask {
  public:
   RasterTaskImpl(TileManager* tile_manager,
@@ -85,8 +115,7 @@
                  std::unique_ptr<RasterBuffer> raster_buffer,
                  TileTask::Vector* dependencies,
                  bool is_gpu_rasterization,
-                 PlaybackImageProvider image_provider,
-                 PaintWorkletImageProvider paint_worklet_image_provider,
+                 DispatchingImageProvider image_provider,
                  GURL url)
       : TileTask(!is_gpu_rasterization, dependencies),
         tile_manager_(tile_manager),
@@ -106,12 +135,9 @@
         is_gpu_rasterization_(is_gpu_rasterization),
         raster_buffer_(std::move(raster_buffer)),
         image_provider_(std::move(image_provider)),
-        paint_worklet_image_provider_(std::move(paint_worklet_image_provider)),
         url_(std::move(url)) {
     DCHECK(origin_thread_checker_.CalledOnValidThread());
     playback_settings_.image_provider = &image_provider_;
-    playback_settings_.paint_worklet_image_provider =
-        &paint_worklet_image_provider_;
   }
 
   // Overridden from Task:
@@ -177,8 +203,7 @@
   int source_frame_number_;
   bool is_gpu_rasterization_;
   std::unique_ptr<RasterBuffer> raster_buffer_;
-  PlaybackImageProvider image_provider_;
-  PaintWorkletImageProvider paint_worklet_image_provider_;
+  DispatchingImageProvider image_provider_;
   GURL url_;
 
   DISALLOW_COPY_AND_ASSIGN(RasterTaskImpl);
@@ -1247,13 +1272,15 @@
 
   PaintWorkletImageProvider paint_worklet_image_provider(
       image_controller_.paint_worklet_image_cache());
+  DispatchingImageProvider dispatching_image_provider(
+      std::move(image_provider), std::move(paint_worklet_image_provider));
 
   return base::MakeRefCounted<RasterTaskImpl>(
       this, tile, std::move(resource), prioritized_tile.raster_source(),
       playback_settings, prioritized_tile.priority().resolution,
       invalidated_rect, prepare_tiles_count_, std::move(raster_buffer),
-      &decode_tasks, use_gpu_rasterization_, std::move(image_provider),
-      std::move(paint_worklet_image_provider), active_url_);
+      &decode_tasks, use_gpu_rasterization_,
+      std::move(dispatching_image_provider), active_url_);
 }
 
 void TileManager::ResetSignalsForTesting() {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index c6ecb8a5..7eee69f 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -218,19 +218,16 @@
   TRACE_EVENT_ASYNC_END0("cc", "LayerTreeHostImpl::SetVisible", id);
 }
 
-bool IsWheelBasedScroll(InputHandler::ScrollInputType type) {
-  return type == InputHandler::WHEEL;
-}
-
 enum ScrollThread { MAIN_THREAD, CC_THREAD };
 
 void RecordCompositorSlowScrollMetric(InputHandler::ScrollInputType type,
                                       ScrollThread scroll_thread) {
+  DCHECK_NE(type, InputHandler::SCROLL_INPUT_UNKNOWN);
   bool scroll_on_main_thread = (scroll_thread == MAIN_THREAD);
-  if (IsWheelBasedScroll(type)) {
+  if (type == InputHandler::WHEEL) {
     UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorWheelScrollUpdateThread",
                           scroll_on_main_thread);
-  } else {
+  } else if (type == InputHandler::TOUCHSCREEN) {
     UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorTouchScrollUpdateThread",
                           scroll_on_main_thread);
   }
@@ -3615,10 +3612,11 @@
                        TRACE_EVENT_SCOPE_THREAD, "isNull",
                        scrolling_node ? false : true);
   active_tree_->SetCurrentlyScrollingNode(scrolling_node);
-  // TODO(majidvp): get rid of wheel_scrolling_ and set is_direct_manipulation
+  // TODO(majidvp): get rid of touch_scrolling_ and set is_direct_manipulation
   // in input_handler_proxy instead.
-  wheel_scrolling_ = IsWheelBasedScroll(type);
-  scroll_state->set_is_direct_manipulation(!wheel_scrolling_);
+  touch_scrolling_ = type == InputHandler::TOUCHSCREEN;
+  wheel_scrolling_ = type == InputHandler::WHEEL;
+  scroll_state->set_is_direct_manipulation(touch_scrolling_);
   // Invoke |DistributeScrollDelta| even with zero delta and velocity to ensure
   // scroll customization callbacks are invoked.
   DistributeScrollDelta(scroll_state);
@@ -3645,7 +3643,7 @@
   client_->RenewTreePriority();
   RecordCompositorSlowScrollMetric(type, CC_THREAD);
 
-  UpdateScrollSourceInfo(wheel_scrolling_);
+  UpdateScrollSourceInfo(type);
 
   return scroll_status;
 }
@@ -4402,7 +4400,7 @@
 
   scroll_state->set_delta_consumed_for_scroll_sequence(
       did_lock_scrolling_layer_);
-  scroll_state->set_is_direct_manipulation(!wheel_scrolling_);
+  scroll_state->set_is_direct_manipulation(touch_scrolling_);
   scroll_state->set_current_native_scrolling_node(scroll_node);
 
   DistributeScrollDelta(scroll_state);
@@ -5667,10 +5665,11 @@
   }
 }
 
-void LayerTreeHostImpl::UpdateScrollSourceInfo(bool is_wheel_scroll) {
-  if (is_wheel_scroll)
+void LayerTreeHostImpl::UpdateScrollSourceInfo(
+    InputHandler::ScrollInputType type) {
+  if (type == InputHandler::WHEEL)
     has_scrolled_by_wheel_ = true;
-  else
+  else if (type == InputHandler::TOUCHSCREEN)
     has_scrolled_by_touch_ = true;
 }
 
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 9c0ef87..1cc6b645 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -865,7 +865,7 @@
   // thread side to keep track of the frequency of scrolling with different
   // sources per page load. TODO(crbug.com/691886): Use GRC API to plumb the
   // scroll source info for Use Counters.
-  void UpdateScrollSourceInfo(bool is_wheel_scroll);
+  void UpdateScrollSourceInfo(InputHandler::ScrollInputType type);
 
   bool IsScrolledBy(LayerImpl* child, ScrollNode* ancestor);
   void ShowScrollbarsForImplScroll(ElementId element_id);
@@ -952,6 +952,7 @@
 
   InputHandlerClient* input_handler_client_ = nullptr;
   bool did_lock_scrolling_layer_ = false;
+  bool touch_scrolling_ = false;
   bool wheel_scrolling_ = false;
   bool scroll_affects_scroll_handler_ = false;
   ElementId scroll_element_id_mouse_currently_over_;
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index b9d0622..27460e0 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1867,6 +1867,7 @@
   sign_bundle = is_official_build
 
   enable_language_splits = enable_chrome_language_splits
+  compress_shared_libraries = true
 }
 
 android_app_bundle("chrome_modern_public_bundle") {
@@ -1877,6 +1878,7 @@
     proguard_enabled = true
   }
   enable_language_splits = enable_chrome_language_splits
+  compress_shared_libraries = true
   if (modularize_vr) {
     extra_modules = [
       {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java
index 847bbbe..c9082077 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/BackgroundSyncLauncher.java
@@ -105,8 +105,7 @@
             protected void onPostExecute(Boolean shouldLaunch) {
                 callback.run(shouldLaunch);
             }
-        }
-                .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackgroundService.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackgroundService.java
index d34b694..47c1ebe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackgroundService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeBackgroundService.java
@@ -14,6 +14,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.library_loader.ProcessInitException;
+import org.chromium.chrome.browser.background_sync.BackgroundSyncBackgroundTaskScheduler;
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.init.ServiceManagerStartupUtils;
 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
@@ -70,6 +71,14 @@
     }
 
     private void handleBackgroundSyncEvent(Context context, String tag) {
+        if (ChromeFeatureList.isEnabled(
+                    ChromeFeatureList.BACKGROUND_TASK_SCHEDULER_FOR_BACKGROUND_SYNC)) {
+            // If BackgroundTaskScheduler has been used to schedule a background
+            // task for Background Sync, we simply reschedule the existing task(s).
+            BackgroundSyncBackgroundTaskScheduler.getInstance().reschedule();
+            return;
+        }
+
         if (!BackgroundSyncLauncher.hasInstance()) {
             // Start the browser. The browser's BackgroundSyncManager (for the active profile) will
             // start, check the network, and run any necessary sync events. This task runs with a
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
index 90ccb6b..7b8a2d70 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -168,6 +168,8 @@
     public static final String AUTOFILL_MANUAL_FALLBACK_ANDROID = "AutofillManualFallbackAndroid";
     public static final String AUTOFILL_REFRESH_STYLE_ANDROID = "AutofillRefreshStyleAndroid";
     public static final String AUTOFILL_KEYBOARD_ACCESSORY = "AutofillKeyboardAccessory";
+    public static final String BACKGROUND_TASK_SCHEDULER_FOR_BACKGROUND_SYNC =
+            "BackgroundTaskSchedulerForBackgroundSync";
     public static final String CAPTIVE_PORTAL_CERTIFICATE_LIST = "CaptivePortalCertificateList";
     public static final String CCT_BACKGROUND_TAB = "CCTBackgroundTab";
     public static final String CCT_MODULE = "CCTModule";
@@ -278,6 +280,7 @@
             "PredictivePrefetchingAllowedOnAllConnectionTypes";
     public static final String PROGRESS_BAR_THROTTLE = "ProgressBarThrottle";
     public static final String PWA_PERSISTENT_NOTIFICATION = "PwaPersistentNotification";
+    public static final String REACHED_CODE_PROFILER = "ReachedCodeProfiler";
     public static final String READER_MODE_IN_CCT = "ReaderModeInCCT";
     public static final String REMOVE_NAVIGATION_HISTORY = "RemoveNavigationHistory";
     public static final String SEARCH_READY_OMNIBOX = "SearchReadyOmnibox";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index e0af559..5845f69 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -7,7 +7,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import org.chromium.base.task.PostTask;
 import org.chromium.chrome.autofill_assistant.R;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.header.AssistantHeaderModel;
@@ -15,12 +14,10 @@
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayCoordinator;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayModel;
 import org.chromium.chrome.browser.autofill_assistant.overlay.AssistantOverlayState;
-import org.chromium.chrome.browser.autofill_assistant.payment.AssistantPaymentRequestModel;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.snackbar.Snackbar;
 import org.chromium.chrome.browser.snackbar.SnackbarManager;
-import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
 
 /**
@@ -29,16 +26,16 @@
  */
 class AssistantCoordinator {
     interface Delegate {
-        /**
-         * Completely stop the Autofill Assistant.
-         */
-        void stop();
+        /** Completely stop the Autofill Assistant. */
+        void stop(@DropOutReason int reason);
+
+        // TODO(crbug.com/806868): Move onboarding and snackbar out of this class and remove the
+        // delegate.
     }
 
     private static final String FEEDBACK_CATEGORY_TAG =
             "com.android.chrome.USER_INITIATED_FEEDBACK_REPORT_AUTOFILL_ASSISTANT";
     private static final int SNACKBAR_DELAY_MS = 5_000;
-    private static final int GRACEFUL_SHUTDOWN_DELAY_MS = 5_000;
 
     private final ChromeActivity mActivity;
     private final Delegate mDelegate;
@@ -50,8 +47,7 @@
     private final AssistantKeyboardCoordinator mKeyboardCoordinator;
     private final AssistantOverlayCoordinator mOverlayCoordinator;
 
-    private boolean mIsShuttingDownGracefully;
-    private boolean mIsDropOutRecorded;
+    private boolean mShowingSnackbar;
 
     AssistantCoordinator(ChromeActivity activity, WebContents webContents, Delegate delegate) {
         mActivity = activity;
@@ -85,51 +81,10 @@
         showAssistantView();
     }
 
-    /**
-     * Shut down the Autofill Assistant immediately, without showing a message.
-     */
-    public void shutdownImmediately(@DropOutReason int reason) {
-        if (!mIsDropOutRecorded) {
-            AutofillAssistantMetrics.recordDropOut(reason);
-            mIsDropOutRecorded = true;
-        }
+    /** Detaches and destroys the view. */
+    public void destroy() {
         detachAssistantView();
         mOverlayCoordinator.destroy();
-        mDelegate.stop();
-    }
-
-    /**
-     * Schedule the shut down of the Autofill Assistant such that any status message currently
-     * visible will still be shown for a few seconds before shutting down. Optionally replace
-     * the status message with a generic error message iff {@code showGiveUpMessage} is true.
-     */
-    // TODO(crbug.com/806868): Move this method to native.
-    public void gracefulShutdown(boolean showGiveUpMessage, @DropOutReason int reason) {
-        mIsShuttingDownGracefully = true;
-
-        // Make sure bottom bar is expanded.
-        mBottomBarCoordinator.expand();
-
-        // Hide everything except header.
-        mModel.getOverlayModel().set(AssistantOverlayModel.STATE, AssistantOverlayState.HIDDEN);
-        mModel.getDetailsModel().clearDetails();
-        mModel.getPaymentRequestModel().set(AssistantPaymentRequestModel.OPTIONS, null);
-        mModel.getCarouselModel().clearChips();
-
-        if (showGiveUpMessage) {
-            mModel.getHeaderModel().set(AssistantHeaderModel.STATUS_MESSAGE,
-                    mActivity.getString(R.string.autofill_assistant_give_up));
-        }
-        PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT,
-                () -> shutdownImmediately(reason), GRACEFUL_SHUTDOWN_DELAY_MS);
-    }
-
-    /**
-     * Shut down the Autofill Assistant and close the current Chrome tab.
-     */
-    public void close() {
-        shutdownImmediately(DropOutReason.CUSTOM_TAB_CLOSED);
-        mActivity.finish();
     }
 
     /**
@@ -151,7 +106,7 @@
                 .then(accepted -> {
                     mBottomBarCoordinator.allowSwipingBottomSheet(true);
                     if (!accepted) {
-                        shutdownImmediately(DropOutReason.DECLINED);
+                        mDelegate.stop(DropOutReason.DECLINED);
                         return;
                     }
 
@@ -186,8 +141,8 @@
      * Autofill assistant is shutting down.
      */
     public void dismissAndShowSnackbar(String message, @DropOutReason int reason) {
-        if (mIsShuttingDownGracefully) {
-            shutdownImmediately(reason);
+        if (mShowingSnackbar) {
+            mDelegate.stop(reason);
             return;
         }
 
@@ -199,18 +154,20 @@
                                     @Override
                                     public void onAction(Object actionData) {
                                         // Shutdown was cancelled.
+                                        mShowingSnackbar = false;
                                         showAssistantView();
                                     }
 
                                     @Override
                                     public void onDismissNoAction(Object actionData) {
-                                        shutdownImmediately(reason);
+                                        mDelegate.stop(reason);
                                     }
                                 },
                                 Snackbar.TYPE_ACTION, Snackbar.UMA_AUTOFILL_ASSISTANT_STOP_UNDO)
                         .setAction(mActivity.getString(R.string.undo), /* actionData= */ null);
         snackBar.setSingleLine(false);
         snackBar.setDuration(SNACKBAR_DELAY_MS);
+        mShowingSnackbar = true;
         mActivity.getSnackbarManager().showSnackbar(snackBar);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 6c15c5b5..d709d79 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -6,13 +6,16 @@
 
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.task.PostTask;
+import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.autofill_assistant.metrics.DropOutReason;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
-import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContents;
 
 /**
@@ -25,9 +28,13 @@
 // TODO(crbug.com/806868): This class should be removed once all logic is in native side and the
 // model is directly modified by the native AssistantMediator.
 class AutofillAssistantUiController implements AssistantCoordinator.Delegate {
+    private static final int GRACEFUL_SHUTDOWN_DELAY_MS = 5_000;
+
     private long mNativeUiController;
 
+    private final ChromeActivity mActivity;
     private final AssistantCoordinator mCoordinator;
+    private final ActivityTabProvider.ActivityTabTabObserver mActivityTabObserver;
 
     @CalledByNative
     private static AutofillAssistantUiController createAndStartUi(
@@ -39,54 +46,61 @@
     private AutofillAssistantUiController(
             ChromeActivity activity, WebContents webContents, long nativeUiController) {
         mNativeUiController = nativeUiController;
+        mActivity = activity;
         mCoordinator = new AssistantCoordinator(activity, webContents, this);
+        mActivityTabObserver =
+                new ActivityTabProvider.ActivityTabTabObserver(activity.getActivityTabProvider()) {
+                    @Override
+                    protected void onObservingDifferentTab(Tab tab) {
+                        // A null tab indicates that there's no selected tab; We're in the process
+                        // of selecting a new tab. TODO(crbug/925947): Hide AssistantCoordinator
+                        // instead of destroying it, in case the tab that's eventually selected also
+                        // has AutofillAssistant enabled or is the same tab.
+                        if (tab == null || tab.getWebContents() != webContents) {
+                            safeNativeDestroyUI();
+                        }
+                    }
 
-        initForCustomTab(activity);
+                    @Override
+                    public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
+                        if (!isAttached && tab.getWebContents() == webContents) {
+                            safeNativeDestroyUI();
+                        }
+                    }
+                };
+
+        initForCustomTab(activity, webContents);
     }
 
-    private void initForCustomTab(ChromeActivity activity) {
+    private void initForCustomTab(ChromeActivity activity, WebContents webContents) {
         if (!(activity instanceof CustomTabActivity)) {
             return;
         }
 
-        Tab activityTab = activity.getActivityTab();
-        activityTab.addObserver(new EmptyTabObserver() {
-            @Override
-            public void onActivityAttachmentChanged(Tab tab, boolean isAttached) {
-                if (!isAttached) {
-                    activityTab.removeObserver(this);
-                    mCoordinator.shutdownImmediately(DropOutReason.TAB_DETACHED);
-                }
-            }
-        });
-
         // Shut down Autofill Assistant when the selected tab (foreground tab) is changed.
         TabModel currentTabModel = activity.getTabModelSelector().getCurrentModel();
         currentTabModel.addObserver(new EmptyTabModelObserver() {
             @Override
             public void didSelectTab(Tab tab, int type, int lastId) {
                 // Shutdown the Autofill Assistant if the user switches to another tab.
-                if (!activityTab.equals(tab)) {
+                if (tab.getWebContents() != webContents) {
                     currentTabModel.removeObserver(this);
-                    mCoordinator.gracefulShutdown(
-                            /* showGiveUpMessage= */ true, DropOutReason.TAB_CHANGED);
+                    safeNativeOnFatalError(activity.getString(R.string.autofill_assistant_give_up),
+                            DropOutReason.TAB_CHANGED);
                 }
             }
         });
     }
 
-    /**
-     * Java => native methods.
-     */
+    // Java => native methods.
 
+    /** Shut down the Autofill Assistant immediately, without showing a message. */
     @Override
-    public void stop() {
-        safeNativeStop();
+    public void stop(@DropOutReason int reason) {
+        safeNativeStop(reason);
     }
 
-    /**
-     * Native => Java methods.
-     */
+    // Native => Java methods.
 
     // TODO(crbug.com/806868): Some of these functions still have a little bit of logic (e.g. make
     // the progress bar pulse when hiding overlay). Maybe it would be better to forward all calls to
@@ -104,19 +118,29 @@
         mNativeUiController = 0;
     }
 
+    /** Destroys this instance and {@link AssistantCoordinator}. */
     @CalledByNative
-    private void onShutdown(@DropOutReason int reason) {
-        mCoordinator.shutdownImmediately(reason);
+    private void destroy(boolean delayed) {
+        mActivityTabObserver.destroy();
+
+        if (delayed) {
+            // Give some time to the user to read any error message.
+            PostTask.postDelayedTask(
+                    UiThreadTaskTraits.DEFAULT, mCoordinator::destroy, GRACEFUL_SHUTDOWN_DELAY_MS);
+            return;
+        }
+        mCoordinator.destroy();
     }
 
+    /**
+     * Close CCT after the current task has finished running - usually after Autofill Assistant has
+     * finished shutting itself down.
+     */
     @CalledByNative
-    private void onShutdownGracefully(@DropOutReason int reason) {
-        mCoordinator.gracefulShutdown(/* showGiveUpMessage= */ false, reason);
-    }
-
-    @CalledByNative
-    private void onClose() {
-        mCoordinator.close();
+    private void scheduleCloseCustomTab() {
+        if (mActivity instanceof CustomTabActivity) {
+            PostTask.postTask(UiThreadTaskTraits.DEFAULT, mActivity::finish);
+        }
     }
 
     @CalledByNative
@@ -140,9 +164,19 @@
     }
 
     // Native methods.
-    void safeNativeStop() {
-        if (mNativeUiController != 0) nativeStop(mNativeUiController);
+    private void safeNativeStop(@DropOutReason int reason) {
+        if (mNativeUiController != 0) nativeStop(mNativeUiController, reason);
     }
-    private native void nativeStop(long nativeUiControllerAndroid);
+    private native void nativeStop(long nativeUiControllerAndroid, @DropOutReason int reason);
 
+    private void safeNativeDestroyUI() {
+        if (mNativeUiController != 0) nativeDestroyUI(mNativeUiController);
+    }
+    private native void nativeDestroyUI(long nativeUiControllerAndroid);
+
+    private void safeNativeOnFatalError(String message, @DropOutReason int reason) {
+        if (mNativeUiController != 0) nativeOnFatalError(mNativeUiController, message, reason);
+    }
+    private native void nativeOnFatalError(
+            long nativeUiControllerAndroid, String message, @DropOutReason int reason);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTask.java b/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTask.java
new file mode 100644
index 0000000..674d4ba
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTask.java
@@ -0,0 +1,76 @@
+// Copyright 2019 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.background_sync;
+
+import android.content.Context;
+
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask;
+import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback;
+import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerPrefs;
+import org.chromium.components.background_task_scheduler.TaskIds;
+import org.chromium.components.background_task_scheduler.TaskParameters;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Handles servicing of Background Sync background tasks coming via
+ * background_task_scheduler component.
+ */
+public class BackgroundSyncBackgroundTask extends NativeBackgroundTask {
+    @Override
+    public @StartBeforeNativeResult int onStartTaskBeforeNativeLoaded(
+            Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
+        assert taskParameters.getTaskId() == TaskIds.BACKGROUND_SYNC_ONE_SHOT_JOB_ID;
+
+        return StartBeforeNativeResult.LOAD_NATIVE;
+    }
+
+    @Override
+    protected void onStartTaskWithNative(
+            Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
+        // Record the delay from soonest expected wakeup time.
+        long delayFromExpectedMs = System.currentTimeMillis()
+                - taskParameters.getExtras().getLong(
+                        BackgroundSyncBackgroundTaskScheduler.SOONEST_EXPECTED_WAKETIME);
+        RecordHistogram.recordLongTimesHistogram(
+                "BackgroundSync.Wakeup.DelayTime", delayFromExpectedMs, TimeUnit.MILLISECONDS);
+
+        // Now that Chrome has been started, BackgroundSyncManager will
+        // eventually be created, and it'll fire any ready sync events.
+        // It'll also schedule a background task with the required delay.
+        // In case Chrome gets closed before native code gets to run,
+        // schedule a task to wake up Chrome with a delay, as a backup. This is
+        // done only if there isn't already a similar task scheduled. This'll
+        // be overwritten by a similar call from BackgroundSyncManager.
+        if (!BackgroundTaskSchedulerPrefs.getScheduledTasks().contains(
+                    BackgroundSyncBackgroundTask.class.getName())) {
+            BackgroundSyncBackgroundTaskScheduler.getInstance().scheduleOneShotTask();
+        }
+        callback.taskFinished(true);
+    }
+
+    @Override
+    protected boolean onStopTaskBeforeNativeLoaded(Context context, TaskParameters taskParameters) {
+        assert taskParameters.getTaskId() == TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID;
+
+        // Native didn't complete loading, but it was supposed to.
+        // Presume we need to reschedule.
+        return true;
+    }
+
+    @Override
+    protected boolean onStopTaskWithNative(Context context, TaskParameters taskParameters) {
+        assert taskParameters.getTaskId() == TaskIds.OFFLINE_PAGES_BACKGROUND_JOB_ID;
+
+        // Don't reschedule again.
+        return false;
+    }
+
+    @Override
+    public void reschedule(Context context) {
+        BackgroundSyncBackgroundTaskScheduler.getInstance().reschedule();
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskScheduler.java b/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskScheduler.java
new file mode 100644
index 0000000..75d2b4a
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskScheduler.java
@@ -0,0 +1,111 @@
+// Copyright 2019 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.background_sync;
+
+import android.os.Bundle;
+
+import org.chromium.base.ContextUtils;
+import org.chromium.base.VisibleForTesting;
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
+import org.chromium.components.background_task_scheduler.TaskIds;
+import org.chromium.components.background_task_scheduler.TaskInfo;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The {@link BackgroundSyncBackgroundTaskScheduler} singleton is responsible
+ * for scheduling and cancelling background tasks to wake Chrome up so that
+ * Background Sync events ready to be fired can be fired.
+ *
+ * Thread model: This class is to be run on the UI thread only.
+ */
+public class BackgroundSyncBackgroundTaskScheduler {
+    // Keep in sync with the default min_sync_recovery_time of
+    // BackgroundSyncParameters.
+    private static final long MIN_SYNC_RECOVERY_TIME = TimeUnit.MINUTES.toMillis(6);
+
+    // Bundle key for the timestamp of the soonest wakeup time expected for
+    // this task.
+    public static final String SOONEST_EXPECTED_WAKETIME = "SoonestWakeupTime";
+
+    private static class LazyHolder {
+        static final BackgroundSyncBackgroundTaskScheduler INSTANCE =
+                new BackgroundSyncBackgroundTaskScheduler();
+    }
+
+    @CalledByNative
+    public static BackgroundSyncBackgroundTaskScheduler getInstance() {
+        return LazyHolder.INSTANCE;
+    }
+
+    /**
+     * Cancels a task with Id BACKGROUND_SYNC_ONE_SHOT_JOB_ID, if there's one
+     * scheduled.
+     */
+    @VisibleForTesting
+    protected void cancelOneShotTask() {
+        BackgroundTaskSchedulerFactory.getScheduler().cancel(
+                ContextUtils.getApplicationContext(), TaskIds.BACKGROUND_SYNC_ONE_SHOT_JOB_ID);
+    }
+
+    /**
+     * Schedules a one-off bakground task to wake the browser up on network
+     * connectivity. There is a delay of of six minutes before waking the
+     * browser.
+     */
+    protected boolean scheduleOneShotTask() {
+        return scheduleOneShotTask(MIN_SYNC_RECOVERY_TIME);
+    }
+
+    /**
+     * Schedules a one-off background task to wake the browser up on network
+     * connectivity and call into native code to fire ready Background Sync
+     * events.
+     * @param minDelayMs The minimum time to wait before waking the browser.
+     */
+    protected boolean scheduleOneShotTask(long minDelayMs) {
+        // Pack SOONEST_EXPECTED_WAKETIME in extras.
+        Bundle taskExtras = new Bundle();
+        taskExtras.putLong(SOONEST_EXPECTED_WAKETIME, System.currentTimeMillis() + minDelayMs);
+
+        TaskInfo taskInfo = TaskInfo.createOneOffTask(TaskIds.BACKGROUND_SYNC_ONE_SHOT_JOB_ID,
+                                            BackgroundSyncBackgroundTask.class, minDelayMs)
+                                    .setRequiredNetworkType(TaskInfo.NetworkType.ANY)
+                                    .setUpdateCurrent(true)
+                                    .setIsPersisted(true)
+                                    .setExtras(taskExtras)
+                                    .build();
+        // This will overwrite any existing task with this ID.
+        return BackgroundTaskSchedulerFactory.getScheduler().schedule(
+                ContextUtils.getApplicationContext(), taskInfo);
+    }
+
+    /**
+     * Based on shouldLaunch, either creates or cancels a one-off background task
+     * to wake up Chrome upon network connectivity.
+     * @param shouldLaunch Whether to launch the browser in the background.
+     * @param minDelayMs The minimum time to wait before waking the browser.
+     */
+    @VisibleForTesting
+    @CalledByNative
+    protected void launchBrowserIfStopped(boolean shouldLaunch, long minDelayMs) {
+        if (!shouldLaunch) {
+            cancelOneShotTask();
+            return;
+        }
+
+        scheduleOneShotTask(minDelayMs);
+    }
+
+    /**
+     * Method for rescheduling a background task to wake up Chrome for processing
+     * one-shot Background Sync events in the event of an OS upgrade or
+     * Google Play Services upgrade.
+     */
+    public void reschedule() {
+        scheduleOneShotTask(MIN_SYNC_RECOVERY_TIME);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/background_sync/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/background_sync/OWNERS
new file mode 100644
index 0000000..59f4eacf
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/background_sync/OWNERS
@@ -0,0 +1,4 @@
+file://chrome/browser/background_sync/OWNERS
+
+# COMPONENT: Blink>BackgroundSync
+# TEAM: platform-capabilities@chromium.org
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index cb77242..213e4e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -240,6 +240,9 @@
         ChromeStrictMode.configureStrictMode();
         ChromeWebApkHost.init();
 
+        // Time this call takes in background from test devices:
+        // - Pixel 2: ~10 ms
+        // - Nokia 1 (Android Go): 20-200 ms
         warmUpSharedPrefs();
 
         DeviceUtils.addDeviceSpecificUserAgentSwitch();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
index 583ce3be..11f6916b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/util/FeatureUtilities.java
@@ -194,11 +194,13 @@
         cacheNightModeAvailable();
         cacheDownloadAutoResumptionEnabledInNative();
 
-        // Propagate DONT_PREFETCH_LIBRARIES feature value to LibraryLoader. This can't
-        // be done in LibraryLoader itself because it lives in //base and can't depend
-        // on ChromeFeatureList.
+        // Propagate DONT_PREFETCH_LIBRARIES and REACHED_CODE_PROFILER feature values to
+        // LibraryLoader. This can't be done in LibraryLoader itself because it lives in //base and
+        // can't depend on ChromeFeatureList.
         LibraryLoader.setDontPrefetchLibrariesOnNextRuns(
                 ChromeFeatureList.isEnabled(ChromeFeatureList.DONT_PREFETCH_LIBRARIES));
+        LibraryLoader.setReachedCodeProfilerEnabledOnNextRuns(
+                ChromeFeatureList.isEnabled(ChromeFeatureList.REACHED_CODE_PROFILER));
     }
 
     /**
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index b7ed9bbd..c085517 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -171,6 +171,8 @@
   "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AutofillAssistantPaymentRequest.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/payment/PaymentRequestBottomBar.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/payment/PaymentRequestUI.java",
+  "java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskScheduler.java",
+  "java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTask.java",
   "java/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTask.java",
   "java/src/org/chromium/chrome/browser/banners/AppBannerManager.java",
   "java/src/org/chromium/chrome/browser/banners/AppBannerUiDelegateAndroid.java",
@@ -1898,6 +1900,7 @@
   "javatests/src/org/chromium/chrome/browser/PowerBroadcastReceiverTest.java",
   "javatests/src/org/chromium/chrome/browser/PrerenderTest.java",
   "javatests/src/org/chromium/chrome/browser/ProcessIsolationTest.java",
+  "javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java",
   "javatests/src/org/chromium/chrome/browser/RestoreHistogramTest.java",
   "javatests/src/org/chromium/chrome/browser/SafeBrowsingTest.java",
   "javatests/src/org/chromium/chrome/browser/SelectFileDialogTest.java",
@@ -2395,6 +2398,7 @@
   "junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryTabLayoutControllerTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingControllerTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetControllerTest.java",
+  "junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskSchedulerTest.java",
   "junit/src/org/chromium/chrome/browser/background_task_scheduler/NativeBackgroundTaskTest.java",
   "junit/src/org/chromium/chrome/browser/browseractions/BrowserActionsIntentTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ClearDataDialogResultRecorderTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java
index ba68340..47f4b599 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/BackgroundSyncLauncherTest.java
@@ -53,11 +53,10 @@
         // Use a semaphore to wait for the callback to be called.
         final Semaphore semaphore = new Semaphore(0);
 
-        BackgroundSyncLauncher.ShouldLaunchCallback callback =
-                shouldLaunch -> {
-                    mShouldLaunchResult = shouldLaunch;
-                    semaphore.release();
-                };
+        BackgroundSyncLauncher.ShouldLaunchCallback callback = shouldLaunch -> {
+            mShouldLaunchResult = shouldLaunch;
+            semaphore.release();
+        };
 
         BackgroundSyncLauncher.shouldLaunchBrowserIfStopped(callback);
         // Wait on the callback to be called.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeBackgroundServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeBackgroundServiceTest.java
index c412172..c7e9d78 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeBackgroundServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ChromeBackgroundServiceTest.java
@@ -23,6 +23,8 @@
 import org.chromium.chrome.browser.ntp.snippets.SnippetsLauncher;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 
+import java.util.HashMap;
+
 /**
  * Tests {@link ChromeBackgroundService}.
  */
@@ -78,9 +80,13 @@
 
     @Before
     public void setUp() throws Exception {
+        HashMap<String, Boolean> features = new HashMap<String, Boolean>();
+        features.put(ChromeFeatureList.BACKGROUND_TASK_SCHEDULER_FOR_BACKGROUND_SYNC, false);
+        ChromeFeatureList.setTestFeatures(features);
         BackgroundSyncLauncher.setGCMEnabled(false);
-        RecordHistogram.setDisabledForTests(true);
         mSyncLauncher = BackgroundSyncLauncher.create();
+
+        RecordHistogram.setDisabledForTests(true);
         mSnippetsLauncher = SnippetsLauncher.create();
         mTaskService = new MockTaskService();
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java
new file mode 100644
index 0000000..fa025940
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ReachedCodeProfilerTest.java
@@ -0,0 +1,118 @@
+// Copyright 2019 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;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.BaseSwitches;
+import org.chromium.base.StrictModeContext;
+import org.chromium.base.test.ReachedCodeProfiler;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.chrome.browser.preferences.ChromePreferenceManager;
+import org.chromium.chrome.test.ChromeActivityTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
+import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+
+/**
+ * Tests for the reached code profiler feature setup.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public final class ReachedCodeProfilerTest {
+    @Rule
+    public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
+            new ChromeActivityTestRule<>(ChromeActivity.class);
+
+    // Shared preferences key for the reached code profiler.
+    private static final String REACHED_CODE_PROFILER_ENABLED_KEY = "reached_code_profiler_enabled";
+
+    /**
+     * Tests that passing a command line switch enables the reached code profiler no matter what.
+     */
+    @Test
+    @SmallTest
+    @DisableFeatures(ChromeFeatureList.REACHED_CODE_PROFILER)
+    @CommandLineFlags.Add(BaseSwitches.ENABLE_REACHED_CODE_PROFILER)
+    public void testExplicitlyEnableViaCommandLineSwitch() throws Exception {
+        mActivityTestRule.startMainActivityFromLauncher();
+        assertReachedCodeProfilerIsEnabled();
+    }
+
+    /**
+     * Tests that setting a shared preference enables the reached code profiler. This test imitates
+     * the second launch after enabling the feature
+     */
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.REACHED_CODE_PROFILER)
+    public void testEnabledViaCachedSharedPreference() throws Exception {
+        setReachedCodeProfilerSharedPreference(true);
+        mActivityTestRule.startMainActivityFromLauncher();
+        assertReachedCodeProfilerIsEnabled();
+    }
+
+    /**
+     * Tests that the feature state is cached in shared preferences after native initialization.
+     * This test imitates the first run when the feature is enabled.
+     */
+    @Test
+    @SmallTest
+    @EnableFeatures(ChromeFeatureList.REACHED_CODE_PROFILER)
+    public void testSharedPreferenceIsCached_Enable() throws Exception {
+        mActivityTestRule.startMainActivityFromLauncher();
+
+        Assert.assertTrue(getReachedCodeProfilerSharedPreference());
+        // Enabling takes effect only on the second startup.
+        Assert.assertFalse(ReachedCodeProfiler.isEnabled());
+    }
+
+    /**
+     * Tests that the feature state is cached in shared preferences after native initialization.
+     * This test imitates disabling the reached code profiler after it has been enabled for some
+     * time.
+     */
+    @Test
+    @SmallTest
+    @DisableFeatures(ChromeFeatureList.REACHED_CODE_PROFILER)
+    public void testSharedPreferenceIsCached_Disable() throws Exception {
+        setReachedCodeProfilerSharedPreference(true);
+        mActivityTestRule.startMainActivityFromLauncher();
+
+        Assert.assertFalse(getReachedCodeProfilerSharedPreference());
+        // Disabling takes effect only on the second startup.
+        assertReachedCodeProfilerIsEnabled();
+    }
+
+    /**
+     * The reached code profiler is always disabled in some configurations. This helper allows to
+     * check if the profiler is enabled in supported configurations.
+     */
+    private void assertReachedCodeProfilerIsEnabled() {
+        if (!ReachedCodeProfiler.isSupported()) {
+            Assert.assertFalse(ReachedCodeProfiler.isEnabled());
+            return;
+        }
+
+        Assert.assertTrue(ReachedCodeProfiler.isEnabled());
+    }
+
+    private boolean getReachedCodeProfilerSharedPreference() {
+        try (StrictModeContext unused = StrictModeContext.allowDiskReads()) {
+            return ChromePreferenceManager.getInstance().readBoolean(
+                    REACHED_CODE_PROFILER_ENABLED_KEY, false);
+        }
+    }
+
+    private void setReachedCodeProfilerSharedPreference(boolean value) {
+        ChromePreferenceManager.getInstance().writeBoolean(
+                REACHED_CODE_PROFILER_ENABLED_KEY, value);
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmptyUpdateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmptyUpdateTest.java
index 0054b34..13699b5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmptyUpdateTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestEmptyUpdateTest.java
@@ -57,7 +57,6 @@
         mPaymentRequestTestRule.clickInShippingAddressAndWait(
                 R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
         mPaymentRequestTestRule.clickInShippingAddressAndWait(
-                R.id.payments_first_radio_button, mPaymentRequestTestRule.getDismissed());
-        mPaymentRequestTestRule.expectResultContains(new String[] {"Total required"});
+                R.id.payments_first_radio_button, mPaymentRequestTestRule.getReadyForInput());
     }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java
index 5551c7d..e68c657 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java
@@ -53,13 +53,31 @@
     }
 
     /**
-     * A merchant that calls updateWith() without shipping options will not cause timeouts in UI.
+     * A merchant that calls updateWith() with {} will not cause timeouts in UI.
      */
     @Test
     @MediumTest
     @Feature({"Payments"})
-    public void testUpdateWithNoShippingOptions() throws Throwable {
-        mRule.triggerUIAndWait("updateWithNoShippingOptions", mRule.getReadyForInput());
+    public void testUpdateWithEmpty() throws Throwable {
+        mRule.triggerUIAndWait("updateWithEmpty", mRule.getReadyForInput());
+        Assert.assertEquals("USD $5.00", mRule.getOrderSummaryTotal());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
+        Assert.assertEquals("USD $5.00", mRule.getOrderSummaryTotal());
+        mRule.clickAndWait(R.id.button_primary, mRule.getReadyForUnmaskInput());
+        mRule.setTextInCardUnmaskDialogAndWait(
+                R.id.card_unmask_input, "123", mRule.getReadyToUnmask());
+        mRule.clickCardUnmaskButtonAndWait(
+                ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"freeShipping"});
+    }
+
+    /** A merchant that calls updateWith() with total will not cause timeouts in UI. */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUpdateWithTotal() throws Throwable {
+        mRule.triggerUIAndWait("updateWithTotal", mRule.getReadyForInput());
         Assert.assertEquals("USD $5.00", mRule.getOrderSummaryTotal());
         mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
         mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
@@ -81,7 +99,7 @@
         Assert.assertEquals("USD $5.00", mRule.getOrderSummaryTotal());
         mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
         mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
-        Assert.assertEquals("USD $10.00", mRule.getOrderSummaryTotal());
+        Assert.assertEquals("USD $5.00", mRule.getOrderSummaryTotal());
         mRule.clickAndWait(R.id.button_primary, mRule.getReadyForUnmaskInput());
         mRule.setTextInCardUnmaskDialogAndWait(
                 R.id.card_unmask_input, "123", mRule.getReadyToUnmask());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskSchedulerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskSchedulerTest.java
new file mode 100644
index 0000000..c0804bd
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskSchedulerTest.java
@@ -0,0 +1,151 @@
+// Copyright 2019 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.background_sync;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.test.support.DisableHistogramsRule;
+import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
+import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
+import org.chromium.components.background_task_scheduler.TaskIds;
+import org.chromium.components.background_task_scheduler.TaskInfo;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+/** Unit tests for BackgroundSyncBackgroundTaskScheduler. */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class BackgroundSyncBackgroundTaskSchedulerTest {
+    @Rule
+    public DisableHistogramsRule mDisableHistogramsRule = new DisableHistogramsRule();
+
+    @Mock
+    private BackgroundTaskScheduler mTaskScheduler;
+    @Captor
+    ArgumentCaptor<TaskInfo> mTaskInfo;
+
+    private static final long ONE_DAY_IN_MILLISECONDS = TimeUnit.DAYS.toMillis(1);
+    private static final long ONE_WEEK_IN_MILLISECONDS = TimeUnit.DAYS.toMillis(7);
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        BackgroundTaskSchedulerFactory.setSchedulerForTesting(mTaskScheduler);
+        HashMap<String, Boolean> features = new HashMap<String, Boolean>();
+        features.put(ChromeFeatureList.BACKGROUND_TASK_SCHEDULER_FOR_BACKGROUND_SYNC, true);
+        ChromeFeatureList.setTestFeatures(features);
+        doReturn(true)
+                .when(mTaskScheduler)
+                .schedule(eq(RuntimeEnvironment.application), mTaskInfo.capture());
+    }
+
+    private void verifyFixedTaskInfoValues(TaskInfo info) {
+        assertEquals(TaskIds.BACKGROUND_SYNC_ONE_SHOT_JOB_ID, info.getTaskId());
+        assertEquals(BackgroundSyncBackgroundTask.class, info.getBackgroundTaskClass());
+        assertTrue(info.isPersisted());
+        assertFalse(info.isPeriodic());
+        assertEquals(TaskInfo.NetworkType.ANY, info.getRequiredNetworkType());
+
+        long expectedSoonestDelayTime = info.getExtras().getLong(
+                BackgroundSyncBackgroundTaskScheduler.SOONEST_EXPECTED_WAKETIME);
+        assertTrue(expectedSoonestDelayTime > 0L);
+    }
+
+    @Test
+    @Feature({"BackgroundSync"})
+    public void testLaunchBrowserIfStopped() {
+        BackgroundSyncBackgroundTaskScheduler.getInstance().launchBrowserIfStopped(
+                /* shouldLaunch= */ true,
+                /* minDelayMs= */ ONE_DAY_IN_MILLISECONDS);
+        verify(mTaskScheduler, times(1))
+                .schedule(eq(RuntimeEnvironment.application), eq(mTaskInfo.getValue()));
+
+        TaskInfo taskInfo = mTaskInfo.getValue();
+        verifyFixedTaskInfoValues(taskInfo);
+
+        assertEquals(ONE_DAY_IN_MILLISECONDS, taskInfo.getOneOffInfo().getWindowEndTimeMs());
+    }
+
+    @Test
+    @Feature({"BackgroundSync"})
+    public void testCancelOneShotTask() {
+        BackgroundSyncBackgroundTaskScheduler.getInstance().launchBrowserIfStopped(
+                /* shouldLaunch= */ true,
+                /* minDelayMs= */ ONE_DAY_IN_MILLISECONDS);
+        verify(mTaskScheduler, times(1))
+                .schedule(eq(RuntimeEnvironment.application), eq(mTaskInfo.getValue()));
+
+        doNothing()
+                .when(mTaskScheduler)
+                .cancel(eq(RuntimeEnvironment.application),
+                        eq(TaskIds.BACKGROUND_SYNC_ONE_SHOT_JOB_ID));
+
+        BackgroundSyncBackgroundTaskScheduler.getInstance().cancelOneShotTask();
+        verify(mTaskScheduler, times(1))
+                .cancel(eq(RuntimeEnvironment.application),
+                        eq(TaskIds.BACKGROUND_SYNC_ONE_SHOT_JOB_ID));
+    }
+
+    @Test
+    @Feature({"BackgroundSync"})
+    public void testLaunchBrowserCalledTwice() {
+        BackgroundSyncBackgroundTaskScheduler.getInstance().launchBrowserIfStopped(
+                /* shouldLaunch= */ true,
+                /* minDelayMs= */ ONE_DAY_IN_MILLISECONDS);
+        verify(mTaskScheduler, times(1))
+                .schedule(eq(RuntimeEnvironment.application), eq(mTaskInfo.getValue()));
+
+        TaskInfo taskInfo = mTaskInfo.getValue();
+        assertEquals(ONE_DAY_IN_MILLISECONDS, taskInfo.getOneOffInfo().getWindowEndTimeMs());
+
+        BackgroundSyncBackgroundTaskScheduler.getInstance().launchBrowserIfStopped(
+                /* shouldLaunch= */ true,
+                /* minDelayMs= */ ONE_WEEK_IN_MILLISECONDS);
+        verify(mTaskScheduler, times(1))
+                .schedule(eq(RuntimeEnvironment.application), eq(mTaskInfo.getValue()));
+
+        taskInfo = mTaskInfo.getValue();
+        assertEquals(ONE_WEEK_IN_MILLISECONDS, taskInfo.getOneOffInfo().getWindowEndTimeMs());
+    }
+
+    @Test
+    @Feature({"BackgroundSync"})
+    public void testLaunchBrowserThenCancel() {
+        BackgroundSyncBackgroundTaskScheduler.getInstance().launchBrowserIfStopped(
+                /* shouldLaunch= */ true,
+                /* minDelayMs= */ ONE_DAY_IN_MILLISECONDS);
+        BackgroundSyncBackgroundTaskScheduler.getInstance().launchBrowserIfStopped(
+                /* shouldLaunch= */ false,
+                /* minDelayMs= */ ONE_DAY_IN_MILLISECONDS);
+
+        verify(mTaskScheduler, times(1))
+                .schedule(eq(RuntimeEnvironment.application), eq(mTaskInfo.getValue()));
+        verify(mTaskScheduler, times(1))
+                .cancel(eq(RuntimeEnvironment.application),
+                        eq(TaskIds.BACKGROUND_SYNC_ONE_SHOT_JOB_ID));
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index f497227..c091c37 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-74.0.3698.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-74.0.3699.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/app/app_management_strings.grdp b/chrome/app/app_management_strings.grdp
index 58e69e2..70d2c1ec 100644
--- a/chrome/app/app_management_strings.grdp
+++ b/chrome/app/app_management_strings.grdp
@@ -31,6 +31,9 @@
   <message name="IDS_APP_MANAGEMENT_MORE_APPS" desc="Label for more apps button at bottom of app list card.">
     Show <ph name="NUMBER_OF_MORE_APPS">$1<ex>4</ex></ph> more apps
   </message>
+  <message name="IDS_APP_MANAGEMENT_NO_RESULTS" desc="Text description of a search that has no results.">
+    No search results found
+  </message>
   <message name="IDS_APP_MANAGEMENT_NOTIFICATIONS" desc="Label for notifications section in the app settings page.">
     Notifications
   </message>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index bffa23b..dda0ea7 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1050,6 +1050,8 @@
     "payments/payment_handler_permission_context.cc",
     "payments/payment_handler_permission_context.h",
     "payments/ssl_validity_checker.cc",
+    "performance_manager/chrome_content_browser_client_performance_manager_part.cc",
+    "performance_manager/chrome_content_browser_client_performance_manager_part.h",
     "performance_manager/frame_resource_coordinator.cc",
     "performance_manager/frame_resource_coordinator.h",
     "performance_manager/graph/frame_node_impl.cc",
@@ -1083,6 +1085,8 @@
     "performance_manager/performance_manager.h",
     "performance_manager/process_resource_coordinator.cc",
     "performance_manager/process_resource_coordinator.h",
+    "performance_manager/render_process_user_data.cc",
+    "performance_manager/render_process_user_data.h",
     "performance_manager/resource_coordinator_clock.cc",
     "performance_manager/resource_coordinator_clock.h",
     "performance_manager/resource_coordinator_interface.h",
@@ -1406,8 +1410,6 @@
     "resource_coordinator/browser_child_process_watcher.h",
     "resource_coordinator/chrome_browser_main_extra_parts_resource_coordinator.cc",
     "resource_coordinator/chrome_browser_main_extra_parts_resource_coordinator.h",
-    "resource_coordinator/chrome_content_browser_client_resource_coordinator_part.cc",
-    "resource_coordinator/chrome_content_browser_client_resource_coordinator_part.h",
     "resource_coordinator/discard_before_unload_helper.cc",
     "resource_coordinator/discard_before_unload_helper.h",
     "resource_coordinator/exponential_moving_average.cc",
@@ -1418,8 +1420,6 @@
     "resource_coordinator/performance_measurement_manager.h",
     "resource_coordinator/render_process_probe.cc",
     "resource_coordinator/render_process_probe.h",
-    "resource_coordinator/render_process_user_data.cc",
-    "resource_coordinator/render_process_user_data.h",
     "resource_coordinator/resource_coordinator_parts.cc",
     "resource_coordinator/resource_coordinator_parts.h",
     "resource_coordinator/session_restore_policy.cc",
@@ -4877,6 +4877,7 @@
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java",
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java",
       "../android/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java",
+      "../android/java/src/org/chromium/chrome/browser/background_sync/BackgroundSyncBackgroundTaskScheduler.java",
       "../android/java/src/org/chromium/chrome/browser/banners/AppBannerManager.java",
       "../android/java/src/org/chromium/chrome/browser/banners/AppBannerUiDelegateAndroid.java",
       "../android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java",
diff --git a/chrome/browser/android/autofill_assistant/client_android.cc b/chrome/browser/android/autofill_assistant/client_android.cc
index 1ba4a81..17a534e 100644
--- a/chrome/browser/android/autofill_assistant/client_android.cc
+++ b/chrome/browser/android/autofill_assistant/client_android.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/android/autofill_assistant/client_android.h"
 
+#include <utility>
+#include <vector>
+
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
@@ -11,6 +14,7 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/no_destructor.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/android/chrome_feature_list.h"
 #include "chrome/browser/autofill/android/personal_data_manager_android.h"
@@ -47,10 +51,6 @@
     &autofill_assistant::features::kAutofillAssistant, "url",
     "https://automate-pa.googleapis.com"};
 
-// Time between two attempts to destroy the controller.
-static constexpr base::TimeDelta kDestroyRetryInterval =
-    base::TimeDelta::FromSeconds(2);
-
 // Fills a map from two Java arrays of strings of the same length.
 void FillParametersFromJava(JNIEnv* env,
                             const JavaRef<jobjectArray>& names,
@@ -80,7 +80,6 @@
 
 ClientAndroid::ClientAndroid(content::WebContents* web_contents)
     : web_contents_(web_contents),
-      // TODO: consider creating the java objects when needed.
       java_object_(Java_AutofillAssistantClient_create(
           AttachCurrentThread(),
           reinterpret_cast<intptr_t>(this))),
@@ -101,7 +100,8 @@
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
     const JavaParamRef<jobject>& on_accept) {
-  GetUiController()->ShowOnboarding(env, on_accept);
+  ShowUI();
+  ui_controller_android_->ShowOnboarding(env, on_accept);
 }
 
 base::android::ScopedJavaLocalRef<jobject> ClientAndroid::GetJavaObject() {
@@ -140,6 +140,26 @@
   }
 }
 
+void ClientAndroid::ShowUI() {
+  if (!controller_) {
+    CreateController();
+    // TODO(crbug.com/806868): allow delaying controller creation, for
+    // onboarding.
+  }
+  if (!ui_controller_android_) {
+    ui_controller_android_ = std::make_unique<UiControllerAndroid>(
+        web_contents_, /* client= */ this, controller_.get());
+  }
+}
+
+void ClientAndroid::DestroyUI() {
+  if (!ui_controller_android_)
+    return;
+
+  ui_controller_android_->Destroy();
+  ui_controller_android_.reset();
+}
+
 std::string ClientAndroid::GetApiKey() {
   std::string api_key;
   if (google_apis::IsGoogleChromeAPIKeyUsed()) {
@@ -174,16 +194,12 @@
   return kAutofillAssistantServerUrl.Get();
 }
 
-UiControllerAndroid* ClientAndroid::GetUiController() {
-  if (!controller_) {
-    CreateController();
-  }
+UiController* ClientAndroid::GetUiController() {
+  if (ui_controller_android_)
+    return ui_controller_android_.get();
 
-  if (!ui_controller_android_ && controller_) {
-    ui_controller_android_ = std::make_unique<UiControllerAndroid>(
-        web_contents_, /* client= */ this, controller_.get());
-  }
-  return ui_controller_android_.get();
+  static base::NoDestructor<UiController> noop_controller_;
+  return noop_controller_.get();
 }
 
 std::string ClientAndroid::GetLocale() {
@@ -196,20 +212,18 @@
                                                   java_object_));
 }
 
-void ClientAndroid::Stop() {
+void ClientAndroid::Shutdown(Metrics::DropOutReason reason) {
   if (!controller_)
     return;
 
-  if (!controller_->Terminate()) {
-    // This is a safety net and should be removed once all uses of
-    // base::Unretained in the execution and script tracking has been removed.
-    base::PostDelayedTaskWithTraits(
-        FROM_HERE, {content::BrowserThread::UI},
-        base::BindOnce(&ClientAndroid::Stop, weak_ptr_factory_.GetWeakPtr()),
-        kDestroyRetryInterval);
+  if (!controller_->Terminate(reason)) {
+    // Controller is responsible for calling Shutdown(reason) again once it's
+    // done.
     return;
   }
   ui_controller_android_.reset();
+
+  Metrics::RecordDropOut(reason);
   controller_.reset();
 }
 
diff --git a/chrome/browser/android/autofill_assistant/client_android.h b/chrome/browser/android/autofill_assistant/client_android.h
index 6e3f1494..454c0a2 100644
--- a/chrome/browser/android/autofill_assistant/client_android.h
+++ b/chrome/browser/android/autofill_assistant/client_android.h
@@ -57,15 +57,17 @@
                      const base::android::JavaParamRef<jstring>& access_token);
 
   // Overrides Client
+  void ShowUI() override;
+  void DestroyUI() override;
   std::string GetApiKey() override;
   std::string GetAccountEmailAddress() override;
   AccessTokenFetcher* GetAccessTokenFetcher() override;
   autofill::PersonalDataManager* GetPersonalDataManager() override;
   std::string GetServerUrl() override;
-  UiControllerAndroid* GetUiController() override;
+  UiController* GetUiController() override;
   std::string GetLocale() override;
   std::string GetCountryCode() override;
-  void Stop() override;
+  void Shutdown(Metrics::DropOutReason reason) override;
 
   // Overrides AccessTokenFetcher
   void FetchAccessToken(
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index e879bd2..0e9498f 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -81,6 +81,21 @@
   // Register payment_request_delegate_ as delegate for the payment request UI.
   Java_AssistantPaymentRequestModel_setDelegate(
       env, GetPaymentRequestModel(), payment_request_delegate_.GetJavaObject());
+
+  if (ui_delegate->GetState() != AutofillAssistantState::INACTIVE) {
+    // The UI was created for an existing Controller.
+    OnStatusMessageChanged(ui_delegate->GetStatusMessage());
+    OnProgressChanged(ui_delegate->GetProgress());
+    OnDetailsChanged(ui_delegate->GetDetails());
+    OnChipsChanged(ui_delegate->GetChips());
+    OnPaymentRequestChanged(ui_delegate->GetPaymentRequestOptions());
+
+    std::vector<RectF> area;
+    ui_delegate->GetTouchableArea(&area);
+    OnTouchableAreaChanged(area);
+
+    OnStateChanged(ui_delegate->GetState());
+  }
 }
 
 UiControllerAndroid::~UiControllerAndroid() {
@@ -106,12 +121,14 @@
       SetOverlayState(OverlayState::FULL);
       AllowShowingSoftKeyboard(false);
       SetProgressPulsingEnabled(false);
+      SetAllowSwipingSheet(true);
       return;
 
     case AutofillAssistantState::RUNNING:
       SetOverlayState(OverlayState::FULL);
       AllowShowingSoftKeyboard(false);
       SetProgressPulsingEnabled(false);
+      SetAllowSwipingSheet(true);
       return;
 
     case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
@@ -128,6 +145,7 @@
       AllowShowingSoftKeyboard(true);
       SetProgressPulsingEnabled(true);
 
+      SetAllowSwipingSheet(ui_delegate_->GetPaymentRequestOptions() == nullptr);
       // user interaction is needed.
       ExpandBottomSheet();
       return;
@@ -136,16 +154,17 @@
       SetOverlayState(OverlayState::FULL);
       AllowShowingSoftKeyboard(true);
       SetProgressPulsingEnabled(false);
+      SetAllowSwipingSheet(true);
       return;
 
     case AutofillAssistantState::STOPPED:
       SetOverlayState(OverlayState::HIDDEN);
       AllowShowingSoftKeyboard(true);
       SetProgressPulsingEnabled(false);
+      SetAllowSwipingSheet(true);
 
       // make sure user sees the error message.
       ExpandBottomSheet();
-      ShutdownGracefully();
       return;
 
     case AutofillAssistantState::INACTIVE:
@@ -182,18 +201,16 @@
       AttachCurrentThread(), java_autofill_assistant_ui_controller_);
 }
 
-void UiControllerAndroid::ShutdownGracefully() {
-  DCHECK(ui_delegate_->GetDropOutReason() != Metrics::AA_START);
-  Java_AutofillAssistantUiController_onShutdownGracefully(
-      AttachCurrentThread(), java_autofill_assistant_ui_controller_,
-      ui_delegate_->GetDropOutReason());
-}
-
 void UiControllerAndroid::SetProgressPulsingEnabled(bool enabled) {
   Java_AssistantHeaderModel_setProgressPulsingEnabled(
       AttachCurrentThread(), GetHeaderModel(), enabled);
 }
 
+void UiControllerAndroid::SetAllowSwipingSheet(bool allow) {
+  Java_AssistantModel_setAllowSwipingSheet(AttachCurrentThread(), GetModel(),
+                                           allow);
+}
+
 void UiControllerAndroid::OnFeedbackButtonClicked() {
   JNIEnv* env = AttachCurrentThread();
   Java_AutofillAssistantUiController_showFeedback(
@@ -213,7 +230,10 @@
 }
 
 std::string UiControllerAndroid::GetDebugContext() {
-  return ui_delegate_->GetDebugContext();
+  if (captured_debug_context_.empty() && ui_delegate_) {
+    return ui_delegate_->GetDebugContext();
+  }
+  return captured_debug_context_;
 }
 
 // Carousel related methods.
@@ -245,7 +265,8 @@
 }
 
 void UiControllerAndroid::OnChipSelected(int index) {
-  ui_delegate_->SelectChip(index);
+  if (ui_delegate_)
+    ui_delegate_->SelectChip(index);
 }
 
 // Overlay related methods.
@@ -284,11 +305,13 @@
 }
 
 void UiControllerAndroid::UpdateTouchableArea() {
-  ui_delegate_->UpdateTouchableArea();
+  if (ui_delegate_)
+    ui_delegate_->UpdateTouchableArea();
 }
 
 void UiControllerAndroid::OnUserInteractionInsideTouchableArea() {
-  ui_delegate_->OnUserInteractionInsideTouchableArea();
+  if (ui_delegate_)
+    ui_delegate_->OnUserInteractionInsideTouchableArea();
 }
 
 // Other methods.
@@ -300,14 +323,27 @@
       env, java_autofill_assistant_ui_controller_, on_accept);
 }
 
-void UiControllerAndroid::Shutdown(Metrics::DropOutReason reason) {
-  Java_AutofillAssistantUiController_onShutdown(
-      AttachCurrentThread(), java_autofill_assistant_ui_controller_, reason);
+void UiControllerAndroid::Destroy() {
+  Java_AutofillAssistantUiController_destroy(
+      AttachCurrentThread(), java_autofill_assistant_ui_controller_,
+      /* delayed= */ false);
 }
 
-void UiControllerAndroid::Close() {
-  Java_AutofillAssistantUiController_onClose(
-      AttachCurrentThread(), java_autofill_assistant_ui_controller_);
+void UiControllerAndroid::WillShutdown(Metrics::DropOutReason reason) {
+  JNIEnv* env = AttachCurrentThread();
+  Java_AutofillAssistantUiController_destroy(
+      env, java_autofill_assistant_ui_controller_,
+      /* delayed= */ ui_delegate_->GetState() ==
+          AutofillAssistantState::STOPPED);
+  if (reason == Metrics::CUSTOM_TAB_CLOSED) {
+    Java_AutofillAssistantUiController_scheduleCloseCustomTab(
+        env, java_autofill_assistant_ui_controller_);
+  }
+
+  // Capture the debug context, for including into a feedback possibly sent
+  // later, after the delegate has been destroyed.
+  captured_debug_context_ = ui_delegate_->GetDebugContext();
+  ui_delegate_ = nullptr;
 }
 
 // Payment request related methods.
@@ -320,30 +356,26 @@
 
 void UiControllerAndroid::OnGetPaymentInformation(
     std::unique_ptr<PaymentInformation> payment_info) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_AssistantModel_setAllowSwipingSheet(env, GetModel(), true);
-  Java_AssistantPaymentRequestModel_clearOptions(env, GetPaymentRequestModel());
-  if (get_payment_information_callback_) {
-    std::move(get_payment_information_callback_).Run(std::move(payment_info));
-  }
+  ui_delegate_->SetPaymentInformation(std::move(payment_info));
 }
 
-void UiControllerAndroid::GetPaymentInformation(
-    payments::mojom::PaymentOptionsPtr payment_options,
-    base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
-    const std::vector<std::string>& supported_basic_card_networks) {
-  DCHECK(!get_payment_information_callback_);
-  get_payment_information_callback_ = std::move(callback);
+void UiControllerAndroid::OnPaymentRequestChanged(
+    const PaymentRequestOptions* payment_options) {
   JNIEnv* env = AttachCurrentThread();
-  Java_AssistantModel_setAllowSwipingSheet(env, GetModel(), false);
+  auto jmodel = GetPaymentRequestModel();
+  if (!payment_options) {
+    Java_AssistantPaymentRequestModel_clearOptions(env, jmodel);
+    return;
+  }
   Java_AssistantPaymentRequestModel_setOptions(
-      env, GetPaymentRequestModel(),
+      env, jmodel,
       base::android::ConvertUTF8ToJavaString(env,
                                              client_->GetAccountEmailAddress()),
       payment_options->request_shipping, payment_options->request_payer_name,
       payment_options->request_payer_phone,
       payment_options->request_payer_email,
-      base::android::ToJavaArrayOfStrings(env, supported_basic_card_networks));
+      base::android::ToJavaArrayOfStrings(
+          env, payment_options->supported_basic_card_networks));
 }
 
 // Details related method.
@@ -377,9 +409,27 @@
   Java_AssistantDetailsModel_setDetails(env, jmodel, jdetails);
 }
 
-void UiControllerAndroid::Stop(
+void UiControllerAndroid::Stop(JNIEnv* env,
+                               const base::android::JavaParamRef<jobject>& obj,
+                               int jreason) {
+  client_->Shutdown(static_cast<Metrics::DropOutReason>(jreason));
+}
+
+void UiControllerAndroid::DestroyUI(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj) {
-  client_->Stop();
+  client_->DestroyUI();
+}
+
+void UiControllerAndroid::OnFatalError(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& obj,
+    const base::android::JavaParamRef<jstring>& jmessage,
+    int jreason) {
+  if (!ui_delegate_)
+    return;
+  ui_delegate_->OnFatalError(
+      base::android::ConvertJavaStringToUTF8(env, jmessage),
+      static_cast<Metrics::DropOutReason>(jreason));
 }
 }  // namespace autofill_assistant.
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index 3fbf46f..6643350 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -29,8 +29,11 @@
 // changes to the UI model.
 class UiControllerAndroid : public UiController {
  public:
-  // pointers to |web_contents|, |client| and |ui_delegate| must remain valid
-  // for the lifetime of this instance.
+  // pointers to |web_contents|, |client| must remain valid for the lifetime of
+  // this instance.
+  //
+  // Pointer to |ui_delegate| must remain valid for the lifetime of this
+  // instance or until WillShutdown is called.
   UiControllerAndroid(content::WebContents* web_contents,
                       Client* client,
                       UiDelegate* ui_delegate);
@@ -39,17 +42,14 @@
   // Called by ClientAndroid.
   void ShowOnboarding(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& on_accept);
+  void Destroy();
 
   // Overrides UiController:
   void OnStateChanged(AutofillAssistantState new_state) override;
   void OnStatusMessageChanged(const std::string& message) override;
-  void Shutdown(Metrics::DropOutReason reason) override;
-  void Close() override;
+  void WillShutdown(Metrics::DropOutReason reason) override;
   void OnChipsChanged(const std::vector<Chip>& chips) override;
-  void GetPaymentInformation(
-      payments::mojom::PaymentOptionsPtr payment_options,
-      base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
-      const std::vector<std::string>& supported_basic_card_networks) override;
+  void OnPaymentRequestChanged(const PaymentRequestOptions* options) override;
   void OnDetailsChanged(const Details* details) override;
   void OnProgressChanged(int progress) override;
   void OnTouchableAreaChanged(const std::vector<RectF>& areas) override;
@@ -71,14 +71,24 @@
   void OnChipSelected(int index);
 
   // Called by Java.
-  void Stop(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
+  void Stop(JNIEnv* env,
+            const base::android::JavaParamRef<jobject>& obj,
+            int reason);
+  void DestroyUI(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
+  void OnFatalError(JNIEnv* env,
+                    const base::android::JavaParamRef<jobject>& obj,
+                    const base::android::JavaParamRef<jstring>& message,
+                    int reason);
   base::android::ScopedJavaLocalRef<jstring> GetPrimaryAccountName(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller);
 
  private:
   Client* const client_;
-  UiDelegate* const ui_delegate_;
+
+  // A pointer to the Autofill Assistant Controller. It can become nullptr after
+  // WillShutdown() has been called.
+  UiDelegate* ui_delegate_;
   AssistantOverlayDelegate overlay_delegate_;
   AssistantHeaderDelegate header_delegate_;
   AssistantPaymentRequestDelegate payment_request_delegate_;
@@ -94,17 +104,18 @@
   void SetOverlayState(OverlayState state);
   void AllowShowingSoftKeyboard(bool enabled);
   void ExpandBottomSheet();
-  void ShutdownGracefully();
   void SetProgressPulsingEnabled(bool enabled);
+  void SetAllowSwipingSheet(bool allow);
   std::string GetDebugContext();
 
+  // Debug context captured previously. If non-empty, GetDebugContext() returns
+  // this context.
+  std::string captured_debug_context_;
+
   // Java-side AutofillAssistantUiController object.
   base::android::ScopedJavaGlobalRef<jobject>
       java_autofill_assistant_ui_controller_;
 
-  base::OnceCallback<void(std::unique_ptr<PaymentInformation>)>
-      get_payment_information_callback_;
-
   base::WeakPtrFactory<UiControllerAndroid> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(UiControllerAndroid);
diff --git a/chrome/browser/android/background_sync_launcher_android.cc b/chrome/browser/android/background_sync_launcher_android.cc
index e5f58674..7b182c6 100644
--- a/chrome/browser/android/background_sync_launcher_android.cc
+++ b/chrome/browser/android/background_sync_launcher_android.cc
@@ -4,7 +4,10 @@
 
 #include "chrome/browser/android/background_sync_launcher_android.h"
 
+#include "base/feature_list.h"
+#include "chrome/browser/android/chrome_feature_list.h"
 #include "content/public/browser/browser_thread.h"
+#include "jni/BackgroundSyncBackgroundTaskScheduler_jni.h"
 #include "jni/BackgroundSyncLauncher_jni.h"
 
 using content::BrowserThread;
@@ -42,8 +45,18 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_BackgroundSyncLauncher_launchBrowserIfStopped(
-      env, java_launcher_, launch_when_next_online, min_delay_ms);
+
+  if (!base::FeatureList::IsEnabled(
+          chrome::android::kBackgroundTaskSchedulerForBackgroundSync)) {
+    Java_BackgroundSyncLauncher_launchBrowserIfStopped(
+        env, java_gcm_network_manager_launcher_, launch_when_next_online,
+        min_delay_ms);
+    return;
+  }
+
+  Java_BackgroundSyncBackgroundTaskScheduler_launchBrowserIfStopped(
+      env, java_background_sync_background_task_scheduler_launcher_,
+      launch_when_next_online, min_delay_ms);
 }
 
 // static
@@ -67,12 +80,26 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   JNIEnv* env = base::android::AttachCurrentThread();
-  java_launcher_.Reset(Java_BackgroundSyncLauncher_create(env));
+
+  if (!base::FeatureList::IsEnabled(
+          chrome::android::kBackgroundTaskSchedulerForBackgroundSync)) {
+    java_gcm_network_manager_launcher_.Reset(
+        Java_BackgroundSyncLauncher_create(env));
+    return;
+  }
+
+  java_background_sync_background_task_scheduler_launcher_.Reset(
+      Java_BackgroundSyncBackgroundTaskScheduler_getInstance(env));
 }
 
 BackgroundSyncLauncherAndroid::~BackgroundSyncLauncherAndroid() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
+  if (base::FeatureList::IsEnabled(
+          chrome::android::kBackgroundTaskSchedulerForBackgroundSync)) {
+    return;
+  }
+
   JNIEnv* env = base::android::AttachCurrentThread();
-  Java_BackgroundSyncLauncher_destroy(env, java_launcher_);
+  Java_BackgroundSyncLauncher_destroy(env, java_gcm_network_manager_launcher_);
 }
diff --git a/chrome/browser/android/background_sync_launcher_android.h b/chrome/browser/android/background_sync_launcher_android.h
index be547a8..0c93da7 100644
--- a/chrome/browser/android/background_sync_launcher_android.h
+++ b/chrome/browser/android/background_sync_launcher_android.h
@@ -40,7 +40,10 @@
   void LaunchBrowserIfStoppedImpl(bool launch_when_next_online,
                                   int64_t min_delay_ms);
 
-  base::android::ScopedJavaGlobalRef<jobject> java_launcher_;
+  base::android::ScopedJavaGlobalRef<jobject>
+      java_gcm_network_manager_launcher_;
+  base::android::ScopedJavaGlobalRef<jobject>
+      java_background_sync_background_task_scheduler_launcher_;
   DISALLOW_COPY_AND_ASSIGN(BackgroundSyncLauncherAndroid);
 };
 
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 722011e6..25f1dc8 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -92,6 +92,7 @@
     &kAndroidPayIntegrationV2,
     &kAndroidPaymentApps,
     &kAndroidSiteSettingsUIRefresh,
+    &kBackgroundTaskSchedulerForBackgroundSync,
     &kCastDeviceFilter,
     &kCCTBackgroundTab,
     &kCCTExternalLinkHandling,
@@ -145,6 +146,7 @@
     &kProgressBarThrottleFeature,
     &kPwaImprovedSplashScreen,
     &kPwaPersistentNotification,
+    &kReachedCodeProfiler,
     &kReaderModeInCCT,
     &kSearchReadyOmniboxFeature,
     &kSearchEnginePromoExistingDevice,
@@ -229,6 +231,10 @@
 const base::Feature kBackgroundTaskComponentUpdate{
     "BackgroundTaskComponentUpdate", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kBackgroundTaskSchedulerForBackgroundSync{
+    "BackgroundTaskSchedulerForBackgroundSync",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Used in downstream code.
 const base::Feature kCastDeviceFilter{"CastDeviceFilter",
                                       base::FEATURE_DISABLED_BY_DEFAULT};
@@ -405,6 +411,9 @@
 const base::Feature kPwaPersistentNotification{
     "PwaPersistentNotification", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kReachedCodeProfiler{"ReachedCodeProfiler",
+                                         base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kReaderModeInCCT{"ReaderModeInCCT",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index c7997f1..7521a2f 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -20,6 +20,7 @@
 extern const base::Feature kAndroidPaymentApps;
 extern const base::Feature kAndroidSiteSettingsUIRefresh;
 extern const base::Feature kBackgroundTaskComponentUpdate;
+extern const base::Feature kBackgroundTaskSchedulerForBackgroundSync;
 extern const base::Feature kCastDeviceFilter;
 extern const base::Feature kCCTBackgroundTab;
 extern const base::Feature kCCTExternalLinkHandling;
@@ -77,6 +78,7 @@
 extern const base::Feature kProgressBarThrottleFeature;
 extern const base::Feature kPwaImprovedSplashScreen;
 extern const base::Feature kPwaPersistentNotification;
+extern const base::Feature kReachedCodeProfiler;
 extern const base::Feature kReaderModeInCCT;
 extern const base::Feature kSearchReadyOmniboxFeature;
 extern const base::Feature kServiceManagerForDownload;
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
index 03be34cb..38a7b842 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
@@ -24,12 +24,12 @@
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/translate/translate_service.h"
 #include "chrome/common/pref_names.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/language/core/browser/language_model.h"
 #include "components/language/core/browser/language_model_manager.h"
 #include "components/prefs/pref_service.h"
 #include "components/search_engines/template_url_service.h"
 #include "components/signin/core/browser/account_consistency_method.h"
+#include "components/sync/driver/sync_service.h"
 #include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "components/variations/variations_associated_data.h"
diff --git a/chrome/browser/android/password_ui_view_android.cc b/chrome/browser/android/password_ui_view_android.cc
index 9f15db6..330dd26 100644
--- a/chrome/browser/android/password_ui_view_android.cc
+++ b/chrome/browser/android/password_ui_view_android.cc
@@ -24,10 +24,8 @@
 #include "base/task/post_task.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/password_manager/core/browser/export/password_csv_writer.h"
 #include "components/password_manager/core/browser/password_ui_utils.h"
 #include "components/password_manager/core/browser/ui/credential_provider_interface.h"
diff --git a/chrome/browser/android/signin/signin_manager_android_unittest.cc b/chrome/browser/android/signin/signin_manager_android_unittest.cc
index 9ccf491..859a3bf 100644
--- a/chrome/browser/android/signin/signin_manager_android_unittest.cc
+++ b/chrome/browser/android/signin/signin_manager_android_unittest.cc
@@ -104,7 +104,10 @@
   DISALLOW_COPY_AND_ASSIGN(SigninManagerAndroidTest);
 };
 
-TEST_F(SigninManagerAndroidTest, DeleteGoogleServiceWorkerCaches) {
+// TODO(crbug.com/929456): This test does not actually test anything; the
+// CannedBrowsingDataCacheStorageHelper isn't hooked up to observe any
+// deletions. Disabled to allow refactoring of browsing data code.
+TEST_F(SigninManagerAndroidTest, DISABLED_DeleteGoogleServiceWorkerCaches) {
   struct TestCase {
     std::string worker_url;
     bool should_be_deleted;
@@ -128,6 +131,8 @@
       {"https://google.com:8444/worker.html", true},
   };
 
+  // TODO(crbug.com/929456): This helper is not attached anywhere to
+  // be able to observe deletions.
   // Add service workers.
   scoped_refptr<CannedBrowsingDataCacheStorageHelper> helper(
       new CannedBrowsingDataCacheStorageHelper(
@@ -151,6 +156,7 @@
   for (const auto& info : helper->GetCacheStorageUsageInfo())
     remaining_cache_storages.insert(info.origin.spec());
 
+  // TODO(crbug.com/929456): If deleted, the key should not be present.
   for (const TestCase& test_case : kTestCases) {
     EXPECT_EQ(test_case.should_be_deleted,
               base::ContainsKey(remaining_cache_storages, test_case.worker_url))
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index aebeaaf..99f6ae10 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -92,7 +92,6 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/handoff/handoff_manager.h"
 #include "components/handoff/handoff_utility.h"
 #include "components/keep_alive_registry/keep_alive_types.h"
diff --git a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
index ae42a24..c58a964 100644
--- a/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
+++ b/chrome/browser/autocomplete/chrome_autocomplete_provider_client.cc
@@ -32,13 +32,13 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/omnibox_pedal_provider.h"
 #include "components/prefs/pref_service.h"
+#include "components/sync/driver/sync_service.h"
 #include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 45ead6a4..fb8b659 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -263,6 +263,8 @@
         <include name="IDR_APP_MANAGEMENT_REDUCERS_JS" file="resources\app_management\reducers.js" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_ROUTER_HTML" file="resources\app_management\router.html" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_ROUTER_JS" file="resources\app_management\router.js" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_SEARCH_VIEW_HTML" file="resources\app_management\search_view.html" type="BINDATA" />
+        <include name="IDR_APP_MANAGEMENT_SEARCH_VIEW_JS" file="resources\app_management\search_view.js" type="BINDATA" />
         <include name="IDR_APP_MANAGEMENT_SHARED_STYLE_HTML" file="resources\app_management\shared_style.html" type="BINDATA"/>
         <include name="IDR_APP_MANAGEMENT_SHARED_VARS_HTML" file="resources\app_management\shared_vars.html" type="BINDATA"/>
         <include name="IDR_APP_MANAGEMENT_STORE_CLIENT_HTML" file="resources\app_management\store_client.html" type="BINDATA" />
diff --git a/chrome/browser/browsing_data/counters/hosted_apps_counter.cc b/chrome/browser/browsing_data/counters/hosted_apps_counter.cc
index 485218b..9d44257 100644
--- a/chrome/browser/browsing_data/counters/hosted_apps_counter.cc
+++ b/chrome/browser/browsing_data/counters/hosted_apps_counter.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/browsing_data/core/pref_names.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 
 HostedAppsCounter::HostedAppsCounter(Profile* profile)
@@ -30,8 +31,12 @@
           ->GenerateInstalledExtensionsSet();
 
   for (const auto& extension : *extensions) {
-    if (extension->is_hosted_app())
+    // Exclude kChromeAppId because this is not a proper hosted app. It is just
+    // a shortcut to launch Chrome on Chrome OS.
+    if (extension->is_hosted_app() &&
+        extension->id() != extension_misc::kChromeAppId) {
       names.push_back(extension->short_name());
+    }
   }
 
   count = names.size();
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2aa0fdb..d60fc85 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -75,6 +75,7 @@
 #include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/payments/payment_request_display_manager_factory.h"
+#include "chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.h"
 #include "chrome/browser/permissions/attestation_permission_request.h"
 #include "chrome/browser/permissions/permission_context_base.h"
 #include "chrome/browser/permissions/permission_request_manager.h"
@@ -107,7 +108,6 @@
 #include "chrome/browser/renderer_preferences_util.h"
 #include "chrome/browser/resource_coordinator/background_tab_navigation_throttle.h"
 #include "chrome/browser/resource_coordinator/chrome_browser_main_extra_parts_resource_coordinator.h"
-#include "chrome/browser/resource_coordinator/chrome_content_browser_client_resource_coordinator_part.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service.h"
 #include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
@@ -1075,7 +1075,7 @@
   extra_parts_.push_back(new ChromeContentBrowserClientExtensionsPart);
 #endif
 
-  extra_parts_.push_back(new ChromeContentBrowserClientResourceCoordinatorPart);
+  extra_parts_.push_back(new ChromeContentBrowserClientPerformanceManagerPart);
 
   gpu_binder_registry_.AddInterface(
       base::Bind(&metrics::CallStackProfileCollector::Create));
diff --git a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_unittest_chromeos.cc b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_unittest_chromeos.cc
index cf1037e..829a87d 100644
--- a/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_unittest_chromeos.cc
+++ b/chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_unittest_chromeos.cc
@@ -168,7 +168,7 @@
     // environments associated with |profile_| and |secondary_profile_|.
     IdentityTestEnvironmentProfileAdaptor identity_test_env_adaptor(
         profile.get());
-    AccountInfo account_info =
+    CoreAccountInfo account_info =
         identity_test_env_adaptor.identity_test_env()->SetPrimaryAccount(email);
 
     *gaia_id = account_info.gaia;
diff --git a/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc b/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
index e85703b..6dde028 100644
--- a/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
+++ b/chrome/browser/chromeos/printing/synced_printers_manager_factory.cc
@@ -13,8 +13,6 @@
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/model_type_store_service_factory.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/sync/model/model_type_store_service.h"
 #include "content/public/browser/browser_context.h"
diff --git a/chrome/browser/consent_auditor/consent_auditor_factory.cc b/chrome/browser/consent_auditor/consent_auditor_factory.cc
index e06af54..dbe26123 100644
--- a/chrome/browser/consent_auditor/consent_auditor_factory.cc
+++ b/chrome/browser/consent_auditor/consent_auditor_factory.cc
@@ -14,7 +14,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sync/model_type_store_service_factory.h"
 #include "chrome/common/channel_info.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/consent_auditor/consent_auditor_impl.h"
 #include "components/consent_auditor/consent_sync_bridge.h"
 #include "components/consent_auditor/consent_sync_bridge_impl.h"
diff --git a/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc b/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
index 6145a859..297a7cf8 100644
--- a/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
+++ b/chrome/browser/extensions/api/identity/identity_get_accounts_function.cc
@@ -47,15 +47,21 @@
 
   auto* identity_manager = IdentityManagerFactory::GetForProfile(profile);
   api::identity::AccountInfo account_info;
-  if (primary_account_only) {
-    // If extensions are restricted to the primary account, only return the
-    // account info when there is a valid primary account.
-    if (identity_manager->HasPrimaryAccountWithRefreshToken()) {
-      account_info.id = identity_manager->GetPrimaryAccountInfo().gaia;
-      infos->Append(account_info.ToValue());
-    }
-  } else {
+
+  // Ensure that the primary account is inserted first; even though this
+  // semantics isn't documented, the implementation has always ensured it and it
+  // shouldn't be changed without determining that it is safe to do so.
+  if (identity_manager->HasPrimaryAccountWithRefreshToken()) {
+    account_info.id = identity_manager->GetPrimaryAccountInfo().gaia;
+    infos->Append(account_info.ToValue());
+  }
+
+  // If secondary accounts are supported, add all the secondary accounts as
+  // well.
+  if (!primary_account_only) {
     for (const auto& account : accounts) {
+      if (account.account_id == identity_manager->GetPrimaryAccountId())
+        continue;
       account_info.id = account.gaia;
       infos->Append(account_info.ToValue());
     }
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc
index 3c42990d..5ac9e7e 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc
@@ -36,11 +36,10 @@
 
 api::passwords_private::PasswordUiEntry CreateEntry(int id) {
   api::passwords_private::PasswordUiEntry entry;
-  entry.login_pair.urls.shown = "test" + std::to_string(id) + ".com";
-  entry.login_pair.urls.origin =
-      "http://" + entry.login_pair.urls.shown + "/login";
-  entry.login_pair.urls.link = entry.login_pair.urls.origin;
-  entry.login_pair.username = "testName" + std::to_string(id);
+  entry.urls.shown = "test" + std::to_string(id) + ".com";
+  entry.urls.origin = "http://" + entry.urls.shown + "/login";
+  entry.urls.link = entry.urls.origin;
+  entry.username = "testName" + std::to_string(id);
   entry.num_characters_in_password = kNumCharactersInPassword;
   entry.id = id;
   return entry;
@@ -101,7 +100,7 @@
 
     // PasswordUiEntry does not contain a password. Thus we are only updating
     // the username and the length of the password.
-    current_entries_[id].login_pair.username = base::UTF16ToUTF8(username);
+    current_entries_[id].username = base::UTF16ToUTF8(username);
     if (password)
       current_entries_[id].num_characters_in_password = password->size();
     SendSavedPasswordsList();
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
index c2455e1..0d620192 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc
@@ -220,8 +220,8 @@
 
   for (const auto& form : password_list) {
     api::passwords_private::PasswordUiEntry entry;
-    entry.login_pair.urls = CreateUrlCollectionFromForm(*form);
-    entry.login_pair.username = base::UTF16ToUTF8(form->username_value);
+    entry.urls = CreateUrlCollectionFromForm(*form);
+    entry.username = base::UTF16ToUTF8(form->username_value);
     entry.id = password_id_generator_.GenerateId(
         password_manager::CreateSortKey(*form));
     entry.num_characters_in_password = form->password_value.length();
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
index 29297d2..84d25a7 100644
--- a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
+++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl_unittest.cc
@@ -237,7 +237,7 @@
         got_passwords = true;
         ASSERT_EQ(1u, password_list.size());
         EXPECT_EQ(sample_form.username_value,
-                  base::UTF8ToUTF16(password_list[0].login_pair.username));
+                  base::UTF8ToUTF16(password_list[0].username));
         EXPECT_EQ(sample_form.password_value.size(),
                   size_t{password_list[0].num_characters_in_password});
       }));
@@ -259,7 +259,7 @@
         got_passwords = true;
         ASSERT_EQ(1u, password_list.size());
         EXPECT_EQ(base::ASCIIToUTF16("new_user"),
-                  base::UTF8ToUTF16(password_list[0].login_pair.username));
+                  base::UTF8ToUTF16(password_list[0].username));
         EXPECT_EQ(base::ASCIIToUTF16("new_pass").size(),
                   size_t{password_list[0].num_characters_in_password});
       }));
diff --git a/chrome/browser/extensions/api/web_request/web_request_apitest.cc b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
index 5d15a1b..997dac7 100644
--- a/chrome/browser/extensions/api/web_request/web_request_apitest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_apitest.cc
@@ -554,6 +554,14 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest,
+                       WebRequestRedirectsWithExtraHeaders) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  ASSERT_TRUE(RunExtensionSubtestWithArg("webrequest", "test_redirects.html",
+                                         "useExtraHeaders"))
+      << message_;
+}
+
 // Tests that redirects from secure to insecure don't send the referrer header.
 IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest,
                        WebRequestRedirectsToInsecure) {
@@ -586,6 +594,14 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest,
+                       WebRequestSubresourceRedirectsWithExtraHeaders) {
+  ASSERT_TRUE(StartEmbeddedTestServer());
+  ASSERT_TRUE(RunExtensionSubtestWithArg(
+      "webrequest", "test_subresource_redirects.html", "useExtraHeaders"))
+      << message_;
+}
+
 // Fails often on Windows dbg bots. http://crbug.com/177163
 #if defined(OS_WIN)
 #define MAYBE_WebRequestNewTab DISABLED_WebRequestNewTab
diff --git a/chrome/browser/extensions/external_provider_impl_chromeos_unittest.cc b/chrome/browser/extensions/external_provider_impl_chromeos_unittest.cc
index dd4afb2..056ad2f 100644
--- a/chrome/browser/extensions/external_provider_impl_chromeos_unittest.cc
+++ b/chrome/browser/extensions/external_provider_impl_chromeos_unittest.cc
@@ -21,7 +21,6 @@
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -29,7 +28,6 @@
 #include "chromeos/system/fake_statistics_provider.h"
 #include "chromeos/system/statistics_provider.h"
 #include "components/browser_sync/browser_sync_switches.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/sync/base/pref_names.h"
 #include "components/sync/model/fake_sync_change_processor.h"
 #include "components/sync/model/sync_change_processor.h"
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 3b28f4a..b52d3479 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1170,8 +1170,10 @@
   },
   {
     "name": "enable-experimental-web-platform-features",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
+    "owners": [ "//third_party/blink/renderer/core/OWNERS" ],
+    // Used by developers to activate experimental features in blink.
+    // See //third_party/blink/renderer/platform/runtime_enabled_features.json5.
+    "expiry_milestone": -1
   },
   {
     "name": "enable-first-run-ui-transitions",
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index a728cff..21761f6 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -59,7 +59,6 @@
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/installer/util/util_constants.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/browser_watcher/stability_paths.h"
 #include "components/crash/core/common/crash_keys.h"
 #include "components/history/core/browser/history_service.h"
@@ -86,6 +85,7 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 #include "components/sync/device_info/device_count_metrics_provider.h"
+#include "components/sync/driver/sync_service.h"
 #include "components/ukm/ukm_service.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
index d05c2cf..f56ee3f 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter_browsertest.cc
@@ -744,12 +744,9 @@
   CheckPageInfoUkmMetrics(url, true);
 }
 
-#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
-#define MAYBE_FetchThreeTimes DISABLED_FetchThreeTimes
-#else
-#define MAYBE_FetchThreeTimes FetchThreeTimes
-#endif
-IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest, MAYBE_FetchThreeTimes) {
+// Flaky test: https://crbug.com/731466
+IN_PROC_BROWSER_TEST_F(ProcessMemoryMetricsEmitterTest,
+                       DISABLED_FetchThreeTimes) {
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL url = embedded_test_server()->GetURL("foo.com", "/empty.html");
   ui_test_utils::NavigateToURLWithDisposition(
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index d0d1e5a..b27e48f 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -34,6 +34,7 @@
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cors_exempt_headers.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/shared_cors_origin_access_list.h"
 #include "content/public/browser/storage_partition.h"
diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc
index e1cc01d..4bdc1ca 100644
--- a/chrome/browser/net/system_network_context_manager.cc
+++ b/chrome/browser/net/system_network_context_manager.cc
@@ -40,10 +40,12 @@
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "components/variations/net/variations_http_headers.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cors_exempt_headers.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
@@ -575,6 +577,8 @@
 SystemNetworkContextManager::CreateDefaultNetworkContextParams() {
   network::mojom::NetworkContextParamsPtr network_context_params =
       network::mojom::NetworkContextParams::New();
+  content::UpdateCorsExemptHeader(network_context_params.get());
+  variations::UpdateCorsExemptHeaderForVariations(network_context_params.get());
 
   network_context_params->enable_brotli =
       base::FeatureList::IsEnabled(features::kBrotliEncoding);
diff --git a/chrome/browser/password_manager/account_chooser_dialog_android.cc b/chrome/browser/password_manager/account_chooser_dialog_android.cc
index 9fcf08b..8ea7da9 100644
--- a/chrome/browser/password_manager/account_chooser_dialog_android.cc
+++ b/chrome/browser/password_manager/account_chooser_dialog_android.cc
@@ -13,11 +13,9 @@
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/credential_android.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/passwords/account_avatar_fetcher.h"
 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/password_manager/core/browser/password_bubble_experiment.h"
 #include "components/password_manager/core/browser/password_manager_constants.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
diff --git a/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.cc b/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.cc
new file mode 100644
index 0000000..67d78edc
--- /dev/null
+++ b/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.cc
@@ -0,0 +1,49 @@
+// Copyright 2019 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.
+
+#include "chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "chrome/browser/performance_manager/render_process_user_data.h"
+
+namespace {
+
+void BindProcessPerformanceManager(
+    content::RenderProcessHost* render_process_host,
+    resource_coordinator::mojom::ProcessCoordinationUnitRequest request) {
+  performance_manager::RenderProcessUserData* user_data =
+      performance_manager::RenderProcessUserData::GetForRenderProcessHost(
+          render_process_host);
+
+  user_data->process_resource_coordinator()->AddBinding(std::move(request));
+}
+
+}  // namespace
+
+ChromeContentBrowserClientPerformanceManagerPart::
+    ChromeContentBrowserClientPerformanceManagerPart() = default;
+ChromeContentBrowserClientPerformanceManagerPart::
+    ~ChromeContentBrowserClientPerformanceManagerPart() = default;
+
+void ChromeContentBrowserClientPerformanceManagerPart::
+    ExposeInterfacesToRenderer(
+        service_manager::BinderRegistry* registry,
+        blink::AssociatedInterfaceRegistry* associated_registry,
+        content::RenderProcessHost* render_process_host) {
+  registry->AddInterface(
+      base::BindRepeating(&BindProcessPerformanceManager,
+                          base::Unretained(render_process_host)),
+      base::SequencedTaskRunnerHandle::Get());
+
+  // When a RenderFrameHost is "resurrected" with a new process  it will already
+  // have user data attached. This will happen on renderer crash.
+  if (!performance_manager::RenderProcessUserData::GetForRenderProcessHost(
+          render_process_host)) {
+    performance_manager::RenderProcessUserData::CreateForRenderProcessHost(
+        render_process_host);
+  }
+}
diff --git a/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.h b/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.h
new file mode 100644
index 0000000..8db693f
--- /dev/null
+++ b/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.h
@@ -0,0 +1,30 @@
+// Copyright 2019 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.
+
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_CHROME_CONTENT_BROWSER_CLIENT_PERFORMANCE_MANAGER_PART_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_CHROME_CONTENT_BROWSER_CLIENT_PERFORMANCE_MANAGER_PART_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "chrome/browser/chrome_content_browser_client_parts.h"
+
+// Allows tracking RenderProcessHost lifetime and proffering the Performance
+// Manager interface to new renderers.
+class ChromeContentBrowserClientPerformanceManagerPart
+    : public ChromeContentBrowserClientParts {
+ public:
+  ChromeContentBrowserClientPerformanceManagerPart();
+  ~ChromeContentBrowserClientPerformanceManagerPart() override;
+
+  // ChromeContentBrowserClientParts overrides.
+  void ExposeInterfacesToRenderer(
+      service_manager::BinderRegistry* registry,
+      blink::AssociatedInterfaceRegistry* associated_registry,
+      content::RenderProcessHost* render_process_host) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ChromeContentBrowserClientPerformanceManagerPart);
+};
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_CHROME_CONTENT_BROWSER_CLIENT_PERFORMANCE_MANAGER_PART_H_
diff --git a/chrome/browser/resource_coordinator/render_process_user_data.cc b/chrome/browser/performance_manager/render_process_user_data.cc
similarity index 94%
rename from chrome/browser/resource_coordinator/render_process_user_data.cc
rename to chrome/browser/performance_manager/render_process_user_data.cc
index 9f877e99..4d13be4 100644
--- a/chrome/browser/resource_coordinator/render_process_user_data.cc
+++ b/chrome/browser/performance_manager/render_process_user_data.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/resource_coordinator/render_process_user_data.h"
+#include "chrome/browser/performance_manager/render_process_user_data.h"
 
 #include <memory>
 #include <utility>
@@ -16,7 +16,7 @@
 #include "content/public/common/content_switches.h"
 #include "services/service_manager/public/cpp/connector.h"
 
-namespace resource_coordinator {
+namespace performance_manager {
 
 namespace {
 
@@ -76,4 +76,4 @@
       host->GetUserData(kRenderProcessUserDataKey));
 }
 
-}  // namespace resource_coordinator
+}  // namespace performance_manager
diff --git a/chrome/browser/resource_coordinator/render_process_user_data.h b/chrome/browser/performance_manager/render_process_user_data.h
similarity index 79%
rename from chrome/browser/resource_coordinator/render_process_user_data.h
rename to chrome/browser/performance_manager/render_process_user_data.h
index a89e96e..5e46ba2 100644
--- a/chrome/browser/resource_coordinator/render_process_user_data.h
+++ b/chrome/browser/performance_manager/render_process_user_data.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_RENDER_PROCESS_USER_DATA_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_RENDER_PROCESS_USER_DATA_H_
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_RENDER_PROCESS_USER_DATA_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_RENDER_PROCESS_USER_DATA_H_
 
 #include "base/macros.h"
 #include "base/supports_user_data.h"
@@ -15,7 +15,7 @@
 
 }  // namespace content
 
-namespace resource_coordinator {
+namespace performance_manager {
 
 // Attached to RenderProcessHost as user data, associates the RenderProcessHost
 // with the Resource Coordinator process node.
@@ -39,6 +39,6 @@
   DISALLOW_COPY_AND_ASSIGN(RenderProcessUserData);
 };
 
-}  // namespace resource_coordinator
+}  // namespace performance_manager
 
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_RENDER_PROCESS_USER_DATA_H_
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_RENDER_PROCESS_USER_DATA_H_
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service.cc b/chrome/browser/policy/cloud/user_policy_signin_service.cc
index 02f69a6..e111707 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service.cc
@@ -129,7 +129,7 @@
 }
 
 void UserPolicySigninService::OnPrimaryAccountSet(
-    const AccountInfo& account_info) {
+    const CoreAccountInfo& account_info) {
   if (!identity_manager()->HasAccountWithRefreshToken(account_info.account_id))
     return;
 
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service.h b/chrome/browser/policy/cloud/user_policy_signin_service.h
index 55301ef6..26b1765 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service.h
+++ b/chrome/browser/policy/cloud/user_policy_signin_service.h
@@ -61,7 +61,7 @@
 
   // identity::IdentityManager::Observer implementation:
   // UserPolicySigninServiceBase is already an observer of IdentityManager.
-  void OnPrimaryAccountSet(const AccountInfo& account_info) override;
+  void OnPrimaryAccountSet(const CoreAccountInfo& account_info) override;
   void OnRefreshTokenUpdatedForAccount(
       const AccountInfo& account_info) override;
 
diff --git a/chrome/browser/profiles/gaia_info_update_service.cc b/chrome/browser/profiles/gaia_info_update_service.cc
index ded21ec..ba759ef7 100644
--- a/chrome/browser/profiles/gaia_info_update_service.cc
+++ b/chrome/browser/profiles/gaia_info_update_service.cc
@@ -215,7 +215,7 @@
 }
 
 void GAIAInfoUpdateService::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   OnUsernameChanged(primary_account_info.gaia);
 }
 
diff --git a/chrome/browser/profiles/gaia_info_update_service.h b/chrome/browser/profiles/gaia_info_update_service.h
index 4128577..79f0b08 100644
--- a/chrome/browser/profiles/gaia_info_update_service.h
+++ b/chrome/browser/profiles/gaia_info_update_service.h
@@ -55,7 +55,8 @@
   void ScheduleNextUpdate();
 
   // Overridden from identity::IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 5dda405..b1971c4 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -76,7 +76,6 @@
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/startup_task_runner_service.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/prefs/pref_service.h"
@@ -86,6 +85,7 @@
 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "components/sync/base/stop_source.h"
+#include "components/sync/driver/sync_service.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
diff --git a/chrome/browser/profiles/profile_window.cc b/chrome/browser/profiles/profile_window.cc
index 4c19ba505..45ce578 100644
--- a/chrome/browser/profiles/profile_window.cc
+++ b/chrome/browser/profiles/profile_window.cc
@@ -31,14 +31,12 @@
 #include "chrome/browser/signin/account_reconcilor_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/signin_ui_util.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/profile_chooser_constants.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/flags_ui/pref_service_flags_storage.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_info.h"
diff --git a/chrome/browser/profiles/renderer_updater.cc b/chrome/browser/profiles/renderer_updater.cc
index 28f8051..4a387bb 100644
--- a/chrome/browser/profiles/renderer_updater.cc
+++ b/chrome/browser/profiles/renderer_updater.cc
@@ -198,7 +198,7 @@
 }
 #endif
 
-void RendererUpdater::OnPrimaryAccountSet(const AccountInfo& account_info) {
+void RendererUpdater::OnPrimaryAccountSet(const CoreAccountInfo& account_info) {
   UpdateAllRenderers();
 }
 
diff --git a/chrome/browser/profiles/renderer_updater.h b/chrome/browser/profiles/renderer_updater.h
index 90a63c1..0d7c3c8 100644
--- a/chrome/browser/profiles/renderer_updater.h
+++ b/chrome/browser/profiles/renderer_updater.h
@@ -60,7 +60,7 @@
 #endif
 
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& account_info) override;
+  void OnPrimaryAccountSet(const CoreAccountInfo& account_info) override;
   void OnPrimaryAccountCleared(const AccountInfo& account_info) override;
 
   // VariationsHttpHeaderProvider::Observer:
diff --git a/chrome/browser/resource_coordinator/chrome_content_browser_client_resource_coordinator_part.cc b/chrome/browser/resource_coordinator/chrome_content_browser_client_resource_coordinator_part.cc
deleted file mode 100644
index 2a1ebb5..0000000
--- a/chrome/browser/resource_coordinator/chrome_content_browser_client_resource_coordinator_part.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2019 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.
-
-#include "chrome/browser/resource_coordinator/chrome_content_browser_client_resource_coordinator_part.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/browser/resource_coordinator/render_process_user_data.h"
-
-namespace {
-
-void BindProcessResourceCoordinator(
-    content::RenderProcessHost* render_process_host,
-    resource_coordinator::mojom::ProcessCoordinationUnitRequest request) {
-  resource_coordinator::RenderProcessUserData* user_data =
-      resource_coordinator::RenderProcessUserData::GetForRenderProcessHost(
-          render_process_host);
-
-  user_data->process_resource_coordinator()->AddBinding(std::move(request));
-}
-
-}  // namespace
-
-ChromeContentBrowserClientResourceCoordinatorPart::
-    ChromeContentBrowserClientResourceCoordinatorPart() = default;
-ChromeContentBrowserClientResourceCoordinatorPart::
-    ~ChromeContentBrowserClientResourceCoordinatorPart() = default;
-
-void ChromeContentBrowserClientResourceCoordinatorPart::
-    ExposeInterfacesToRenderer(
-        service_manager::BinderRegistry* registry,
-        blink::AssociatedInterfaceRegistry* associated_registry,
-        content::RenderProcessHost* render_process_host) {
-  registry->AddInterface(
-      base::BindRepeating(&BindProcessResourceCoordinator,
-                          base::Unretained(render_process_host)),
-      base::SequencedTaskRunnerHandle::Get());
-
-  // When a RenderFrameHost is "resurrected" with a new process  it will already
-  // have user data attached. This will happen on renderer crash.
-  if (!resource_coordinator::RenderProcessUserData::GetForRenderProcessHost(
-          render_process_host)) {
-    resource_coordinator::RenderProcessUserData::CreateForRenderProcessHost(
-        render_process_host);
-  }
-}
diff --git a/chrome/browser/resource_coordinator/chrome_content_browser_client_resource_coordinator_part.h b/chrome/browser/resource_coordinator/chrome_content_browser_client_resource_coordinator_part.h
deleted file mode 100644
index cfd3af7..0000000
--- a/chrome/browser/resource_coordinator/chrome_content_browser_client_resource_coordinator_part.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2019 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.
-
-#ifndef CHROME_BROWSER_RESOURCE_COORDINATOR_CHROME_CONTENT_BROWSER_CLIENT_RESOURCE_COORDINATOR_PART_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_CHROME_CONTENT_BROWSER_CLIENT_RESOURCE_COORDINATOR_PART_H_
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "chrome/browser/chrome_content_browser_client_parts.h"
-
-// Allows tracking RenderProcessHost lifetime and proffering the Resource
-// Coordinator interface to new renderers.
-class ChromeContentBrowserClientResourceCoordinatorPart
-    : public ChromeContentBrowserClientParts {
- public:
-  ChromeContentBrowserClientResourceCoordinatorPart();
-  ~ChromeContentBrowserClientResourceCoordinatorPart() override;
-
-  // ChromeContentBrowserClientParts overrides.
-  void ExposeInterfacesToRenderer(
-      service_manager::BinderRegistry* registry,
-      blink::AssociatedInterfaceRegistry* associated_registry,
-      content::RenderProcessHost* render_process_host) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ChromeContentBrowserClientResourceCoordinatorPart);
-};
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_CHROME_CONTENT_BROWSER_CLIENT_RESOURCE_COORDINATOR_PART_H_
diff --git a/chrome/browser/resource_coordinator/tab_helper.cc b/chrome/browser/resource_coordinator/tab_helper.cc
index e300a6d..9ff6188d 100644
--- a/chrome/browser/resource_coordinator/tab_helper.cc
+++ b/chrome/browser/resource_coordinator/tab_helper.cc
@@ -15,8 +15,8 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/performance_manager/page_resource_coordinator.h"
 #include "chrome/browser/performance_manager/performance_manager.h"
+#include "chrome/browser/performance_manager/render_process_user_data.h"
 #include "chrome/browser/resource_coordinator/page_signal_receiver.h"
-#include "chrome/browser/resource_coordinator/render_process_user_data.h"
 #include "chrome/browser/resource_coordinator/resource_coordinator_parts.h"
 #include "chrome/browser/resource_coordinator/tab_load_tracker.h"
 #include "chrome/browser/resource_coordinator/tab_memory_metrics_reporter.h"
@@ -111,8 +111,8 @@
     parent_frame_node->AddChildFrame(*frame.get());
   }
 
-  RenderProcessUserData* user_data =
-      RenderProcessUserData::GetForRenderProcessHost(
+  performance_manager::RenderProcessUserData* user_data =
+      performance_manager::RenderProcessUserData::GetForRenderProcessHost(
           render_frame_host->GetProcess());
   // In unittests the user data isn't populated as the relevant main parts
   // is not in play.
diff --git a/chrome/browser/resources/app_management/BUILD.gn b/chrome/browser/resources/app_management/BUILD.gn
index 081def7..8193a52 100644
--- a/chrome/browser/resources/app_management/BUILD.gn
+++ b/chrome/browser/resources/app_management/BUILD.gn
@@ -25,6 +25,7 @@
       ":pwa_permission_view",
       ":reducers",
       ":router",
+      ":search_view",
       ":store",
       ":store_client",
       ":types",
@@ -176,6 +177,14 @@
     ]
   }
 
+  js_library("search_view") {
+    deps = [
+      ":app_item",
+      ":store_client",
+      "//ui/webui/resources/js:load_time_data",
+    ]
+  }
+
   js_library("store") {
     deps = [
       ":reducers",
diff --git a/chrome/browser/resources/app_management/actions.js b/chrome/browser/resources/app_management/actions.js
index a195768e..79a41f6 100644
--- a/chrome/browser/resources/app_management/actions.js
+++ b/chrome/browser/resources/app_management/actions.js
@@ -48,6 +48,10 @@
           'Tried to load app detail page without providing an app id.');
     }
 
+    if (PageType == PageType.SEARCH) {
+      console.warn('This should not be invoked');
+    }
+
     return {
       name: 'change-page',
       pageType: pageType,
@@ -55,10 +59,34 @@
     };
   }
 
+  /** @return {!cr.ui.Action} */
+  function clearSearch() {
+    return {
+      name: 'clear-search',
+    };
+  }
+
+  /**
+   * @param {string} term
+   * @return {!cr.ui.Action}
+   */
+  function setSearchTerm(term) {
+    if (!term) {
+      return clearSearch();
+    }
+    return {
+      name: 'start-search',
+      term: term,
+    };
+  }
+
+
   return {
     addApp: addApp,
     changeApp: changeApp,
     removeApp: removeApp,
     changePage: changePage,
+    clearSearch: clearSearch,
+    setSearchTerm: setSearchTerm,
   };
 });
diff --git a/chrome/browser/resources/app_management/app.html b/chrome/browser/resources/app_management/app.html
index 4218b74..b9d948e 100644
--- a/chrome/browser/resources/app_management/app.html
+++ b/chrome/browser/resources/app_management/app.html
@@ -8,6 +8,7 @@
 <link rel="import" href="notifications_view.html">
 <link rel="import" href="pwa_permission_view.html">
 <link rel="import" href="router.html">
+<link rel="import" href="search_view.html">
 <link rel="import" href="store.html">
 <link rel="import" href="store_client.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html">
@@ -26,7 +27,10 @@
       background: var(--md-toolbar-color);
     }
     </style>
-    <cr-toolbar page-name="$i18n{title}" search-prompt="$i18n{searchPrompt}">
+    <cr-toolbar
+        page-name="$i18n{title}"
+        search-prompt="$i18n{searchPrompt}"
+        on-search-changed="onSearchChanged_">
     </cr-toolbar>
     <app-management-dom-switch id="view-selector"
         route="[[selectedRouteId_(currentPage_)]]">
@@ -40,6 +44,8 @@
         <app-management-chrome-app-permission-view
             route-id="chrome-app-permission-view">
         </app-management-chrome-app-permission-view>
+        <app-management-search-view route-id="search-view">
+        </app-management-search-view>
       </template>
     </app-management-dom-switch>
     <app-management-router></app-management-router>
diff --git a/chrome/browser/resources/app_management/app.js b/chrome/browser/resources/app_management/app.js
index 4816261..f2e30a0 100644
--- a/chrome/browser/resources/app_management/app.js
+++ b/chrome/browser/resources/app_management/app.js
@@ -10,6 +10,11 @@
   ],
 
   properties: {
+    /** @private */
+    searchTerm_: {
+      type: String,
+    },
+
     /**
      * @private {Page}
      */
@@ -22,11 +27,25 @@
    * @override
    */
   attached: function() {
+    this.watch('searchTerm_', function(state) {
+      return state.search.term;
+    });
     this.watch('currentPage_', state => state.currentPage);
     this.updateFromStore();
   },
 
   /**
+   * @param {Event} e
+   * @private
+   */
+  onSearchChanged_: function(e) {
+    const searchTerm = /** @type {string} */ (e.detail);
+    if (searchTerm != this.searchTerm_) {
+      this.dispatch(app_management.actions.setSearchTerm(searchTerm));
+    }
+  },
+
+  /**
    * @param {Page} currentPage
    * @private
    */
@@ -38,6 +57,9 @@
       case (PageType.NOTIFICATIONS):
         return 'notifications-view';
 
+      case (PageType.SEARCH):
+        return 'search-view';
+
       case (PageType.DETAIL):
         const state = app_management.Store.getInstance().data;
         const selectedAppType =
diff --git a/chrome/browser/resources/app_management/constants.js b/chrome/browser/resources/app_management/constants.js
index a44e8179..a461221 100644
--- a/chrome/browser/resources/app_management/constants.js
+++ b/chrome/browser/resources/app_management/constants.js
@@ -23,6 +23,7 @@
   MAIN: 0,
   DETAIL: 1,
   NOTIFICATIONS: 2,
+  SEARCH: 3,
 };
 
 /**
diff --git a/chrome/browser/resources/app_management/reducers.js b/chrome/browser/resources/app_management/reducers.js
index 337bdca2..ce1512e 100644
--- a/chrome/browser/resources/app_management/reducers.js
+++ b/chrome/browser/resources/app_management/reducers.js
@@ -95,6 +95,25 @@
   };
 
   /**
+   * TODO(ceciliani) Delete search page type and navigate router by calculating
+   * if there is search.term.
+   * @param {Object} action
+   * @return {Page}
+   */
+  CurrentPageState.changeForSearch = function(action) {
+    if (action.term) {
+      return {
+        pageType: PageType.SEARCH,
+        selectedAppId: null,
+      };
+    } else {
+      return {
+        pageType: PageType.MAIN,
+        selectedAppId: null,
+      };
+    }
+  };
+  /**
    * @param {Page} currentPage
    * @param {Object} action
    * @return {Page}
@@ -119,6 +138,10 @@
    */
   CurrentPageState.updateCurrentPage = function(apps, currentPage, action) {
     switch (action.name) {
+      case 'start-search':
+        return CurrentPageState.changeForSearch(action);
+      case 'clear-search':
+        return CurrentPageState.changeForSearch(action);
       case 'change-page':
         return CurrentPageState.changePage(apps, action);
       case 'remove-app':
@@ -128,6 +151,52 @@
     }
   };
 
+  const SearchState = {};
+
+  /**
+   * @param {AppMap} apps
+   * @param {SearchState} search
+   * @param {Object} action
+   * @return {SearchState}
+   */
+  SearchState.startSearch = function(apps, search, action) {
+    const results = [];
+    for (const app of Object.values(apps)) {
+      if (app.title.includes(action.term)) {
+        results.push(app);
+      }
+    }
+    return /** @type {SearchState} */ (Object.assign({}, search, {
+      term: action.term,
+      results: results,
+    }));
+  };
+
+  /** @return {SearchState} */
+  SearchState.clearSearch = function() {
+    return {
+      term: null,
+      results: null,
+    };
+  };
+
+  /**
+   * @param {AppMap} apps
+   * @param {SearchState} search
+   * @param {Object} action
+   * @return {SearchState}
+   */
+  SearchState.updateSearch = function(apps, search, action) {
+    switch (action.name) {
+      case 'start-search':
+        return SearchState.startSearch(apps, search, action);
+      case 'clear-search':
+        return SearchState.clearSearch();
+      default:
+        return search;
+    }
+  };
+
   /**
    * Root reducer for the App Management page. This is called by the store in
    * response to an action, and the return value is used to update the UI.
@@ -138,6 +207,7 @@
   function reduceAction(state, action) {
     return {
       apps: AppState.updateApps(state.apps, action),
+      search: SearchState.updateSearch(state.apps, state.search, action),
       currentPage: CurrentPageState.updateCurrentPage(
           state.apps, state.currentPage, action),
     };
@@ -147,5 +217,6 @@
     reduceAction: reduceAction,
     AppState: AppState,
     CurrentPageState: CurrentPageState,
+    SearchState: SearchState,
   };
 });
diff --git a/chrome/browser/resources/app_management/router.js b/chrome/browser/resources/app_management/router.js
index 0b5dae0..88fb59b 100644
--- a/chrome/browser/resources/app_management/router.js
+++ b/chrome/browser/resources/app_management/router.js
@@ -28,6 +28,12 @@
       observer: 'onUrlQueryChanged_',
     },
 
+    /** @private */
+    searchTerm_: {
+      type: String,
+      value: '',
+    },
+
     /** @private {PageType} */
     currentPageType_: {
       type: Number,
@@ -41,16 +47,19 @@
 
   observers: [
     'onUrlChanged_(path_, queryParams_)',
-    'onStateChanged_(currentPageType_, selectedAppId_)',
+    'onStateChanged_(currentPageType_, selectedAppId_, searchTerm_)',
   ],
 
   attached: function() {
-    this.watch('currentPageType_', function(state) {
+    this.watch('currentPageType_', (state) => {
       return state.currentPage.pageType;
     });
-    this.watch('selectedAppId_', function(state) {
+    this.watch('selectedAppId_', (state) => {
       return state.currentPage.selectedAppId;
     });
+    this.watch('searchTerm_', (state) => {
+      return state.search.term;
+    });
     this.updateFromStore();
   },
 
@@ -94,7 +103,11 @@
     if (!newId) {
       delete this.queryParams_.id;
     }
-
+    const newSearch = this.searchTerm_;
+    this.queryParams_.q = newSearch;
+    if (!newSearch) {
+      delete this.queryParams_.q;
+    }
     this.queryParams_ = Object.assign({}, this.queryParams_);
   },
 
@@ -106,7 +119,6 @@
     } else if (this.currentPageType_ === PageType.NOTIFICATIONS) {
       path = 'notifications';
     }
-
     this.path_ = '/' + path;
   },
 
@@ -118,6 +130,7 @@
   /** @private */
   parseUrl_: function() {
     const newId = this.queryParams_.id;
+    const searchTerm = this.queryParams_.q;
 
     const pageFromUrl = this.path_.substr(1).split('/')[0];
     let newPage = PageType.MAIN;
@@ -131,6 +144,8 @@
 
     if (newPage === PageType.DETAIL) {
       this.dispatch(app_management.actions.changePage(PageType.DETAIL, newId));
+    } else if (this.queryParams_.q) {
+      this.dispatch(app_management.actions.setSearchTerm(this.queryParams_.q));
     } else {
       this.dispatch(app_management.actions.changePage(newPage));
     }
diff --git a/chrome/browser/resources/app_management/search_view.html b/chrome/browser/resources/app_management/search_view.html
new file mode 100644
index 0000000..3fa410b
--- /dev/null
+++ b/chrome/browser/resources/app_management/search_view.html
@@ -0,0 +1,43 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="app_item.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_toolbar/cr_toolbar.html">
+
+<dom-module id="app-management-search-view">
+  <template>
+    <style include="app-management-shared-css">
+      #centered-message {
+        align-items: center;
+        color: #6e6e6e;
+        cursor: default;
+        display: flex;
+        font-size: 14px;
+        font-weight: 500;
+        height: 100%;
+        justify-content: center;
+        user-select: none;
+        white-space: nowrap;
+      }
+
+      .app-management-item-arrow {
+        margin-inline-end: 8px;
+        padding: 12px;
+      }
+    </style>
+    <div class="card-container" hidden$="[[isEmptyList_(apps_)]]">
+      <template is="dom-repeat" items="[[apps_]]">
+        <app-management-app-item app="[[item]]">
+          <paper-icon-button-light slot="right-content"
+              class="subpage-arrow app-management-item-arrow" actionable>
+            <button></button>
+          </paper-icon-button-light>
+        </app-management-app-item>
+      </template>
+    </div>
+    <div id="centered-message"
+        hidden$="[[!isEmptyList_(apps_)]]">
+      $i18n{noSearchResults}
+    </div>
+  </template>
+  <script src="search_view.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/app_management/search_view.js b/chrome/browser/resources/app_management/search_view.js
new file mode 100644
index 0000000..b149e94
--- /dev/null
+++ b/chrome/browser/resources/app_management/search_view.js
@@ -0,0 +1,40 @@
+// 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.
+Polymer({
+  is: 'app-management-search-view',
+
+  behaviors: [
+    app_management.StoreClient,
+  ],
+
+  properties: {
+    /**
+     * List of apps returned from search results.
+     * @private {Array<App>}
+     */
+    apps_: {
+      type: Array,
+      value: () => [],
+    },
+  },
+
+  attached: function() {
+    this.watch('apps_', (state) => {
+      return state.search.results;
+    });
+  },
+
+  /**
+   * Check whether there is search results.
+   * @param {Array<App>} apps
+   * @return {boolean}
+   * @private
+   */
+  isEmptyList_: function(apps) {
+    if (!apps) {
+      return true;
+    }
+    return apps.length === 0;
+  },
+});
diff --git a/chrome/browser/resources/app_management/types.js b/chrome/browser/resources/app_management/types.js
index c0df8cbc..afeb469c 100644
--- a/chrome/browser/resources/app_management/types.js
+++ b/chrome/browser/resources/app_management/types.js
@@ -39,6 +39,15 @@
  * @typedef {{
  *   apps: AppMap,
  *   currentPage: Page,
+ *   search: SearchState,
  * }}
  */
 let AppManagementPageState;
+
+/**
+ * @typedef {{
+ *   term: ?string,
+ *   results: ?Array<App>,
+ * }}
+ */
+let SearchState;
diff --git a/chrome/browser/resources/app_management/util.js b/chrome/browser/resources/app_management/util.js
index c30185d..fc8e56ff 100644
--- a/chrome/browser/resources/app_management/util.js
+++ b/chrome/browser/resources/app_management/util.js
@@ -17,6 +17,10 @@
         pageType: PageType.MAIN,
         selectedAppId: null,
       },
+      search: {
+        term: null,
+        results: null,
+      },
     };
   }
 
diff --git a/chrome/browser/resources/chromeos/BUILD.gn b/chrome/browser/resources/chromeos/BUILD.gn
index a1ce079..e8d171e8 100644
--- a/chrome/browser/resources/chromeos/BUILD.gn
+++ b/chrome/browser/resources/chromeos/BUILD.gn
@@ -25,6 +25,7 @@
   deps = [
     "bluetooth_pairing_dialog:closure_compile",
     "braille_ime:closure_compile",
+    "contained_home:closure_compile",
     "internet_config_dialog:closure_compile",
     "internet_detail_dialog:closure_compile",
     "login:closure_compile",
diff --git a/chrome/browser/resources/chromeos/contained_home/BUILD.gn b/chrome/browser/resources/chromeos/contained_home/BUILD.gn
new file mode 100644
index 0000000..88a33a4
--- /dev/null
+++ b/chrome/browser/resources/chromeos/contained_home/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2019 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.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_type_check("closure_compile") {
+  deps = [
+    ":contained_home_api",
+  ]
+}
+
+js_library("contained_home_api") {
+  sources = [
+    "api.js",
+    "api_impl.js",
+  ]
+  externs_list = [
+    "$externs_path/arc_apps_private.js",
+    "$externs_path/chrome_extensions.js",
+  ]
+}
diff --git a/chrome/browser/resources/chromeos/contained_home/api.js b/chrome/browser/resources/chromeos/contained_home/api.js
new file mode 100644
index 0000000..a3592f5
--- /dev/null
+++ b/chrome/browser/resources/chromeos/contained_home/api.js
@@ -0,0 +1,123 @@
+// Copyright 2019 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.
+
+/**
+ * @fileoverview Chrome OS Contained Home API definition.
+ */
+
+/**
+ * Namespace for the contained home bridge and related data.
+ * @const
+ */
+var containedHome = {};
+
+/**
+ * System bridge API for the contained home experience.
+ *
+ * @interface
+ */
+containedHome.Bridge = class {
+  /**
+   * Adds listener for system events.
+   * @param {!containedHome.Listener} listener Listener for system events.
+   */
+  addListener(listener) {}
+
+  /**
+   * Returns an access token with scope for the contained home experience.
+   * @return {!Promise<string>} Promise for the access token.
+   */
+  getAccessToken() {}
+
+  /**
+   * Returns a list of apps installed in the user session.
+   * @return {!Promise<!Array<!containedHome.InstalledApp>>} Promise for the
+   *     list of apps.
+   */
+  getInstalledApps() {}
+
+  /**
+   * Launches a content (app, video, etc).
+   * @param {!containedHome.ContentSource} contentSource
+   * @param {string} contentId
+   * @param {?Object=} opt_params Optional params to locate the content.
+   * @return {!Promise<boolean>} Promise that is resolved after the content is
+   *     launched.
+   */
+  launchContent(contentSource, contentId, opt_params) {}
+};
+
+/**
+ * Set of known / handled content sources.
+ *
+ * A "Content Source" describes how to launch/view the content.
+ * @enum {string}
+ */
+containedHome.ContentSource = {
+  /** The content is, or is hosted inside, an ARC++ app. */
+  ARC_INTENT: 'arc_intent',
+};
+
+/**
+ * Types of installed apps on ChromeOS.
+ * @enum {string}
+ */
+containedHome.AppType = {
+  /** The app is an ARC++ app (Android app). */
+  ARC: 'arc',
+};
+
+/**
+ * A record representing an installed app on the system.
+ * @record
+ */
+containedHome.InstalledApp = class {
+  constructor() {
+    /** @type {!containedHome.AppType} The type of app. */
+    this.appType;
+    /**
+     * @type {string} Stable, unique identifier for the app. For ARC++ apps,
+     *     this is the package name.
+     */
+    this.appId;
+    /** @type {string} Readable name to display. */
+    this.displayName;
+    /** @type {string | undefined} Base64-encoded thumbnail image, fallback. */
+    this.thumbnailImage;
+    /** @type {boolean | undefined} Whether the app is suspended. */
+    this.suspended;
+  }
+};
+
+/**
+ * Different ways an installed app can change.
+ * @enum {string}
+ */
+containedHome.AppEventType = {
+  INSTALLED: 'installed',
+  UNINSTALLED: 'uninstalled',
+};
+
+/**
+ * Interface for a listener of system events, subscribed via
+ * {!containedHome.Bridge}.
+ *
+ * @interface
+ */
+containedHome.Listener = class {
+  /**
+   * Called when an app state change.
+   * @param {!containedHome.InstalledApp} app The app whose state changed.
+   * @param {!containedHome.AppEventType} appEventType Type of the event
+   *     indicating what changed for the app.
+   */
+  onInstalledAppChanged(app, appEventType) {}
+};
+
+/**
+ * Provides bridge implementation.
+ * @return {!containedHome.Bridge} Bridge instance that can be used to interact
+ *     with ChromeOS.
+ */
+containedHome.getChromeOsBridge = function() {};
diff --git a/chrome/browser/resources/chromeos/contained_home/api_impl.js b/chrome/browser/resources/chromeos/contained_home/api_impl.js
new file mode 100644
index 0000000..55cecde4
--- /dev/null
+++ b/chrome/browser/resources/chromeos/contained_home/api_impl.js
@@ -0,0 +1,84 @@
+// Copyright 2019 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.
+
+/**
+ * @fileoverview ContainedHome implementation.
+ */
+
+/** @implements {containedHome.Bridge} */
+class ContainedHomeBridge {
+  constructor() {
+    /** @type {!Array<!containedHome.Listener>} */
+    this.listeners = [];
+
+    chrome.arcAppsPrivate.onInstalled.addListener(installedApp => {
+      const app = {
+        appType: containedHome.AppType.ARC,
+        appId: installedApp.packageName,
+        displayName: installedApp.packageName,
+        suspended: false,
+        thumbnailImage: '',
+      };
+      for (const listener of this.listeners) {
+        listener.onInstalledAppChanged(
+            app, containedHome.AppEventType.INSTALLED);
+      }
+    });
+  }
+
+  /** @override */
+  addListener(listener) {
+    this.listeners.push(listener);
+  }
+
+  /** @override */
+  getAccessToken() {
+    return new Promise((resolve, reject) => {
+      chrome.identity.getAuthToken({'scopes': []}, token => {
+        if (token) {
+          resolve(token);
+        } else {
+          reject('Unable to get access token.');
+        }
+      });
+    });
+  }
+
+  /** @override */
+  getInstalledApps() {
+    return new Promise((resolve, reject) => {
+      chrome.arcAppsPrivate.getLaunchableApps(launchableApps => {
+        const installedApps = [];
+        for (const launchableApp of launchableApps) {
+          installedApps.push({
+            appType: containedHome.AppType.ARC,
+            appId: launchableApp.packageName,
+            displayName: launchableApp.packageName,
+            suspended: false,
+            thumbnailImage: '',
+          });
+        }
+        resolve(installedApps);
+      });
+    });
+  }
+
+  /** @override */
+  launchContent(contentSource, contentId, opt_params) {
+    if (contentSource === containedHome.ContentSource.ARC_INTENT) {
+      // TODO(brunoad): create and migrate to a more generic API.
+      chrome.arcAppsPrivate.launchApp(contentId);
+    }
+    return Promise.resolve(true);
+  }
+}
+
+/**
+ * Provides bridge implementation.
+ * @return {!containedHome.Bridge} Bridge instance that can be used to interact
+ *     with ChromeOS.
+ */
+containedHome.getChromeOsBridge = function() {
+  return new ContainedHomeBridge();
+};
diff --git a/chrome/browser/resources/chromeos/contained_home/contained_home_resources.grdp b/chrome/browser/resources/chromeos/contained_home/contained_home_resources.grdp
new file mode 100644
index 0000000..408b3bfd
--- /dev/null
+++ b/chrome/browser/resources/chromeos/contained_home/contained_home_resources.grdp
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<grit-part>
+  <include name="IDR_CONTAINED_HOME_BG_JS" file="chromeos/contained_home/bg.js" type="BINDATA" />
+  <include name="IDR_CONTAINED_HOME_ICON_192" file="chromeos/contained_home/static/icon192.png" type="BINDATA" />
+  <include name="IDR_CONTAINED_HOME_MAIN_HTML" file="chromeos/contained_home/main.html" type="chrome_html" />
+  <include name="IDR_CONTAINED_HOME_API_JS" file="chromeos/contained_home/api.js" type="BINDATA" />
+  <include name="IDR_CONTAINED_HOME_API_IMPL_JS" file="chromeos/contained_home/api_impl.js" type="BINDATA" />
+
+  <part file="chromeos/contained_home/contained_home_resources_internal.grdp" />
+</grit-part>
diff --git a/chrome/browser/resources/chromeos/contained_home/contained_home_resources_internal.grdp b/chrome/browser/resources/chromeos/contained_home/contained_home_resources_internal.grdp
new file mode 100644
index 0000000..5656207d
--- /dev/null
+++ b/chrome/browser/resources/chromeos/contained_home/contained_home_resources_internal.grdp
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- TODO(brunoad): This list will be populated from src-internal -->
+<grit-part />
diff --git a/chrome/browser/resources/chromeos/contained_home/manifest.json b/chrome/browser/resources/chromeos/contained_home/manifest.json
index 03c1d26..bafcc4f 100644
--- a/chrome/browser/resources/chromeos/contained_home/manifest.json
+++ b/chrome/browser/resources/chromeos/contained_home/manifest.json
@@ -6,11 +6,17 @@
   "manifest_version": 2,
   "description": "Contained Home",
   "icons": {
-    "192": "images/icon192.png"
+    "192": "static/icon192.png"
   },
+  "permissions": [
+    "arcAppsPrivate",
+    "https://*.googleapis.com",
+    "identity"
+  ],
   "app": {
     "background": {
       "scripts": ["bg.js"]
-    }
+    },
+    "content_security_policy": "script-src 'self'; object-src 'self'; img-src 'self' data: https://lh3.googleusercontent.com"
   }
 }
diff --git a/chrome/browser/resources/chromeos/contained_home/images/icon192.png b/chrome/browser/resources/chromeos/contained_home/static/icon192.png
similarity index 100%
rename from chrome/browser/resources/chromeos/contained_home/images/icon192.png
rename to chrome/browser/resources/chromeos/contained_home/static/icon192.png
Binary files differ
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index 7790ea7..70695a1 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -91,9 +91,7 @@
         <include name="IDR_ARC_SUPPORT_RECOMMEND_APP_LIST_VIEW_JS" file="chromeos/arc_support/recommend_app_list_view.js" type="BINDATA" />
         <include name="IDR_ARC_SUPPORT_RECOMMEND_APP_LIST_VIEW_HTML" file="chromeos/arc_support/recommend_app_list_view.html" type="chrome_html" flattenhtml="true" />
         <if expr="_google_chrome">
-          <include name="IDR_CONTAINED_HOME_BG_JS" file="chromeos/contained_home/bg.js" type="BINDATA" />
-          <include name="IDR_CONTAINED_HOME_ICON_192" file="chromeos/contained_home/images/icon192.png" type="BINDATA" />
-          <include name="IDR_CONTAINED_HOME_MAIN_HTML" file="chromeos/contained_home/main.html" type="chrome_html" />
+          <part file="chromeos/contained_home/contained_home_resources.grdp" />
         </if>
       </if>
       <if expr="enable_plugins">
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
index 0cb6594..2cb94c23 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
@@ -57,10 +57,10 @@
       <div slot="title">$i18n{passwordDetailsTitle}</div>
       <div slot="body">
         <cr-input id="websiteInput" label="$i18n{editPasswordWebsiteLabel}"
-            value="[[item.entry.loginPair.urls.link]]" readonly>
+            value="[[item.entry.urls.link]]" readonly>
         </cr-input>
         <cr-input id="usernameInput" label="$i18n{editPasswordUsernameLabel}"
-            value="[[item.entry.loginPair.username]]" readonly>
+            value="[[item.entry.username]]" readonly>
         </cr-input>
         <cr-input id="passwordInput" readonly
             label="$i18n{editPasswordPasswordLabel}"
diff --git a/chrome/browser/resources/settings/autofill_page/password_list_item.html b/chrome/browser/resources/settings/autofill_page/password_list_item.html
index cd795d3a..9eb7a20a 100644
--- a/chrome/browser/resources/settings/autofill_page/password_list_item.html
+++ b/chrome/browser/resources/settings/autofill_page/password_list_item.html
@@ -51,20 +51,20 @@
     </style>
     <div class="list-item" focus-row-container>
       <div class="website-column no-min-width"
-          title="[[item.entry.loginPair.urls.link]]">
-        <site-favicon url="[[item.entry.loginPair.urls.link]]"></site-favicon>
+          title="[[item.entry.urls.link]]">
+        <site-favicon url="[[item.entry.urls.link]]"></site-favicon>
         <a id="originUrl" target="_blank" class="no-min-width"
-            href="[[item.entry.loginPair.urls.link]]"
+            href="[[item.entry.urls.link]]"
             focus-row-control focus-type="originUrl">
           <span class="text-elide">
             <!-- This bdo tag is necessary to fix the display of domains
               starting with numbers. -->
-            <bdo dir="ltr">[[item.entry.loginPair.urls.shown]]</bdo>
+            <bdo dir="ltr">[[item.entry.urls.shown]]</bdo>
           </span>
         </a>
       </div>
       <input id="username" class="username-column password-field"
-          readonly tabindex="-1" value="[[item.entry.loginPair.username]]">
+          readonly tabindex="-1" value="[[item.entry.username]]">
       <div class="password-column">
         <template is="dom-if" if="[[!item.entry.federationText]]">
           <input id="password" aria-label=$i18n{editPasswordPasswordLabel}
diff --git a/chrome/browser/resources/settings/autofill_page/password_list_item.js b/chrome/browser/resources/settings/autofill_page/password_list_item.js
index d530298..12c176f 100644
--- a/chrome/browser/resources/settings/autofill_page/password_list_item.js
+++ b/chrome/browser/resources/settings/autofill_page/password_list_item.js
@@ -46,6 +46,6 @@
     return loadTimeData.getStringF(
         (item.entry.federationText) ? 'passwordRowFederatedMoreActionsButton' :
                                       'passwordRowMoreActionsButton',
-        item.entry.loginPair.username, item.entry.loginPair.urls.shown);
+        item.entry.username, item.entry.urls.shown);
   },
 });
diff --git a/chrome/browser/resources/settings/autofill_page/password_manager_proxy.js b/chrome/browser/resources/settings/autofill_page/password_manager_proxy.js
index cfa8d09..fd97628 100644
--- a/chrome/browser/resources/settings/autofill_page/password_manager_proxy.js
+++ b/chrome/browser/resources/settings/autofill_page/password_manager_proxy.js
@@ -134,9 +134,6 @@
 /** @typedef {chrome.passwordsPrivate.PasswordUiEntry} */
 PasswordManagerProxy.PasswordUiEntry;
 
-/** @typedef {chrome.passwordsPrivate.LoginPair} */
-PasswordManagerProxy.LoginPair;
-
 /** @typedef {chrome.passwordsPrivate.ExceptionEntry} */
 PasswordManagerProxy.ExceptionEntry;
 
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_section.js b/chrome/browser/resources/settings/autofill_page/passwords_section.js
index bfc8647..2dc5b59 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_section.js
+++ b/chrome/browser/resources/settings/autofill_page/passwords_section.js
@@ -252,7 +252,7 @@
     }
 
     return this.savedPasswords.filter(
-        p => [p.entry.loginPair.urls.shown, p.entry.loginPair.username].some(
+        p => [p.entry.urls.shown, p.entry.username].some(
             term => term.toLowerCase().includes(filter.toLowerCase())));
   },
 
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js
index b3f9457..e57935a 100644
--- a/chrome/browser/resources/settings/people_page/sync_page.js
+++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -272,11 +272,17 @@
       if (this.unifiedConsentEnabled && this.syncStatus &&
           !!this.syncStatus.setupInProgress && this.didAbort_ &&
           !this.setupCancelDialogConfirmed_) {
-        settings.navigateTo(settings.routes.SYNC);
-        this.showSetupCancelDialog_ = true;
-        // Flush to make sure that the setup cancel dialog is attached.
-        Polymer.dom.flush();
-        this.$$('#setupCancelDialog').showModal();
+        // Yield so that other |currentRouteChanged| observers are called,
+        // before triggering another navigation (and another round of observers
+        // firing). Triggering navigation from within an observer leads to some
+        // undefined behavior and runtime errors.
+        requestAnimationFrame(() => {
+          settings.navigateTo(settings.routes.SYNC);
+          this.showSetupCancelDialog_ = true;
+          // Flush to make sure that the setup cancel dialog is attached.
+          Polymer.dom.flush();
+          this.$$('#setupCancelDialog').showModal();
+        });
       } else {
         this.setupCancelDialogConfirmed_ = false;
         this.onNavigateAwayFromPage_();
diff --git a/chrome/browser/resources/settings/settings_page/main_page_behavior.js b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
index ea535adc..761e23c 100644
--- a/chrome/browser/resources/settings/settings_page/main_page_behavior.js
+++ b/chrome/browser/resources/settings/settings_page/main_page_behavior.js
@@ -202,17 +202,21 @@
 
     /**
      * @param {!settings.Route} oldRoute
+     * @return {!Promise<void>}
      * @private
      */
     enterMainPage_: function(oldRoute) {
       const oldSection = this.getSection(oldRoute.section);
       oldSection.classList.remove('expanded');
       this.classList.remove('showing-subpage');
-      requestAnimationFrame(() => {
-        if (settings.lastRouteChangeWasPopstate()) {
-          this.scroller.scrollTop = this.lastScrollTop_;
-        }
-        this.fire('showing-main-page');
+      return new Promise((res, rej) => {
+        requestAnimationFrame(() => {
+          if (settings.lastRouteChangeWasPopstate()) {
+            this.scroller.scrollTop = this.lastScrollTop_;
+          }
+          this.fire('showing-main-page');
+          res();
+        });
       });
     },
 
@@ -318,8 +322,9 @@
           // different sections, but are linked to each other. For example
           // /storage and /accounts (in ChromeOS).
           if (!oldRoute.contains(newRoute) && !newRoute.contains(oldRoute)) {
-            this.enterMainPage_(oldRoute);
-            this.enterSubpage_(newRoute);
+            this.enterMainPage_(oldRoute).then(() => {
+              this.enterSubpage_(newRoute);
+            });
             return;
           }
 
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager.cc b/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
index d8c967b..368bc20 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager.cc
@@ -127,7 +127,9 @@
 }
 
 void AdvancedProtectionStatusManager::OnPrimaryAccountSet(
-    const AccountInfo& account_info) {
+    const CoreAccountInfo& account_info) {
+  // TODO(crbug.com/926204): remove IdentityManager ensures that primary account
+  // always has valid refresh token when it is set.
   if (account_info.is_under_advanced_protection)
     OnAdvancedProtectionEnabled();
 }
diff --git a/chrome/browser/safe_browsing/advanced_protection_status_manager.h b/chrome/browser/safe_browsing/advanced_protection_status_manager.h
index abaf42f..6bd48d6 100644
--- a/chrome/browser/safe_browsing/advanced_protection_status_manager.h
+++ b/chrome/browser/safe_browsing/advanced_protection_status_manager.h
@@ -82,7 +82,7 @@
   void UnsubscribeFromSigninEvents();
 
   // IdentityManager::Observer implementations.
-  void OnPrimaryAccountSet(const AccountInfo& account_info) override;
+  void OnPrimaryAccountSet(const CoreAccountInfo& account_info) override;
   void OnPrimaryAccountCleared(const AccountInfo& account_info) override;
   void OnAccountUpdated(const AccountInfo& info) override;
   void OnAccountRemovedWithInfo(const AccountInfo& info) override;
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index 3652483..e41ae1b 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -241,7 +241,7 @@
     service_->RequestFinished(request_.get(), false, std::move(verdict));
   }
 
-  AccountInfo SetPrimaryAccount(const std::string& email) {
+  CoreAccountInfo SetPrimaryAccount(const std::string& email) {
     IdentityTestEnvironmentProfileAdaptor identity_test_env_profile_adaptor(
         profile());
     return identity_test_env_profile_adaptor.identity_test_env()
@@ -249,7 +249,7 @@
   }
 
   void SetUpSyncAccount(const std::string& hosted_domain,
-                        const AccountInfo account_info) {
+                        const CoreAccountInfo& account_info) {
     FakeAccountFetcherService* account_fetcher_service =
         static_cast<FakeAccountFetcherService*>(
             AccountFetcherServiceFactory::GetForProfile(profile()));
@@ -369,7 +369,7 @@
       PasswordReuseEvent::SIGN_IN_PASSWORD, &reason));
   EXPECT_EQ(RequestOutcome::USER_NOT_SIGNED_IN, reason);
 
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
 
   // Sync password entry pinging is enabled by default.
@@ -466,7 +466,7 @@
   EXPECT_TRUE(
       service_->GetOrganizationName(PasswordReuseEvent::SIGN_IN_PASSWORD)
           .empty());
-  AccountInfo account_info = SetPrimaryAccount(kTestGmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestGmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
   EXPECT_EQ(
@@ -485,7 +485,7 @@
   EXPECT_TRUE(
       service_->GetOrganizationName(PasswordReuseEvent::SIGN_IN_PASSWORD)
           .empty());
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount("example.com", account_info);
   EXPECT_EQ(PasswordReuseEvent::GSUITE, service_->GetSyncAccountType());
   EXPECT_EQ("example.com", service_->GetOrganizationName(
@@ -571,7 +571,7 @@
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyPasswordReuseUserEventNotRecordedDueToIncognito) {
   // Configure sync account type to GMAIL.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
   service_->ConfigService(true /*is_incognito*/,
@@ -607,7 +607,7 @@
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyPasswordReuseDetectedUserEventRecorded) {
   // Configure sync account type to GMAIL.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
 
@@ -640,7 +640,7 @@
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyPasswordCaptureEventScheduledOnStartup) {
   // Configure sync account type to GMAIL.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
 
@@ -667,7 +667,7 @@
                  prefs::kSafeBrowsingNextPasswordCaptureEventLogTime, delay);
 
   // Configure sync account type to GMAIL.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
 
@@ -693,7 +693,7 @@
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyPasswordCaptureEventRecorded) {
   // Configure sync account type to GMAIL.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
 
@@ -724,7 +724,7 @@
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyPasswordCaptureEventReschedules) {
   // Configure sync account type to GMAIL.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
 
@@ -750,7 +750,7 @@
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyPasswordReuseLookupUserEventRecorded) {
   // Configure sync account type to GMAIL.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
 
@@ -812,7 +812,7 @@
 }
 
 TEST_F(ChromePasswordProtectionServiceTest, VerifyGetDefaultChangePasswordURL) {
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount("example.com", account_info);
   EXPECT_EQ(GURL("https://accounts.google.com/"
                  "AccountChooser?Email=foo%40example.com&continue=https%3A%2F%"
@@ -974,7 +974,7 @@
   TestExtensionEventObserver event_observer(test_event_router_);
 
   // Preparing sync account.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount("example.com", account_info);
 
   // Simulates change password.
@@ -999,7 +999,7 @@
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyOnPolicySpecifiedPasswordReuseDetectedEventForPasswordReuse) {
   TestExtensionEventObserver event_observer(test_event_router_);
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount("example.com", account_info);
   profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
                                     PASSWORD_REUSE);
@@ -1032,7 +1032,7 @@
 TEST_F(ChromePasswordProtectionServiceTest,
        VerifyOnPolicySpecifiedPasswordReuseDetectedEventForPhishingReuse) {
   TestExtensionEventObserver event_observer(test_event_router_);
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount("example.com", account_info);
   profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
                                     PASSWORD_REUSE);
@@ -1074,7 +1074,7 @@
       service_->GetWarningDetailText(PasswordReuseEvent::ENTERPRISE_PASSWORD));
 
   // Signs in as a GSuite user.
-  AccountInfo account_info = SetPrimaryAccount(kTestEmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
   SetUpSyncAccount(std::string("example.com"), account_info);
   EXPECT_EQ(PasswordReuseEvent::GSUITE, service_->GetSyncAccountType());
   EXPECT_EQ(
@@ -1098,7 +1098,7 @@
       service_->GetWarningDetailText(PasswordReuseEvent::ENTERPRISE_PASSWORD));
 
   // Signs in as a Gmail user.
-  AccountInfo account_info = SetPrimaryAccount(kTestGmail);
+  CoreAccountInfo account_info = SetPrimaryAccount(kTestGmail);
   SetUpSyncAccount(kNoHostedDomainFound, account_info);
   EXPECT_EQ(PasswordReuseEvent::GMAIL, service_->GetSyncAccountType());
   EXPECT_EQ(default_warning_text, service_->GetWarningDetailText(
diff --git a/chrome/browser/sessions/session_service.cc b/chrome/browser/sessions/session_service.cc
index 0aa0ce88..254ec59 100644
--- a/chrome/browser/sessions/session_service.cc
+++ b/chrome/browser/sessions/session_service.cc
@@ -382,32 +382,41 @@
   ScheduleCommand(sessions::CreateSetWindowAppNameCommand(window_id, app_name));
 }
 
-void SessionService::TabNavigationPathPrunedFromBack(const SessionID& window_id,
-                                                     const SessionID& tab_id,
-                                                     int count) {
+void SessionService::TabNavigationPathPruned(const SessionID& window_id,
+                                             const SessionID& tab_id,
+                                             int index,
+                                             int count) {
   if (!ShouldTrackChangesToWindow(window_id))
     return;
 
-  ScheduleCommand(
-      sessions::CreateTabNavigationPathPrunedFromBackCommand(tab_id, count));
-}
+  DCHECK_GE(index, 0);
+  DCHECK_GT(count, 0);
 
-void SessionService::TabNavigationPathPrunedFromFront(
-    const SessionID& window_id,
-    const SessionID& tab_id,
-    int count) {
-  if (!ShouldTrackChangesToWindow(window_id))
-    return;
-
-  // Update the range of indices.
+  // Update the range of available indices.
   if (tab_to_available_range_.find(tab_id) != tab_to_available_range_.end()) {
     std::pair<int, int>& range = tab_to_available_range_[tab_id];
-    range.first = std::max(0, range.first - count);
-    range.second = std::max(0, range.second - count);
+
+    // if both range.first and range.second are also deleted.
+    if (range.second >= index && range.second < index + count &&
+        range.first >= index && range.first < index + count) {
+      range.first = range.second = 0;
+    } else {
+      // Update range.first
+      if (range.first >= index + count)
+        range.first = range.first - count;
+      else if (range.first >= index && range.first < index + count)
+        range.first = index;
+
+      // Update range.second
+      if (range.second >= index + count)
+        range.second = std::max(range.first, range.second - count);
+      else if (range.second >= index && range.second < index + count)
+        range.second = std::max(range.first, index - 1);
+    }
   }
 
-  ScheduleCommand(
-      sessions::CreateTabNavigationPathPrunedFromFrontCommand(tab_id, count));
+  return ScheduleCommand(
+      sessions::CreateTabNavigationPathPrunedCommand(tab_id, index, count));
 }
 
 void SessionService::TabNavigationPathEntriesDeleted(const SessionID& window_id,
@@ -899,3 +908,18 @@
 sessions::BaseSessionService* SessionService::GetBaseSessionServiceForTest() {
   return base_session_service_.get();
 }
+
+void SessionService::SetAvailableRangeForTest(const SessionID& tab_id,
+                                              const std::pair<int, int> range) {
+  tab_to_available_range_[tab_id] = range;
+}
+
+bool SessionService::GetAvailableRangeForTest(const SessionID& tab_id,
+                                              std::pair<int, int>& range) {
+  auto i = tab_to_available_range_.find(tab_id);
+  if (i == tab_to_available_range_.end())
+    return false;
+
+  range = i->second;
+  return true;
+}
diff --git a/chrome/browser/sessions/session_service.h b/chrome/browser/sessions/session_service.h
index 3c42ba4e..dd63fb9 100644
--- a/chrome/browser/sessions/session_service.h
+++ b/chrome/browser/sessions/session_service.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <string>
+#include <utility>
 
 #include "base/callback.h"
 #include "base/gtest_prod_util.h"
@@ -162,17 +163,13 @@
   void SetWindowAppName(const SessionID& window_id,
                         const std::string& app_name);
 
-  // Invoked when the NavigationController has removed entries from the back of
-  // the list. |count| gives the number of entries in the navigation controller.
-  void TabNavigationPathPrunedFromBack(const SessionID& window_id,
-                                       const SessionID& tab_id,
-                                       int count);
-
-  // Invoked when the NavigationController has removed entries from the front of
-  // the list. |count| gives the number of entries that were removed.
-  void TabNavigationPathPrunedFromFront(const SessionID& window_id,
-                                        const SessionID& tab_id,
-                                        int count);
+  // Invoked when the NavigationController has removed entries from the list.
+  // |index| gives the the starting index from which entries were deleted.
+  // |count| gives the number of entries that were removed.
+  void TabNavigationPathPruned(const SessionID& window_id,
+                               const SessionID& tab_id,
+                               int index,
+                               int count);
 
   // Invoked when the NavigationController has deleted entries because of a
   // history deletion.
@@ -329,6 +326,11 @@
   // Unit test accessors.
   sessions::BaseSessionService* GetBaseSessionServiceForTest();
 
+  void SetAvailableRangeForTest(const SessionID& tab_id,
+                                const std::pair<int, int> range);
+  bool GetAvailableRangeForTest(const SessionID& tab_id,
+                                std::pair<int, int>& range);
+
   // The profile. This may be null during testing.
   Profile* profile_;
 
diff --git a/chrome/browser/sessions/session_service_test_helper.cc b/chrome/browser/sessions/session_service_test_helper.cc
index da3db835..b32e2b5f 100644
--- a/chrome/browser/sessions/session_service_test_helper.cc
+++ b/chrome/browser/sessions/session_service_test_helper.cc
@@ -121,3 +121,14 @@
       service_->GetBaseSessionServiceForTest());
   test_helper.RunTaskOnBackendThread(from_here, task);
 }
+
+void SessionServiceTestHelper::SetAvailableRange(
+    const SessionID& tab_id,
+    const std::pair<int, int> range) {
+  service_->SetAvailableRangeForTest(tab_id, range);
+}
+
+bool SessionServiceTestHelper::GetAvailableRange(const SessionID& tab_id,
+                                                 std::pair<int, int>& range) {
+  return service_->GetAvailableRangeForTest(tab_id, range);
+}
diff --git a/chrome/browser/sessions/session_service_test_helper.h b/chrome/browser/sessions/session_service_test_helper.h
index 5c08752..f2be5ee9 100644
--- a/chrome/browser/sessions/session_service_test_helper.h
+++ b/chrome/browser/sessions/session_service_test_helper.h
@@ -9,6 +9,7 @@
 
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/callback_forward.h"
@@ -83,6 +84,10 @@
   void RunTaskOnBackendThread(const base::Location& from_here,
                               const base::Closure& task);
 
+  void SetAvailableRange(const SessionID& tab_id,
+                         const std::pair<int, int> range);
+  bool GetAvailableRange(const SessionID& tab_id, std::pair<int, int>& range);
+
  private:
   std::unique_ptr<SessionService> service_;
 
diff --git a/chrome/browser/sessions/session_service_unittest.cc b/chrome/browser/sessions/session_service_unittest.cc
index 17b823b..81b2f19c 100644
--- a/chrome/browser/sessions/session_service_unittest.cc
+++ b/chrome/browser/sessions/session_service_unittest.cc
@@ -272,7 +272,17 @@
     nav->set_index(i);
     UpdateNavigation(window_id, tab_id, *nav, true);
   }
-  service()->TabNavigationPathPrunedFromBack(window_id, tab_id, 3);
+
+  // Set available range for testing.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 5));
+
+  service()->TabNavigationPathPruned(window_id, tab_id, 3 /* index */,
+                                     3 /* count */);
+
+  std::pair<int, int> available_range;
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(0, available_range.first);
+  EXPECT_EQ(2, available_range.second);
 
   std::vector<std::unique_ptr<sessions::SessionWindow>> windows;
   ReadWindows(&windows, NULL);
@@ -624,8 +634,17 @@
     UpdateNavigation(window_id, tab_id, nav, (i == 3));
   }
 
+  // Set available range for testing.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 4));
+
   // Prune the first two navigations from the front.
-  helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 2);
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 0 /* index */,
+                                             2 /* count */);
+
+  std::pair<int, int> available_range;
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(0, available_range.first);
+  EXPECT_EQ(2, available_range.second);
 
   // Read back in.
   std::vector<std::unique_ptr<sessions::SessionWindow>> windows;
@@ -651,6 +670,128 @@
               tab->navigations[2].virtual_url());
 }
 
+// Tests pruning from the middle.
+TEST_F(SessionServiceTest, PruneFromMiddle) {
+  const std::string base_url("http://google.com/");
+  SessionID tab_id = SessionID::NewUnique();
+
+  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
+
+  // Add 5 navigations, with the 4th selected.
+  for (int i = 0; i < 5; ++i) {
+    SerializedNavigationEntry nav =
+        SerializedNavigationEntryTestHelper::CreateNavigation(
+            base_url + base::IntToString(i), "a");
+    nav.set_index(i);
+    UpdateNavigation(window_id, tab_id, nav, (i == 3));
+  }
+
+  // Set available range for testing.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 4));
+
+  // Prune two navigations starting from second.
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 1 /* index */,
+                                             2 /* count */);
+
+  std::pair<int, int> available_range;
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(0, available_range.first);
+  EXPECT_EQ(2, available_range.second);
+
+  // Read back in.
+  std::vector<std::unique_ptr<sessions::SessionWindow>> windows;
+  ReadWindows(&windows, nullptr);
+
+  ASSERT_EQ(1U, windows.size());
+  ASSERT_EQ(0, windows[0]->selected_tab_index);
+  ASSERT_EQ(window_id, windows[0]->window_id);
+  ASSERT_EQ(1U, windows[0]->tabs.size());
+
+  // There shouldn't be an app id.
+  EXPECT_TRUE(windows[0]->tabs[0]->extension_app_id.empty());
+
+  // We should be left with three navigations, the 2nd selected.
+  sessions::SessionTab* tab = windows[0]->tabs[0].get();
+  ASSERT_EQ(1, tab->current_navigation_index);
+  EXPECT_EQ(3U, tab->navigations.size());
+  EXPECT_EQ(GURL(base_url + base::IntToString(0)),
+            tab->navigations[0].virtual_url());
+  EXPECT_EQ(GURL(base_url + base::IntToString(3)),
+            tab->navigations[1].virtual_url());
+  EXPECT_EQ(GURL(base_url + base::IntToString(4)),
+            tab->navigations[2].virtual_url());
+}
+
+// Tests possible computations of available ranges.
+TEST_F(SessionServiceTest, AvailableRanges) {
+  const std::string base_url("http://google.com/");
+  SessionID tab_id = SessionID::NewUnique();
+
+  helper_.PrepareTabInWindow(window_id, tab_id, 0, true);
+
+  // Set available range to a subset for testing.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7));
+
+  // 1. Test when range starts after the pruned entries.
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 1 /* index */,
+                                             2 /* count */);
+
+  std::pair<int, int> available_range;
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(2, available_range.first);
+  EXPECT_EQ(5, available_range.second);
+
+  // Set back available range.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7));
+
+  // 2. Test when range is before the pruned entries.
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 8 /* index */,
+                                             2 /* count */);
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(4, available_range.first);
+  EXPECT_EQ(7, available_range.second);
+
+  // Set back available range.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7));
+
+  // 3. Test when range is within the pruned entries.
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 3 /* index */,
+                                             5 /* count */);
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(0, available_range.first);
+  EXPECT_EQ(0, available_range.second);
+
+  // Set back available range.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7));
+
+  // 4. Test when only range.first is within the pruned entries.
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 3 /* index */,
+                                             3 /* count */);
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(3, available_range.first);
+  EXPECT_EQ(4, available_range.second);
+
+  // Set back available range.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7));
+
+  // 4. Test when only range.second is within the pruned entries.
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 5 /* index */,
+                                             3 /* count */);
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(4, available_range.first);
+  EXPECT_EQ(4, available_range.second);
+
+  // Set back available range.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(4, 7));
+
+  // 4. Test when only range contains all the pruned entries.
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 5 /* index */,
+                                             2 /* count */);
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(4, available_range.first);
+  EXPECT_EQ(5, available_range.second);
+}
+
 // Prunes from front so that we have no entries.
 TEST_F(SessionServiceTest, PruneToEmpty) {
   const std::string base_url("http://google.com/");
@@ -667,8 +808,17 @@
     UpdateNavigation(window_id, tab_id, nav, (i == 3));
   }
 
-  // Prune the first two navigations from the front.
-  helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 5);
+  // Set available range for testing.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 4));
+
+  // Prune all navigations from the front.
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 0 /* index */,
+                                             5 /* count */);
+
+  std::pair<int, int> available_range;
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(0, available_range.first);
+  EXPECT_EQ(0, available_range.second);
 
   // Read back in.
   std::vector<std::unique_ptr<sessions::SessionWindow>> windows;
@@ -894,8 +1044,17 @@
     UpdateNavigation(window_id, tab_id, nav, true);
   }
 
+  // Set available range for testing.
+  helper_.SetAvailableRange(tab_id, std::pair<int, int>(0, 4));
+
   // Prune all those navigations.
-  helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 5);
+  helper_.service()->TabNavigationPathPruned(window_id, tab_id, 0 /* index */,
+                                             5 /* count */);
+
+  std::pair<int, int> available_range;
+  EXPECT_TRUE(helper_.GetAvailableRange(tab_id, available_range));
+  EXPECT_EQ(0, available_range.first);
+  EXPECT_EQ(0, available_range.second);
 
   // Add another navigation to replace the last one.
   SerializedNavigationEntry nav =
diff --git a/chrome/browser/sessions/session_tab_helper.cc b/chrome/browser/sessions/session_tab_helper.cc
index 15c87c95..b9deb54 100644
--- a/chrome/browser/sessions/session_tab_helper.cc
+++ b/chrome/browser/sessions/session_tab_helper.cc
@@ -85,14 +85,8 @@
   if (!session_service)
     return;
 
-  if (pruned_details.from_front) {
-    session_service->TabNavigationPathPrunedFromFront(window_id(), session_id(),
-                                                      pruned_details.count);
-  } else {
-    session_service->TabNavigationPathPrunedFromBack(
-        window_id(), session_id(),
-        web_contents()->GetController().GetEntryCount());
-  }
+  session_service->TabNavigationPathPruned(
+      window_id(), session_id(), pruned_details.index, pruned_details.count);
 }
 
 void SessionTabHelper::NavigationEntriesDeleted() {
diff --git a/chrome/browser/signin/dice_browsertest.cc b/chrome/browser/signin/dice_browsertest.cc
index 69d4ece..ae5d13b 100644
--- a/chrome/browser/signin/dice_browsertest.cc
+++ b/chrome/browser/signin/dice_browsertest.cc
@@ -521,7 +521,8 @@
   void OnStartReconcile() override { ++reconcilor_started_count_; }
 
   // identity::IdentityManager::Observer
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override {
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override {
     RunClosureIfValid(std::move(on_primary_account_set_quit_closure_));
   }
 
diff --git a/chrome/browser/signin/identity_manager_factory.cc b/chrome/browser/signin/identity_manager_factory.cc
index 50b899a..741664f 100644
--- a/chrome/browser/signin/identity_manager_factory.cc
+++ b/chrome/browser/signin/identity_manager_factory.cc
@@ -13,6 +13,8 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/core/browser/signin_manager.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator_impl.h"
 #include "services/identity/public/cpp/accounts_mutator.h"
 #include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/primary_account_mutator.h"
@@ -74,7 +76,9 @@
             AccountTrackerServiceFactory::GetForProfile(profile),
             GaiaCookieManagerServiceFactory::GetForProfile(profile),
             BuildPrimaryAccountMutator(profile),
-            BuildAccountsMutator(profile)) {}
+            BuildAccountsMutator(profile),
+            std::make_unique<identity::AccountsCookieMutatorImpl>(
+                GaiaCookieManagerServiceFactory::GetForProfile(profile))) {}
 };
 
 IdentityManagerFactory::IdentityManagerFactory()
diff --git a/chrome/browser/signin/signin_profile_attributes_updater.cc b/chrome/browser/signin/signin_profile_attributes_updater.cc
index 5073663..3232d83 100644
--- a/chrome/browser/signin/signin_profile_attributes_updater.cc
+++ b/chrome/browser/signin/signin_profile_attributes_updater.cc
@@ -80,7 +80,7 @@
 }
 
 void SigninProfileAttributesUpdater::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   UpdateProfileAttributes();
 }
 
diff --git a/chrome/browser/signin/signin_profile_attributes_updater.h b/chrome/browser/signin/signin_profile_attributes_updater.h
index 344508d..a602075 100644
--- a/chrome/browser/signin/signin_profile_attributes_updater.h
+++ b/chrome/browser/signin/signin_profile_attributes_updater.h
@@ -37,7 +37,8 @@
   void OnErrorChanged() override;
 
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 
diff --git a/chrome/browser/signin/signin_ui_util.cc b/chrome/browser/signin/signin_ui_util.cc
index 70bc277..498d99e 100644
--- a/chrome/browser/signin/signin_ui_util.cc
+++ b/chrome/browser/signin/signin_ui_util.cc
@@ -15,13 +15,11 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/common/pref_names.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/identity_utils.h"
 #include "components/signin/core/browser/signin_pref_names.h"
diff --git a/chrome/browser/spellchecker/spellcheck_factory.cc b/chrome/browser/spellchecker/spellcheck_factory.cc
index 173df9f..a982b2ee 100644
--- a/chrome/browser/spellchecker/spellcheck_factory.cc
+++ b/chrome/browser/spellchecker/spellcheck_factory.cc
@@ -67,10 +67,9 @@
 
 void SpellcheckServiceFactory::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* user_prefs) {
-  user_prefs->RegisterListPref(spellcheck::prefs::kSpellCheckDictionaries,
-                               std::make_unique<base::ListValue>());
-  user_prefs->RegisterListPref(spellcheck::prefs::kSpellCheckForcedDictionaries,
-                               std::make_unique<base::ListValue>());
+  user_prefs->RegisterListPref(spellcheck::prefs::kSpellCheckDictionaries);
+  user_prefs->RegisterListPref(
+      spellcheck::prefs::kSpellCheckForcedDictionaries);
   // Continue registering kSpellCheckDictionary for preference migration.
   // TODO(estade): remove: crbug.com/751275
   user_prefs->RegisterStringPref(
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
index ddf0df4..1b12006b 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
@@ -91,11 +91,7 @@
       url_loader_factory_(std::move(url_loader_factory)),
       access_token_expired_(false) {}
 
-FamilyInfoFetcher::~FamilyInfoFetcher() {
-  // Ensures IdentityManager observation is cleared when FamilyInfoFetcher is
-  // destructed before refresh token is available.
-  identity_manager_->RemoveObserver(this);
-}
+FamilyInfoFetcher::~FamilyInfoFetcher() {}
 
 // static
 std::string FamilyInfoFetcher::RoleToString(FamilyMemberRole role) {
@@ -117,42 +113,22 @@
 
 void FamilyInfoFetcher::StartGetFamilyProfile() {
   request_path_ = kGetFamilyProfileApiPath;
-  StartFetching();
+  StartFetchingAccessToken();
 }
 
 void FamilyInfoFetcher::StartGetFamilyMembers() {
   request_path_ = kGetFamilyMembersApiPath;
-  StartFetching();
-}
-
-void FamilyInfoFetcher::StartFetching() {
-  if (identity_manager_->HasAccountWithRefreshToken(primary_account_id_)) {
-    StartFetchingAccessToken();
-  } else {
-    // Wait until we get a refresh token.
-    identity_manager_->AddObserver(this);
-  }
+  StartFetchingAccessToken();
 }
 
 void FamilyInfoFetcher::StartFetchingAccessToken() {
   OAuth2TokenService::ScopeSet scopes{kScope};
-  access_token_fetcher_ =
-      std::make_unique<identity::PrimaryAccountAccessTokenFetcher>(
-          "family_info_fetcher", identity_manager_, scopes,
-          base::BindOnce(&FamilyInfoFetcher::OnAccessTokenFetchComplete,
-                         base::Unretained(this)),
-          identity::PrimaryAccountAccessTokenFetcher::Mode::kImmediate);
-}
-
-void FamilyInfoFetcher::OnRefreshTokenUpdatedForAccount(
-    const AccountInfo& account_info) {
-  // Wait until we get a refresh token for the requested account.
-  if (account_info.account_id != primary_account_id_)
-    return;
-
-  identity_manager_->RemoveObserver(this);
-
-  StartFetchingAccessToken();
+  access_token_fetcher_ = std::make_unique<
+      identity::PrimaryAccountAccessTokenFetcher>(
+      "family_info_fetcher", identity_manager_, scopes,
+      base::BindOnce(&FamilyInfoFetcher::OnAccessTokenFetchComplete,
+                     base::Unretained(this)),
+      identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
 }
 
 void FamilyInfoFetcher::OnAccessTokenFetchComplete(
@@ -243,7 +219,7 @@
     scopes.insert(kScope);
     identity_manager_->RemoveAccessTokenFromCache(primary_account_id_, scopes,
                                                   access_token_);
-    StartFetching();
+    StartFetchingAccessToken();
     return;
   }
 
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
index b6ff1cd..b1229b0 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
@@ -31,7 +31,7 @@
 // Fetches information about the family of the signed-in user. It can get
 // information about the family itself (e.g. a name), as well as a list of
 // family members and their properties.
-class FamilyInfoFetcher : public identity::IdentityManager::Observer {
+class FamilyInfoFetcher {
  public:
   enum ErrorCode {
     TOKEN_ERROR,    // Failed to get OAuth2 token.
@@ -86,7 +86,7 @@
       Consumer* consumer,
       identity::IdentityManager* identity_manager,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-  ~FamilyInfoFetcher() override;
+  ~FamilyInfoFetcher();
 
   // Public so tests can use them.
   static std::string RoleToString(FamilyMemberRole role);
@@ -103,10 +103,6 @@
                                       const std::string& response_body);
 
  private:
-  // IdentityManager::Observer implementation:
-  void OnRefreshTokenUpdatedForAccount(
-      const AccountInfo& account_info) override;
-
   void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
                                   identity::AccessTokenInfo access_token_info);
 
diff --git a/chrome/browser/supervised_user/legacy/custodian_profile_downloader_service_factory.cc b/chrome/browser/supervised_user/legacy/custodian_profile_downloader_service_factory.cc
index 32e565d23..d715212 100644
--- a/chrome/browser/supervised_user/legacy/custodian_profile_downloader_service_factory.cc
+++ b/chrome/browser/supervised_user/legacy/custodian_profile_downloader_service_factory.cc
@@ -5,8 +5,7 @@
 #include "chrome/browser/supervised_user/legacy/custodian_profile_downloader_service_factory.h"
 
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/supervised_user/legacy/custodian_profile_downloader_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 
@@ -30,8 +29,7 @@
           "CustodianProfileDownloaderService",
           BrowserContextDependencyManager::GetInstance()) {
   // Indirect dependency via ProfileDownloader.
-  DependsOn(ProfileOAuth2TokenServiceFactory::GetInstance());
-  DependsOn(SigninManagerFactory::GetInstance());
+  DependsOn(IdentityManagerFactory::GetInstance());
 }
 
 CustodianProfileDownloaderServiceFactory::
diff --git a/chrome/browser/sync/session_sync_service_factory.cc b/chrome/browser/sync/session_sync_service_factory.cc
index a1693ca..b4fb776 100644
--- a/chrome/browser/sync/session_sync_service_factory.cc
+++ b/chrome/browser/sync/session_sync_service_factory.cc
@@ -17,7 +17,6 @@
 #include "chrome/browser/ui/sync/browser_synced_window_delegates_getter.h"
 #include "chrome/common/channel_info.h"
 #include "chrome/common/url_constants.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/sync/device_info/device_info_sync_service.h"
 #include "components/sync/device_info/local_device_info_provider.h"
diff --git a/chrome/browser/sync/sync_error_notifier_ash.cc b/chrome/browser/sync/sync_error_notifier_ash.cc
index a1e589c..168b9ab 100644
--- a/chrome/browser/sync/sync_error_notifier_ash.cc
+++ b/chrome/browser/sync/sync_error_notifier_ash.cc
@@ -25,7 +25,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/account_id/account_id.h"
-#include "components/browser_sync/profile_sync_service.h"
+#include "components/sync/driver/sync_service.h"
 #include "components/user_manager/user_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
diff --git a/chrome/browser/sync/test/integration/autofill_helper.cc b/chrome/browser/sync/test/integration/autofill_helper.cc
index bc375e8..9684611 100644
--- a/chrome/browser/sync/test/integration/autofill_helper.cc
+++ b/chrome/browser/sync/test/integration/autofill_helper.cc
@@ -28,7 +28,6 @@
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
 #include "components/autofill/core/common/form_field_data.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/webdata/common/web_database.h"
 
 using autofill::AutofillChangeList;
diff --git a/chrome/browser/sync/test/integration/passwords_helper.h b/chrome/browser/sync/test/integration/passwords_helper.h
index 2c407bc..9846cfd5 100644
--- a/chrome/browser/sync/test/integration/passwords_helper.h
+++ b/chrome/browser/sync/test/integration/passwords_helper.h
@@ -14,7 +14,6 @@
 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
 #include "chrome/browser/sync/test/integration/sync_test.h"
 #include "components/autofill/core/common/password_form.h"
-#include "components/browser_sync/profile_sync_service.h"
 
 namespace password_manager {
 class PasswordStore;
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index cdbc96e..e09a3a67 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -494,6 +494,83 @@
   ASSERT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex, title));
 }
 
+// Legacy bookmark clients append a blank space to empty titles. This tests that
+// this is respected when merging local and remote hierarchies.
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
+                       ShouldTruncateBlanksWhenMatchingTitles) {
+  const std::string remote_blank_title = " ";
+  const std::string local_empty_title = "";
+
+  // Create a folder on the server under BookmarkBar with a title with a blank
+  // space.
+  fake_server::EntityBuilderFactory entity_builder_factory;
+  fake_server::BookmarkEntityBuilder bookmark_builder =
+      entity_builder_factory.NewBookmarkEntityBuilder(remote_blank_title);
+  fake_server_->InjectEntity(bookmark_builder.BuildFolder());
+
+  DisableVerifier();
+  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+
+  // Create a folder on the client under BookmarkBar with an empty title.
+  const BookmarkNode* node =
+      AddFolder(kSingleProfileIndex, GetBookmarkBarNode(kSingleProfileIndex), 0,
+                local_empty_title);
+  ASSERT_TRUE(node);
+  ASSERT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                              local_empty_title));
+
+  ASSERT_TRUE(SetupSync());
+  // There should be only one bookmark on the client. The remote node should
+  // have been merged with the local node and either the local or remote titles
+  // is picked.
+  EXPECT_EQ(1, CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                              local_empty_title) +
+                   CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                                  remote_blank_title));
+}
+
+// Legacy bookmark clients truncate long titles up to 255 bytes. This tests that
+// this is respected when merging local and remote hierarchies.
+IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
+                       ShouldTruncateLongTitles) {
+  const std::string remote_truncated_title =
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst"
+      "uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"
+      "OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
+      "ijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU";
+  const std::string local_full_title =
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst"
+      "uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"
+      "OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
+      "ijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB"
+      "CDEFGHIJKLMNOPQRSTUVWXYZ";
+
+  // Create a folder on the server under BookmarkBar with a truncated title.
+  fake_server::EntityBuilderFactory entity_builder_factory;
+  fake_server::BookmarkEntityBuilder bookmark_builder =
+      entity_builder_factory.NewBookmarkEntityBuilder(remote_truncated_title);
+  fake_server_->InjectEntity(bookmark_builder.BuildFolder());
+
+  DisableVerifier();
+  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
+  // Create a folder on the client under BookmarkBar with a long title.
+  const BookmarkNode* node =
+      AddFolder(kSingleProfileIndex, GetBookmarkBarNode(kSingleProfileIndex), 0,
+                local_full_title);
+  ASSERT_TRUE(node);
+  ASSERT_EQ(
+      1, CountFoldersWithTitlesMatching(kSingleProfileIndex, local_full_title));
+
+  ASSERT_TRUE(SetupSync());
+  // There should be only one bookmark on the client. The remote node should
+  // have been merged with the local node and either the local or remote title
+  // is picked.
+  EXPECT_EQ(
+      1, CountFoldersWithTitlesMatching(kSingleProfileIndex, local_full_title) +
+             CountFoldersWithTitlesMatching(kSingleProfileIndex,
+                                            remote_truncated_title));
+}
+
 IN_PROC_BROWSER_TEST_P(SingleClientBookmarksSyncTest,
                        DownloadBookmarkFoldersWithPositions) {
   const std::string title0 = "Folder left";
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine.cc b/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
index dde2a9d4..621ca3d 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
@@ -709,7 +709,8 @@
                      "Failed to sign in.");
 }
 
-void SyncEngine::OnPrimaryAccountSet(const AccountInfo& primary_account_info) {
+void SyncEngine::OnPrimaryAccountSet(
+    const CoreAccountInfo& primary_account_info) {
   Initialize();
 }
 
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_engine.h b/chrome/browser/sync_file_system/drive_backend/sync_engine.h
index 74eb25a..4d5b4d76 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine.h
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine.h
@@ -147,7 +147,8 @@
   void OnConnectionChanged(network::mojom::ConnectionType type) override;
 
   // IdentityManager::Observer overrides.
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
   void OnPrimaryAccountSigninFailed(
diff --git a/chrome/browser/ui/signin_view_controller.cc b/chrome/browser/ui/signin_view_controller.cc
index d38a50c..2e00ae9 100644
--- a/chrome/browser/ui/signin_view_controller.cc
+++ b/chrome/browser/ui/signin_view_controller.cc
@@ -167,11 +167,6 @@
     delegate_->ResizeNativeView(height);
 }
 
-void SigninViewController::PerformNavigation() {
-  if (delegate_)
-    delegate_->PerformNavigation();
-}
-
 void SigninViewController::ResetModalSigninDelegate() {
   delegate_ = nullptr;
 }
diff --git a/chrome/browser/ui/signin_view_controller.h b/chrome/browser/ui/signin_view_controller.h
index d067ed1f..3dcd2a3 100644
--- a/chrome/browser/ui/signin_view_controller.h
+++ b/chrome/browser/ui/signin_view_controller.h
@@ -81,10 +81,6 @@
   // Sets the height of the modal signin dialog.
   void SetModalSigninHeight(int height);
 
-  // Either navigates back in the signin flow if the history state allows it or
-  // closes the flow otherwise.
-  // Does nothing if the signin flow does not exist.
-  void PerformNavigation();
 
   // Notifies this object that it's |delegate_| member has become invalid.
   void ResetModalSigninDelegate();
diff --git a/chrome/browser/ui/signin_view_controller_delegate.h b/chrome/browser/ui/signin_view_controller_delegate.h
index 6df28e9..1cc63ca 100644
--- a/chrome/browser/ui/signin_view_controller_delegate.h
+++ b/chrome/browser/ui/signin_view_controller_delegate.h
@@ -44,12 +44,6 @@
   // so the caller should no longer use this object after calling this method.
   virtual void CloseModalSignin() = 0;
 
-  // Either navigates back in the signin flow if the history state allows it or
-  // closes the flow otherwise. Note that if view is closed, this method may
-  // destroy this object, so the caller should no longer use this object after
-  // calling this method.
-  virtual void PerformNavigation() = 0;
-
   // This will be called by the base class to request a resize of the native
   // view hosting the content to |height|. |height| is the total height of the
   // content, in pixels.
diff --git a/chrome/browser/ui/views/payments/empty_update_browsertest.cc b/chrome/browser/ui/views/payments/empty_update_browsertest.cc
index 4c16f1c..2763206 100644
--- a/chrome/browser/ui/views/payments/empty_update_browsertest.cc
+++ b/chrome/browser/ui/views/payments/empty_update_browsertest.cc
@@ -25,8 +25,9 @@
   InvokePaymentRequestUI();
   OpenShippingAddressSectionScreen();
 
-  ResetEventWaiterForSequence(
-      {DialogEvent::PROCESSING_SPINNER_SHOWN, DialogEvent::DIALOG_CLOSED});
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
+                               DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::SPEC_DONE_UPDATING});
 
   ClickOnChildInListViewAndWait(
       /* child_index=*/0, /*total_num_children=*/1,
diff --git a/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc
index 159e790..7d60f96 100644
--- a/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc
@@ -30,8 +30,7 @@
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestUpdateWithTest);
 };
 
-IN_PROC_BROWSER_TEST_F(PaymentRequestUpdateWithTest,
-                       UpdateWithNoShippingOptions) {
+IN_PROC_BROWSER_TEST_F(PaymentRequestUpdateWithTest, UpdateWithEmpty) {
   NavigateTo("/payment_request_update_with_test.html");
   autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
   AddAutofillProfile(billing_address);
@@ -40,7 +39,48 @@
   card.set_billing_address_id(billing_address.guid());
   AddCreditCard(card);
 
-  RunJavaScriptFunctionToOpenPaymentRequestUI("updateWithNoShippingOptions");
+  RunJavaScriptFunctionToOpenPaymentRequestUI("updateWithEmpty");
+
+  OpenOrderSummaryScreen();
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  ClickOnBackArrow();
+
+  OpenShippingAddressSectionScreen();
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
+                               DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::SPEC_DONE_UPDATING,
+                               DialogEvent::BACK_NAVIGATION});
+  ClickOnChildInListViewAndWait(
+      /* child_index=*/1, /*total_num_children=*/2,
+      DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW,
+      /*wait_for_animation=*/false);
+  // Wait for the animation here explicitly, otherwise
+  // ClickOnChildInListViewAndWait tries to install an AnimationDelegate before
+  // the animation is kicked off (since that's triggered off of the spec being
+  // updated) and this hits a DCHECK.
+  WaitForAnimation();
+
+  OpenOrderSummaryScreen();
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  ClickOnBackArrow();
+
+  PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
+
+  ExpectBodyContains({"freeShipping"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestUpdateWithTest, UpdateWithTotal) {
+  NavigateTo("/payment_request_update_with_test.html");
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);
+
+  RunJavaScriptFunctionToOpenPaymentRequestUI("updateWithTotal");
 
   OpenOrderSummaryScreen();
   EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
@@ -105,7 +145,7 @@
   WaitForAnimation();
 
   OpenOrderSummaryScreen();
-  EXPECT_EQ(base::ASCIIToUTF16("$10.00"),
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
             GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
   ClickOnBackArrow();
 
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
index cd4002b..37d031c 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.cc
@@ -23,17 +23,6 @@
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/widget/widget.h"
 
-#include "chrome/browser/ui/webui/signin/signin_utils.h"
-
-namespace {
-
-content::WebContents* GetAuthFrameWebContents(
-    content::WebContents* web_ui_web_contents) {
-  return signin::GetAuthFrameWebContents(web_ui_web_contents, "signin-frame");
-}
-
-}  // namespace
-
 namespace {
 
 const int kModalDialogWidth = 448;
@@ -116,11 +105,6 @@
   return ui::DIALOG_BUTTON_NONE;
 }
 
-void SigninViewControllerDelegateViews::PerformClose() {
-  if (modal_signin_widget_)
-    modal_signin_widget_->Close();
-}
-
 void SigninViewControllerDelegateViews::ResizeNativeView(int height) {
   int max_height = browser()
                        ->window()
@@ -141,16 +125,10 @@
   return web_contents_;
 }
 
-void SigninViewControllerDelegateViews::PerformNavigation() {
-  if (CanGoBack(web_contents_))
-    GetAuthFrameWebContents(web_contents_)->GetController().GoBack();
-  else
-    CloseModalSignin();
-}
-
 void SigninViewControllerDelegateViews::CloseModalSignin() {
   ResetSigninViewControllerDelegate();
-  PerformClose();
+  if (modal_signin_widget_)
+    modal_signin_widget_->Close();
 }
 
 void SigninViewControllerDelegateViews::DisplayModal() {
@@ -200,23 +178,6 @@
   return true;
 }
 
-void SigninViewControllerDelegateViews::LoadingStateChanged(
-    content::WebContents* source,
-    bool to_different_document) {
-  // The WebUI object can be missing for an error page, per
-  // https://crbug.com/860409.
-  if (!source->GetWebUI())
-    return;
-
-  if (CanGoBack(source)) {
-    source->GetWebUI()->CallJavascriptFunctionUnsafe(
-        "inline.login.showBackButton");
-  } else {
-    source->GetWebUI()->CallJavascriptFunctionUnsafe(
-        "inline.login.showCloseButton");
-  }
-}
-
 std::unique_ptr<views::WebView>
 SigninViewControllerDelegateViews::CreateSyncConfirmationWebView(
     Browser* browser) {
@@ -271,12 +232,6 @@
   }
 }
 
-bool SigninViewControllerDelegateViews::CanGoBack(
-    content::WebContents* web_ui_web_contents) const {
-  auto* auth_web_contents = GetAuthFrameWebContents(web_ui_web_contents);
-  return auth_web_contents && auth_web_contents->GetController().CanGoBack();
-}
-
 // --------------------------------------------------------------------
 // SigninViewControllerDelegate static methods
 // --------------------------------------------------------------------
diff --git a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
index d2e43be..474d251 100644
--- a/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
+++ b/chrome/browser/ui/views/profiles/signin_view_controller_delegate_views.h
@@ -49,13 +49,10 @@
 
   // SigninViewControllerDelegate:
   void CloseModalSignin() override;
-  void PerformNavigation() override;
   void ResizeNativeView(int height) override;
   content::WebContents* GetWebContents() override;
 
   // content::WebContentsDelegate:
-  void LoadingStateChanged(content::WebContents* source,
-                           bool to_different_document) override;
   bool HandleContextMenu(const content::ContextMenuParams& params) override;
   bool HandleKeyboardEvent(
       content::WebContents* source,
@@ -92,14 +89,6 @@
   // Displays the modal dialog.
   void DisplayModal();
 
-  // Returns true if |web_ui_web_contents| can go back.
-  bool CanGoBack(content::WebContents* web_ui_web_contents) const;
-
-  // Close the platform-specific dialog window. Note that this
-  // method may destroy this object, so the caller should no longer use this
-  // object after calling this method.
-  void PerformClose();
-
   Browser* browser() { return browser_; }
 
   SigninViewController* signin_view_controller_;  // Not owned.
diff --git a/chrome/browser/ui/webui/app_management/app_management_ui.cc b/chrome/browser/ui/webui/app_management/app_management_ui.cc
index 7aa8e93..6961c64 100644
--- a/chrome/browser/ui/webui/app_management/app_management_ui.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_ui.cc
@@ -37,6 +37,7 @@
   source->AddLocalizedString("location", IDS_APP_MANAGEMENT_LOCATION);
   source->AddLocalizedString("microphone", IDS_APP_MANAGEMENT_MICROPHONE);
   source->AddLocalizedString("moreApps", IDS_APP_MANAGEMENT_MORE_APPS);
+  source->AddLocalizedString("noSearchResults", IDS_APP_MANAGEMENT_NO_RESULTS);
   source->AddLocalizedString("notificationSublabel",
                              IDS_APP_MANAGEMENT_NOTIFICATIONS_SUBLABEL);
   source->AddLocalizedString("notifications", IDS_APP_MANAGEMENT_NOTIFICATIONS);
@@ -120,6 +121,9 @@
   source->AddResourcePath("reducers.js", IDR_APP_MANAGEMENT_REDUCERS_JS);
   source->AddResourcePath("router.html", IDR_APP_MANAGEMENT_ROUTER_HTML);
   source->AddResourcePath("router.js", IDR_APP_MANAGEMENT_ROUTER_JS);
+  source->AddResourcePath("search_view.html",
+                          IDR_APP_MANAGEMENT_SEARCH_VIEW_HTML);
+  source->AddResourcePath("search_view.js", IDR_APP_MANAGEMENT_SEARCH_VIEW_JS);
   source->AddResourcePath("shared_style.html",
                           IDR_APP_MANAGEMENT_SHARED_STYLE_HTML);
   source->AddResourcePath("shared_vars.html",
diff --git a/chrome/browser/ui/webui/browsing_history_handler.cc b/chrome/browser/ui/webui/browsing_history_handler.cc
index 85386327..b9e63f7 100644
--- a/chrome/browser/ui/webui/browsing_history_handler.cc
+++ b/chrome/browser/ui/webui/browsing_history_handler.cc
@@ -33,7 +33,6 @@
 #include "chrome/common/pref_names.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/favicon/core/fallback_url_util.h"
 #include "components/favicon/core/large_icon_service.h"
 #include "components/keyed_service/core/service_access_type.h"
@@ -43,6 +42,7 @@
 #include "components/sync/device_info/device_info.h"
 #include "components/sync/device_info/device_info_sync_service.h"
 #include "components/sync/device_info/device_info_tracker.h"
+#include "components/sync/driver/sync_service.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_ui.h"
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
index 5b0dce2..17a7e80 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.cc
@@ -416,7 +416,7 @@
 }
 
 void LocalDiscoveryUIHandler::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   CheckUserLoggedIn();
 }
 
diff --git a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h
index 935fa1ee..cd90069 100644
--- a/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h
+++ b/chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h
@@ -91,7 +91,8 @@
   void OnDeviceListUnavailable() override;
 
   // identity::IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index a745350b..439d019 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -36,7 +36,6 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/autofill/core/common/autofill_features.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/google/core/common/google_util.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/password_manager/core/browser/manage_passwords_referrer.h"
@@ -45,7 +44,9 @@
 #include "components/signin/core/browser/signin_buildflags.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
+#include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_service_utils.h"
+#include "components/sync/driver/sync_user_settings.h"
 #include "components/unified_consent/feature.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/common/content_features.h"
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index a48662a8..c5fe1773 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -862,7 +862,7 @@
 }
 
 void PeopleHandler::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   UpdateSyncStatus();
 }
 
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h
index 37e54023..1bdb3f4 100644
--- a/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -126,7 +126,8 @@
   void FocusUI() override;
 
   // IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler.cc b/chrome/browser/ui/webui/signin/inline_login_handler.cc
index 6cd2376..d3bb605 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler.cc
@@ -33,10 +33,6 @@
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/base/url_util.h"
 
-#if !defined(OS_CHROMEOS)
-#include "chrome/browser/ui/signin_view_controller.h"
-#endif
-
 const char kSignInPromoQueryKeyShowAccountManagement[] =
     "showAccountManagement";
 
@@ -236,20 +232,13 @@
 void InlineLoginHandler::HandleNavigationButtonClicked(
     const base::ListValue* args) {
 #if !defined(OS_CHROMEOS)
-  Browser* browser = signin::GetDesktopBrowser(web_ui());
-  DCHECK(browser);
-
-  browser->signin_view_controller()->PerformNavigation();
+  NOTREACHED() << "The inline login handler is no longer used in a browser "
+                  "or tab modal dialog.";
 #endif
 }
 
 void InlineLoginHandler::HandleDialogClose(const base::ListValue* args) {
 #if !defined(OS_CHROMEOS)
-  Browser* browser = signin::GetDesktopBrowser(web_ui());
-  // If the dialog was opened in the User Manager browser will be null here.
-  if (browser)
-    browser->signin_view_controller()->CloseModalSignin();
-
   // Does nothing if user manager is not showing.
   UserManagerProfileDialog::HideDialog();
 #endif  // !defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
index 97497aa..d94313c 100644
--- a/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
+++ b/chrome/browser/ui/webui/signin/inline_login_handler_impl.cc
@@ -54,7 +54,6 @@
 #include "chrome/browser/ui/webui/signin/signin_utils_desktop.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/chromium_strings.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
 #include "components/password_manager/core/browser/password_store.h"
 #include "components/prefs/pref_service.h"
diff --git a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
index 8566699..71a622f 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -71,7 +71,8 @@
     QuitLoopRunner();
   }
 
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override {
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override {
     DVLOG(1) << "Google signin succeeded.";
     signed_in_ = true;
     QuitLoopRunner();
diff --git a/chrome/browser/ui/webui/sync_internals_message_handler.cc b/chrome/browser/ui/webui/sync_internals_message_handler.cc
index d738d8c..8ca9d47 100644
--- a/chrome/browser/ui/webui/sync_internals_message_handler.cc
+++ b/chrome/browser/ui/webui/sync_internals_message_handler.cc
@@ -16,11 +16,11 @@
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/user_event_service_factory.h"
 #include "chrome/common/channel_info.h"
-#include "components/browser_sync/profile_sync_service.h"
 #include "components/sync/base/model_type.h"
 #include "components/sync/driver/about_sync_util.h"
 #include "components/sync/driver/sync_driver_switches.h"
 #include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_user_settings.h"
 #include "components/sync/engine/cycle/commit_counters.h"
 #include "components/sync/engine/cycle/status_counters.h"
 #include "components/sync/engine/cycle/update_counters.h"
diff --git a/chrome/common/extensions/api/passwords_private.idl b/chrome/common/extensions/api/passwords_private.idl
index 4d63cf9..e83f1e5 100644
--- a/chrome/common/extensions/api/passwords_private.idl
+++ b/chrome/common/extensions/api/passwords_private.idl
@@ -29,18 +29,13 @@
     DOMString link;
   };
 
-  // Pair of a URL collection and a username saved for these URLs.
-  dictionary LoginPair {
+  // Entry used to display a password in the settings UI.
+  dictionary PasswordUiEntry {
+    // The URL collection corresponding to this saved password entry.
     UrlCollection urls;
 
     // The username used in conjunction with the saved password.
     DOMString username;
-  };
-
-  // Entry used to display a password in the settings UI.
-  dictionary PasswordUiEntry {
-    // The login information for this entry.
-    LoginPair loginPair;
 
     // The number of characters in the password; used to display placeholder
     // dots in the UI.
@@ -54,6 +49,7 @@
   };
 
   dictionary ExceptionEntry {
+    // The URL collection corresponding to this exception entry.
     UrlCollection urls;
 
     // An id to refer back to a unique exception entry record.
@@ -88,9 +84,9 @@
                                     DOMString new_username,
                                     optional DOMString new_password);
 
-    // Removes the saved password corresponding to |loginPair|. If no saved
-    // password for this pair exists, this function is a no-op.
-    // |id|: The id for the password entry being removed.
+    // Removes the saved password corresponding to |id|. If no saved password
+    // for this pair exists, this function is a no-op. |id|: The id for the
+    // password entry being removed.
     static void removeSavedPassword(long id);
 
     // Removes the saved password exception corresponding to |exceptionUrl|. If
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
index 8cc6480d..942e0ad8 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc
@@ -533,15 +533,16 @@
 CGaiaCredentialBase::~CGaiaCredentialBase() {}
 
 bool CGaiaCredentialBase::AreCredentialsValid() const {
-  return AreWindowsCredentialsAvailable() &&
-         AreWindowsCredentialsValid(password_) == S_OK;
+  return CanAttemptWindowsLogon() &&
+         IsWindowsPasswordValidForStoredUser(password_) == S_OK;
 }
 
-bool CGaiaCredentialBase::AreWindowsCredentialsAvailable() const {
+bool CGaiaCredentialBase::CanAttemptWindowsLogon() const {
   return username_.Length() > 0 && password_.Length() > 0;
 }
 
-HRESULT CGaiaCredentialBase::AreWindowsCredentialsValid(BSTR password) const {
+HRESULT CGaiaCredentialBase::IsWindowsPasswordValidForStoredUser(
+    BSTR password) const {
   if (username_.Length() == 0 || user_sid_.Length() == 0)
     return S_FALSE;
 
@@ -588,6 +589,8 @@
   password_.Empty();
   current_windows_password_.Empty();
   authentication_results_.reset();
+  needs_to_update_windows_password_ = false;
+  needs_windows_password_ = false;
 
   // Don't reset user_sid_ or username_ as those are set for existing gaia
   // users in CReauthCredential::SetGaiaUserInfo so that the user account
@@ -608,6 +611,7 @@
                             current_windows_password_);
     events_->SetFieldState(this, FID_SUBMIT, CPFS_DISPLAY_IN_SELECTED_TILE);
     events_->SetFieldSubmitButton(this, FID_SUBMIT, FID_DESCRIPTION);
+    UpdateSubmitButtonInteractiveState();
   }
 }
 
@@ -718,63 +722,54 @@
   DCHECK(cpgsr);
   DCHECK(cpcs);
 
-  if (!AreWindowsCredentialsAvailable())
+  if (!CanAttemptWindowsLogon())
     return S_FALSE;
 
   // If the credentials are not valid, check if the user entered their old
   // Windows password and it is valid. If it is, try to change the password
   // using the old password. If it isn't, return S_FALSE to state that the
   // login is not complete.
-  if (!AreCredentialsValid()) {
+  if (needs_windows_password_) {
     HRESULT windows_cred_hr =
-        AreWindowsCredentialsValid(current_windows_password_);
+        IsWindowsPasswordValidForStoredUser(current_windows_password_);
     if (windows_cred_hr == S_OK) {
-      OSUserManager* manager = OSUserManager::Get();
-      HRESULT changepassword_hr = manager->ChangeUserPassword(
-          username_, current_windows_password_, password_);
-      if (FAILED(changepassword_hr)) {
-        if (changepassword_hr != HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
-          LOGFN(ERROR) << "ChangeUserPassword hr=" << putHR(changepassword_hr);
-          return changepassword_hr;
+      if (needs_to_update_windows_password_) {
+        OSUserManager* manager = OSUserManager::Get();
+        HRESULT changepassword_hr = manager->ChangeUserPassword(
+            username_, current_windows_password_, password_);
+        if (FAILED(changepassword_hr)) {
+          if (changepassword_hr != HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)) {
+            LOGFN(ERROR) << "ChangeUserPassword hr="
+                         << putHR(changepassword_hr);
+            return changepassword_hr;
+          }
+          LOGFN(ERROR) << "Access was denied to ChangeUserPassword.";
+          password_ = current_windows_password_;
         }
-        LOGFN(ERROR) << "Access was denied to ChangeUserPassword.";
+      } else {
         password_ = current_windows_password_;
       }
     } else {
-      if (events_) {
-        int message_id = IDS_INVALID_PASSWORD_BASE;
+      if (current_windows_password_.Length() && events_) {
+        UINT pasword_message_id = IDS_INVALID_PASSWORD_BASE;
         if (windows_cred_hr == HRESULT_FROM_WIN32(ERROR_ACCOUNT_LOCKED_OUT)) {
-          message_id = IDS_ACCOUNT_LOCKED_BASE;
+          pasword_message_id = IDS_ACCOUNT_LOCKED_BASE;
           LOGFN(ERROR) << "Account is locked.";
         }
 
-        events_->SetFieldState(this, FID_DESCRIPTION,
-                               CPFS_DISPLAY_IN_SELECTED_TILE);
         events_->SetFieldString(this, FID_DESCRIPTION,
-                                GetStringResource(message_id).c_str());
+                                GetStringResource(pasword_message_id).c_str());
+        events_->SetFieldInteractiveState(this, FID_CURRENT_PASSWORD_FIELD,
+                                          CPFIS_FOCUSED);
       }
       return S_FALSE;
     }
   }
 
-  // At this point the user and password stored in authentication_results_
-  // should match what is stored in username_ and password_ so the
-  // SaveAccountInfo process can be forked.
-  //
-  // The successful return from this function will tell winlogon that
-  // logging in is finished. It seems that winlogon will kill this process
-  // after a short time, which races with an attempt to save the account info
-  // to the registry if done here.  For this reason a child pocess is used.
-  CComBSTR status_text;
-  HRESULT hr = ForkSaveAccountInfoStub(authentication_results_, &status_text);
-  authentication_results_.reset();
-
-  if (FAILED(hr))
-    LOGFN(ERROR) << "ForkSaveAccountInfoStub hr=" << putHR(hr);
-
   // The OS user has already been created, so return all the information
   // needed to log them in.
-  hr = BuildCredPackAuthenticationBuffer(get_username(), get_password(), cpcs);
+  HRESULT hr =
+      BuildCredPackAuthenticationBuffer(get_username(), get_password(), cpcs);
   if (FAILED(hr)) {
     LOGFN(ERROR) << "BuildCredPackAuthenticationBuffer hr=" << putHR(hr);
     return hr;
@@ -860,7 +855,7 @@
 }
 
 HRESULT CGaiaCredentialBase::SetSelected(BOOL* auto_login) {
-  *auto_login = AreCredentialsValid();
+  *auto_login = CanAttemptWindowsLogon();
   LOGFN(INFO) << "auto-login=" << *auto_login;
 
   // After this point the user is able to interact with the winlogon and thus
@@ -879,7 +874,6 @@
   // Whenever a different user is selected and then the original credential
   // is selected again, the password is cleared.
   ResetInternalState();
-  TerminateLogonProcess();
 
   return S_OK;
 }
@@ -984,7 +978,10 @@
   HRESULT hr = E_INVALIDARG;
   switch (field_id) {
     case FID_CURRENT_PASSWORD_FIELD:
-      current_windows_password_ = W2COLE(psz);
+      if (needs_windows_password_) {
+        current_windows_password_ = W2COLE(psz);
+        UpdateSubmitButtonInteractiveState();
+      }
       hr = S_OK;
       break;
   }
@@ -1020,61 +1017,87 @@
   *status_text = nullptr;
   *status_icon = CPSI_NONE;
 
-  HRESULT hr = HandleAutologon(cpgsr, cpcs);
-
-  // Clear the state of the credential on error or on autologon.
-  if (hr != S_FALSE)
-    ResetInternalState();
-
-  if (FAILED(hr)) {
-    LOGFN(ERROR) << "HandleAutologon hr=" << putHR(hr);
-    *cpgsr = CPGSR_RETURN_NO_CREDENTIAL_FINISHED;
-    return hr;
+  // This may be a long running function so disable user input while processing.
+  if (events_) {
+    events_->SetFieldInteractiveState(this, FID_SUBMIT, CPFIS_DISABLED);
+    events_->SetFieldInteractiveState(this, FID_CURRENT_PASSWORD_FIELD,
+                                      CPFIS_DISABLED);
   }
 
-  // If HandleAutologon returns S_FALSE, then there was not enough information
-  // to log the user on or they need to update their password and gave an
-  // invalid old password.  Display the Gaia sign in page if there is not
-  // sufficient Gaia credentials or just return CPGSR_NO_CREDENTIAL_NOT_FINISHED
-  // to wait for the user to try a new password.
-  if (hr == S_FALSE) {
+  HRESULT hr = HandleAutologon(cpgsr, cpcs);
+
+  // Don't clear the state of the credential on error. The error can occur
+  // because the user is locked out or entered an incorrect old password when
+  // trying to update their password. In these situations it may still be
+  // possible to sign in with the information that is currently available if
+  // the problem can be fixed externally so keep all the information for now.
+  if (FAILED(hr)) {
+    LOGFN(ERROR) << "HandleAutologon hr=" << putHR(hr);
+    *status_icon = CPSI_ERROR;
+    *cpgsr = CPGSR_RETURN_NO_CREDENTIAL_FINISHED;
+  } else if (hr == S_FALSE) {
+    // If HandleAutologon returns S_FALSE, then there was not enough information
+    // to log the user on or they need to update their password and gave an
+    // invalid old password.  Display the Gaia sign in page if there is not
+    // sufficient Gaia credentials or just return
+    // CPGSR_NO_CREDENTIAL_NOT_FINISHED to wait for the user to try a new
+    // password.
+
     // Logon process is still running or windows password needs to be updated,
     // return that serialization is not finished so that a second logon stub
     // isn't started.
-    if (logon_ui_process_ != INVALID_HANDLE_VALUE ||
-        AreWindowsCredentialsAvailable()) {
+    if (logon_ui_process_ != INVALID_HANDLE_VALUE || needs_windows_password_) {
       *cpgsr = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
-      return S_OK;
+
+      // Warn that password needs update.
+      if (needs_to_update_windows_password_)
+        *status_icon = CPSI_WARNING;
+
+      hr = S_OK;
+    } else {
+      LOGFN(INFO) << "HandleAutologon hr=" << putHR(hr);
+      TellOmahaDidRun();
+
+      // If there is no internet connection, just abort right away.
+      if (provider_->HasInternetConnection() != S_OK) {
+        BSTR error_message = AllocErrorString(IDS_NO_NETWORK_BASE);
+        ::SHStrDupW(OLE2CW(error_message), status_text);
+        ::SysFreeString(error_message);
+
+        *status_icon = CPSI_NONE;
+        *cpgsr = CPGSR_NO_CREDENTIAL_FINISHED;
+        LOGFN(INFO) << "No internet connection";
+        UpdateSubmitButtonInteractiveState();
+        hr = S_OK;
+      } else {
+        // The account creation is async so we are not done yet.
+        *cpgsr = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
+
+        // The expectation is that the UI will eventually return the username,
+        // password, and auth to this CGaiaCredentialBase object, so that
+        // OnUserAuthenticated() can be called, followed by
+        // provider_->OnUserAuthenticated().
+        hr = CreateAndRunLogonStub();
+      }
     }
-
-    LOGFN(INFO) << "HandleAutologon hr=" << putHR(hr);
-    TellOmahaDidRun();
-
-    // If there is no internet connection, just abort right away.
-    if (provider_->HasInternetConnection() != S_OK) {
-      BSTR error_message = AllocErrorString(IDS_NO_NETWORK_BASE);
-      ::SHStrDupW(OLE2CW(error_message), status_text);
-      ::SysFreeString(error_message);
-
-      *status_icon = CPSI_NONE;
-      *cpgsr = CPGSR_NO_CREDENTIAL_FINISHED;
-      LOGFN(INFO) << "No internet connection";
-      return S_OK;
-    }
-
-    if (events_)
-      events_->SetFieldState(this, FID_SUBMIT, CPFS_HIDDEN);
-
-    // The account creation is async so we are not done yet.
-    *cpgsr = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
-
-    // The expectation is that the UI will eventually return the username,
-    // password, and auth to this CGaiaCredentialBase object, so that
-    // OnUserAuthenticated() can be called, followed by
-    // provider_->OnUserAuthenticated().
-    hr = CreateAndRunLogonStub();
+  } else {
+    *status_icon = CPSI_SUCCESS;
   }
 
+  // Logon is not complete, re-enable UI as needed.
+  if (*cpgsr != CPGSR_NO_CREDENTIAL_FINISHED &&
+      *cpgsr != CPGSR_RETURN_CREDENTIAL_FINISHED &&
+      *cpgsr != CPGSR_RETURN_NO_CREDENTIAL_FINISHED) {
+    if (events_) {
+      events_->SetFieldInteractiveState(
+          this, FID_CURRENT_PASSWORD_FIELD,
+          needs_windows_password_ ? CPFIS_FOCUSED : CPFIS_NONE);
+    }
+    UpdateSubmitButtonInteractiveState();
+  }
+  // Otherwise, keep the ui disable forever now. ReportResult will eventually
+  // be called on success or failure and the reset of the state of the
+  // credential will be done there.
   return hr;
 }
 
@@ -1271,7 +1294,6 @@
   return S_OK;
 }
 
-// static
 HRESULT CGaiaCredentialBase::ForkSaveAccountInfoStub(
     const std::unique_ptr<base::DictionaryValue>& dict,
     BSTR* status_text) {
@@ -1426,6 +1448,21 @@
   LOGFN(INFO) << "status=" << putHR(status)
               << " substatus=" << putHR(substatus);
 
+  if (status == STATUS_SUCCESS && authentication_results_) {
+    // Update the password in |authentication_results_| with the real Windows
+    // password for the user so that the SaveAccountInfo process can correctly
+    // sign in to the user account.
+    authentication_results_->SetKey(
+        kKeyPassword, base::Value(base::UTF16ToUTF8((BSTR)password_)));
+
+    // At this point the user and password stored in authentication_results_
+    // should match what is stored in username_ and password_ so the
+    // SaveAccountInfo process can be forked.
+    CComBSTR status_text;
+    HRESULT hr = ForkSaveAccountInfoStub(authentication_results_, &status_text);
+    if (FAILED(hr))
+      LOGFN(ERROR) << "ForkSaveAccountInfoStub hr=" << putHR(hr);
+  }
   *ppszOptionalStatusText = nullptr;
   *pcpsiOptionalStatusIcon = CPSI_NONE;
   ResetInternalState();
@@ -1574,6 +1611,11 @@
   USES_CONVERSION;
   DCHECK(status_text);
 
+  // Logon UI process is no longer needed and should already be finished by now
+  // so clear the handle so that calls to HandleAutoLogon do not block further
+  // processing thinking that there is still a logon process active.
+  logon_ui_process_ = INVALID_HANDLE_VALUE;
+
   // Convert the string to a base::Dictionary and add the calculated username
   // to it to be passed to the SaveAccountInfo process.
   std::string json_string;
@@ -1623,21 +1665,18 @@
   dict->SetString(kKeyUsername, new_username);
   dict->SetString(kKeySID, OLE2CA(sid));
 
+  // Disable the submit button. Either the signon will succeed with the given
+  // credentials or a password update will be needed and that flow will handle
+  // re-enabling the submit button in HandleAutoLogon.
+  if (events_)
+    events_->SetFieldInteractiveState(this, FID_SUBMIT, CPFIS_DISABLED);
+
+  // Check if the credentials are valid for the user. If they aren't show the
+  // password update prompt and continue without authenticating on the provider.
   bool password_stale = hr == S_FALSE;
   if (password_stale) {
-    *status_text = AllocErrorString(IDS_PASSWORD_UPDATE_NEEDED_BASE);
-    if (events_) {
-      events_->SetFieldState(this, FID_DESCRIPTION,
-                             CPFS_DISPLAY_IN_SELECTED_TILE);
-      events_->SetFieldString(this, FID_DESCRIPTION, *status_text);
-      events_->SetFieldState(this, FID_CURRENT_PASSWORD_FIELD,
-                             CPFS_DISPLAY_IN_SELECTED_TILE);
-      events_->SetFieldState(this, FID_SUBMIT, CPFS_DISPLAY_IN_SELECTED_TILE);
-      events_->SetFieldInteractiveState(this, FID_CURRENT_PASSWORD_FIELD,
-                                        CPFIS_FOCUSED);
-      events_->SetFieldSubmitButton(this, FID_SUBMIT,
-                                    FID_CURRENT_PASSWORD_FIELD);
-    }
+    needs_to_update_windows_password_ = true;
+    DisplayPasswordField(IDS_PASSWORD_UPDATE_NEEDED_BASE);
   }
 
   username_ = new_username.c_str();
@@ -1670,14 +1709,12 @@
   if (status_text != nullptr)
     result_status_text_.assign(OLE2CW(status_text));
 
-  if (events_)
-    events_->SetFieldState(this, FID_SUBMIT, CPFS_DISPLAY_IN_SELECTED_TILE);
-
   // If the user cancelled out of the logon, the process may be already
   // terminated, but if the handle to the process is still valid the
   // credential provider will not start a new GLS process when requested so
   // try to terminate the logon process now and clear the handle.
   TerminateLogonProcess();
+  UpdateSubmitButtonInteractiveState();
 
   // TODO(rogerta): for some reason the error info saved by ReportError()
   // never gets used because ReportResult() is never called by winlogon.exe
@@ -1689,4 +1726,27 @@
                                         CComBSTR(), FALSE);
 }
 
+void CGaiaCredentialBase::UpdateSubmitButtonInteractiveState() {
+  if (events_) {
+    bool should_enable =
+        logon_ui_process_ == INVALID_HANDLE_VALUE &&
+        (!needs_windows_password_ || current_windows_password_.Length());
+    events_->SetFieldInteractiveState(
+        this, FID_SUBMIT, should_enable ? CPFIS_NONE : CPFIS_DISABLED);
+  }
+}
+
+void CGaiaCredentialBase::DisplayPasswordField(int password_message) {
+  needs_windows_password_ = true;
+  if (events_) {
+    events_->SetFieldString(this, FID_DESCRIPTION,
+                            GetStringResource(password_message).c_str());
+    events_->SetFieldState(this, FID_CURRENT_PASSWORD_FIELD,
+                           CPFS_DISPLAY_IN_SELECTED_TILE);
+    events_->SetFieldInteractiveState(this, FID_CURRENT_PASSWORD_FIELD,
+                                      CPFIS_FOCUSED);
+    events_->SetFieldSubmitButton(this, FID_SUBMIT, FID_CURRENT_PASSWORD_FIELD);
+  }
+}
+
 }  // namespace credential_provider
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.h b/chrome/credential_provider/gaiacp/gaia_credential_base.h
index 86b50a6d..3282222 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base.h
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base.h
@@ -92,14 +92,32 @@
   const CComBSTR& get_current_windows_password() const {
     return current_windows_password_;
   }
+  const base::DictionaryValue* get_authentication_results() const {
+    return authentication_results_.get();
+  }
   void set_username(BSTR username) { username_ = username; }
   void set_user_sid(BSTR sid) { user_sid_ = sid; }
   void set_current_windows_password(BSTR password) {
     current_windows_password_ = password;
   }
+
+  // Returns true if the current credentials stored in |username_| and
+  // |password_| are valid and should succeed a local Windows logon. This
+  // function is successful if we are able to create a logon token with the
+  // credentials.
   bool AreCredentialsValid() const;
-  bool AreWindowsCredentialsAvailable() const;
-  HRESULT AreWindowsCredentialsValid(BSTR password) const;
+
+  // Returns true if some kind of credential information is available so that a
+  // sign in can be attempled. Does not guarantee that the sign in will succeed
+  // with the current credentials.
+  bool CanAttemptWindowsLogon() const;
+
+  // Returns true if the specific password credential is valid for |username_|.
+  HRESULT IsWindowsPasswordValidForStoredUser(BSTR password) const;
+
+  // Updates the UI so that the password field is displayed and also sets the
+  // state of the credential to wait for a password.
+  void DisplayPasswordField(int password_message);
 
   // IGaiaCredential
   IFACEMETHODIMP Initialize(IGaiaCredentialProvider* provider) override;
@@ -123,6 +141,14 @@
   // class method.
   virtual void ResetInternalState();
 
+  // Display error message to the user.  Virtual so that tests can override.
+  virtual void DisplayErrorInUI(LONG status, LONG substatus, BSTR status_text);
+
+  // Forks the logon stub process and waits for it to start.
+  virtual HRESULT ForkGaiaLogonStub(OSProcessManager* process_manager,
+                                    const base::CommandLine& command_line,
+                                    UIProcessInfo* uiprocinfo);
+
  private:
   // Gets the base portion of the command line to run the Gaia Logon stub.
   // This portion of the command line would only include the executable and
@@ -133,9 +159,6 @@
   HRESULT GetGlsCommandline(const wchar_t* email,
                             base::CommandLine* command_line);
 
-  // Display error message to the user.  Virtual so that tests can override.
-  virtual void DisplayErrorInUI(LONG status, LONG substatus, BSTR status_text);
-
   // Called from GetSerialization() to handle auto-logon.  If the credential
   // has enough information in internal state to auto-logon, the two arguments
   // are filled in as needed and S_OK is returned.  S_FALSE is returned to
@@ -157,11 +180,6 @@
   static HRESULT CreateGaiaLogonToken(base::win::ScopedHandle* token,
                                       PSID* sid);
 
-  // Forks the logon stub process and waits for it to start.
-  static HRESULT ForkGaiaLogonStub(OSProcessManager* process_manager,
-                                   const base::CommandLine& command_line,
-                                   UIProcessInfo* uiprocinfo);
-
   // Forks a stub process to save account information for a user.
   static HRESULT ForkSaveAccountInfoStub(
       const std::unique_ptr<base::DictionaryValue>& dict,
@@ -210,6 +228,17 @@
       CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon) override;
   IFACEMETHODIMP GetUserSid(wchar_t** sid) override;
 
+  // Checks whether the submit button for the credential should be enabled
+  // (depending on the state of the credential) and enables / disables it
+  // accordingly. This generally occurs when processing is being done that does
+  // not require direct user input to the credential (user is entering
+  // credentials in  GLS) or a submit of the credential is not valid (user needs
+  // to enter the old Windows password but currently nothing has been entered in
+  // the password field).
+  void UpdateSubmitButtonInteractiveState();
+
+  // Stops the GLS process in case it is still executing. Often called when user
+  // switches credentials in the middle of a sign in through the GLS.
   void TerminateLogonProcess();
 
   // Checks if the information for the given |username| is valid and creates it
@@ -242,6 +271,9 @@
   CComBSTR password_;
   CComBSTR user_sid_;
 
+  bool needs_windows_password_ = false;
+  bool needs_to_update_windows_password_ = false;
+
   // The password entered into the FID_CURRENT_PASSWORD_FIELD to update the
   // Windows password with the gaia password.
   CComBSTR current_windows_password_;
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
index 556e198..a8206c1 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_base_unittests.cc
@@ -5,7 +5,7 @@
 #include "chrome/credential_provider/common/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/gaia_credential_base.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
-#include "chrome/credential_provider/test/fake_gls_run_helper.h"
+#include "chrome/credential_provider/test/gls_runner_test_base.h"
 #include "chrome/credential_provider/test/test_credential.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -15,9 +15,6 @@
 
 // This class is used to implement a test credential based off only
 // CGaiaCredentialBase which requires certain functions be implemented.
-
-// This class is used to implement a test credential based off only
-// CGaiaCredentialBase which requires certain
 class ATL_NO_VTABLE CTestCredentialForBase
     : public CTestCredentialBase<CGaiaCredentialBase>,
       public CComObjectRootEx<CComMultiThreadModel> {
@@ -47,9 +44,9 @@
 namespace {
 
 HRESULT CreateCredential(ICredentialProviderCredential** credential) {
-  return CComCreator<CComObject<testing::CTestCredentialForBase>>::
-      CreateInstance(nullptr, IID_ICredentialProviderCredential,
-                     reinterpret_cast<void**>(credential));
+  return CComCreator<CComObject<CTestCredentialForBase>>::CreateInstance(
+      nullptr, IID_ICredentialProviderCredential,
+      reinterpret_cast<void**>(credential));
 }
 
 HRESULT CreateCredentialWithProvider(
@@ -69,23 +66,7 @@
 
 }  // namespace
 
-class GcpGaiaCredentialBaseTest : public ::testing::Test {
- protected:
-  ~GcpGaiaCredentialBaseTest() override;
-
-  void SetUp() override;
-
-  FakeGlsRunHelper* run_helper() { return &run_helper_; }
-
- private:
-  FakeGlsRunHelper run_helper_;
-};
-
-GcpGaiaCredentialBaseTest::~GcpGaiaCredentialBaseTest() = default;
-
-void GcpGaiaCredentialBaseTest::SetUp() {
-  run_helper_.SetUp();
-}
+class GcpGaiaCredentialBaseTest : public GlsRunnerTestBase {};
 
 TEST_F(GcpGaiaCredentialBaseTest, Advise) {
   CComPtr<ICredentialProviderCredential> cred;
@@ -114,7 +95,7 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcess(cred, /*succeeds=*/false));
@@ -129,7 +110,7 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
@@ -145,9 +126,6 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
-  ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   // Now finish the logon.
@@ -157,22 +135,33 @@
   CREDENTIAL_PROVIDER_STATUS_ICON status_icon;
   ASSERT_EQ(S_OK,
             cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon));
-  ASSERT_EQ(nullptr, status_text);
-  ASSERT_EQ(CPSI_NONE, status_icon);
-  ASSERT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr);
-  ASSERT_LT(0u, cpcs.cbSerialization);
-  ASSERT_NE(nullptr, cpcs.rgbSerialization);
+  EXPECT_EQ(nullptr, status_text);
+  EXPECT_EQ(CPSI_SUCCESS, status_icon);
+  EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr);
+  EXPECT_LT(0u, cpcs.cbSerialization);
+  EXPECT_NE(nullptr, cpcs.rgbSerialization);
 
-  // State was reset.
-  ASSERT_FALSE(test->AreCredentialsValid());
+  CComPtr<ITestCredential> test;
+  ASSERT_EQ(S_OK, cred.QueryInterface(&test));
+
+  // State was not reset.
+  EXPECT_TRUE(test->AreCredentialsValid());
 
   // Make sure a "foo" user was created.
   PSID sid;
-  ASSERT_EQ(S_OK, run_helper()->fake_os_user_manager()->GetUserSID(
-                      testing::kDefaultUsername, &sid));
+  EXPECT_EQ(S_OK, fake_os_user_manager()->GetUserSID(kDefaultUsername, &sid));
   ::LocalFree(sid);
+  EXPECT_EQ(test->GetFinalEmail(), kDefaultEmail);
 
-  ASSERT_EQ(S_OK, gaia_cred->Terminate());
+  wchar_t* report_status_text = nullptr;
+  CREDENTIAL_PROVIDER_STATUS_ICON report_icon;
+  EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon));
+  // State was reset.
+  EXPECT_FALSE(test->AreCredentialsValid());
+  EXPECT_EQ(S_OK, gaia_cred->Terminate());
+
+  // New user should be created.
+  EXPECT_EQ(2ul, fake_os_user_manager()->GetUserCount());
 }
 
 TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_MultipleCalls) {
@@ -182,7 +171,7 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
 
   constexpr wchar_t kStartGlsEventName[] =
@@ -232,12 +221,13 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
-  ASSERT_TRUE(test->AreWindowsCredentialsAvailable());
+  EXPECT_TRUE(test->CanAttemptWindowsLogon());
+  EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser());
 
   // Check that the process has not finished yet.
   CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE cpgsr;
@@ -246,41 +236,49 @@
   CREDENTIAL_PROVIDER_STATUS_ICON status_icon;
   ASSERT_EQ(S_OK,
             cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon));
-  ASSERT_EQ(nullptr, status_text);
-  ASSERT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr);
+  EXPECT_EQ(nullptr, status_text);
+  EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr);
 
   // Credentials should still be available.
-  ASSERT_TRUE(test->AreWindowsCredentialsAvailable());
+  EXPECT_TRUE(test->CanAttemptWindowsLogon());
+  EXPECT_FALSE(test->IsWindowsPasswordValidForStoredUser());
 
   // Set an invalid password and try to get serialization again. Credentials
   // should still be valid but serialization is not complete.
   CComBSTR invalid_windows_password = L"a";
   test->SetWindowsPassword(invalid_windows_password);
-  ASSERT_EQ(nullptr, status_text);
+  EXPECT_EQ(nullptr, status_text);
   ASSERT_EQ(S_OK,
             cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon));
-  ASSERT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr);
+  EXPECT_EQ(CPGSR_NO_CREDENTIAL_NOT_FINISHED, cpgsr);
 
   // Update the Windows password to be the real password created for the user.
   test->SetWindowsPassword(windows_password);
+  // Sign in information should still be available.
+  EXPECT_TRUE(test->GetFinalEmail().length());
 
   // Both Windows and Gaia credentials should be valid now
-  ASSERT_TRUE(test->AreWindowsCredentialsAvailable());
-  ASSERT_TRUE(test->AreWindowsCredentialsValid());
+  EXPECT_TRUE(test->CanAttemptWindowsLogon());
+  EXPECT_TRUE(test->IsWindowsPasswordValidForStoredUser());
 
   // Serialization should complete without any errors.
   ASSERT_EQ(S_OK,
             cred->GetSerialization(&cpgsr, &cpcs, &status_text, &status_icon));
-  ASSERT_EQ(nullptr, status_text);
-  ASSERT_EQ(CPSI_NONE, status_icon);
-  ASSERT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr);
-  ASSERT_LT(0u, cpcs.cbSerialization);
-  ASSERT_NE(nullptr, cpcs.rgbSerialization);
+  EXPECT_EQ(nullptr, status_text);
+  EXPECT_EQ(CPSI_SUCCESS, status_icon);
+  EXPECT_EQ(CPGSR_RETURN_CREDENTIAL_FINISHED, cpgsr);
+  EXPECT_LT(0u, cpcs.cbSerialization);
+  EXPECT_NE(nullptr, cpcs.rgbSerialization);
 
+  // State was not reset.
+  EXPECT_TRUE(test->AreCredentialsValid());
+  wchar_t* report_status_text = nullptr;
+  CREDENTIAL_PROVIDER_STATUS_ICON report_icon;
+  EXPECT_EQ(S_OK, cred->ReportResult(0, 0, &report_status_text, &report_icon));
   // State was reset.
-  ASSERT_FALSE(test->AreCredentialsValid());
+  EXPECT_FALSE(test->AreCredentialsValid());
 
-  ASSERT_EQ(S_OK, gaia_cred->Terminate());
+  EXPECT_EQ(S_OK, gaia_cred->Terminate());
 }
 
 TEST_F(GcpGaiaCredentialBaseTest, GetSerialization_Cancel) {
@@ -290,7 +288,7 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
 
   // This event is merely used to keep the gls running while it is cancelled
@@ -320,13 +318,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "foo@imfl.info";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK, test->SetGlsEmailAddress("foo@imfl.info"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"foo_imfl"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
@@ -339,13 +340,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "bar@gmail.com";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK, test->SetGlsEmailAddress("bar@gmail.com"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"bar"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
@@ -358,13 +362,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "toto@googlemail.com";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK, test->SetGlsEmailAddress("toto@googlemail.com"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"toto"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
@@ -377,13 +384,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "a\\[]:|<>+=;?*z@gmail.com";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK, test->SetGlsEmailAddress("a\\[]:|<>+=;?*z@gmail.com"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"a____________z"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
@@ -396,14 +406,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "areallylongemailadressdude@gmail.com";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK,
-            test->SetGlsEmailAddress("areallylongemailadressdude@gmail.com"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"areallylongemailadre"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
@@ -416,13 +428,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "foo@areallylongdomaindude.com";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK, test->SetGlsEmailAddress("foo@areallylongdomaindude.com"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"foo_areallylongdomai"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
@@ -435,13 +450,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "foo";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK, test->SetGlsEmailAddress("foo"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"foo_gmail"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
@@ -454,13 +472,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "@com";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK, test->SetGlsEmailAddress("@com"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"_com"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
@@ -473,13 +494,16 @@
   CComPtr<ICredentialProviderCredential> cred;
   ASSERT_EQ(S_OK, CreateCredentialWithProvider(&provider, &gaia_cred, &cred));
 
-  CComPtr<testing::ITestCredential> test;
+  constexpr char email[] = "@.com";
+
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
-  ASSERT_EQ(S_OK, test->SetGlsEmailAddress("@.com"));
+  ASSERT_EQ(S_OK, test->SetGlsEmailAddress(email));
 
   ASSERT_EQ(S_OK, run_helper()->StartLogonProcessAndWait(cred));
 
   ASSERT_STREQ(W2COLE(L"_.com"), test->GetFinalUsername());
+  EXPECT_EQ(test->GetFinalEmail(), email);
 
   ASSERT_EQ(S_OK, gaia_cred->Terminate());
 }
diff --git a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
index 0ad6a9c..9e18a731 100644
--- a/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
+++ b/chrome/credential_provider/gaiacp/gaia_credential_provider_unittests.cc
@@ -28,11 +28,9 @@
                       const wchar_t* comment,
                       const wchar_t* gaia_id,
                       BSTR* sid) {
-    DWORD error;
-    ASSERT_EQ(S_OK, fake_os_user_manager_.AddUser(username, password, fullname,
-                                                  comment, true, sid, &error));
-    ASSERT_EQ(S_OK, SetUserProperty(OLE2CW(*sid), L"id", gaia_id));
-    ASSERT_EQ(S_OK, SetUserProperty(OLE2CW(*sid), L"email", email));
+    ASSERT_EQ(S_OK,
+              fake_os_user_manager_.CreateTestOSUser(
+                  username, password, fullname, comment, gaia_id, email, sid));
   }
 
   void CreateDeletedGCPWUser(BSTR* sid) {
diff --git a/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc b/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc
index 3161b6a..f0f9335 100644
--- a/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc
+++ b/chrome/credential_provider/gaiacp/reauth_credential_unittests.cc
@@ -16,27 +16,32 @@
 #include "chrome/credential_provider/gaiacp/reauth_credential.h"
 #include "chrome/credential_provider/gaiacp/reg_utils.h"
 #include "chrome/credential_provider/test/com_fakes.h"
-#include "chrome/credential_provider/test/fake_gls_run_helper.h"
 #include "chrome/credential_provider/test/gcp_fakes.h"
+#include "chrome/credential_provider/test/gls_runner_test_base.h"
 #include "chrome/credential_provider/test/test_credential.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace credential_provider {
 
+namespace testing {
+
 namespace {
 
 HRESULT CreateReauthCredentialWithProvider(
     IGaiaCredentialProvider* provider,
     IGaiaCredential** gaia_credential,
     ICredentialProviderCredential** credential) {
-  return testing::CreateInheritedCredentialWithProvider<CReauthCredential,
-                                                        IReauthCredential>(
+  return CreateInheritedCredentialWithProvider<CReauthCredential,
+                                               IReauthCredential>(
       provider, gaia_credential, credential);
 }
 
 }  // namespace
 
 class GcpReauthCredentialTest : public ::testing::Test {
+ protected:
+  FakeOSUserManager* fake_os_user_manager() { return &fake_os_user_manager_; }
+
  private:
   void SetUp() override;
 
@@ -104,20 +109,18 @@
 
   // Create two fake users to reauth. One associated with the valid Gaia id
   // and the other associated to the invalid gaia id.
-  OSUserManager* manager = OSUserManager::Get();
   CComBSTR first_sid;
-  DWORD error;
-  ASSERT_EQ(S_OK, manager->AddUser(username, password, full_name, L"comment",
-                                   true, &first_sid, &error));
-  ASSERT_EQ(S_OK, SetUserProperty(
-                      OLE2CW(first_sid), A2CW(kKeyId),
-                      base::UTF8ToUTF16(test_data_storage.GetSuccessId())));
+  ASSERT_EQ(S_OK,
+            fake_os_user_manager()->CreateTestOSUser(
+                OLE2CW(username), OLE2CW(password), OLE2CW(full_name),
+                L"comment", base::UTF8ToUTF16(test_data_storage.GetSuccessId()),
+                base::string16(), &first_sid));
 
   CComBSTR second_sid;
-  ASSERT_EQ(S_OK, manager->AddUser(L"foo_bar2", L"pwd2", L"name2", L"comment2",
-                                   true, &second_sid, &error));
-  ASSERT_EQ(S_OK, SetUserProperty(OLE2CW(second_sid), A2CW(kKeyId),
-                                  base::UTF8ToUTF16(unexpected_gaia_id)));
+  ASSERT_EQ(S_OK, fake_os_user_manager()->CreateTestOSUser(
+                      L"foo_bar2", L"pwd2", L"name2", L"comment2",
+                      base::UTF8ToUTF16(unexpected_gaia_id), base::string16(),
+                      &second_sid));
 
   // Initialize a reauth credential for the valid gaia id.
   CComPtr<IReauthCredential> reauth;
@@ -147,25 +150,9 @@
   EXPECT_EQ(FALSE, provider.credentials_changed_fired());
 }
 
-class GcpReauthCredentialRunnableTest : public ::testing::Test {
- protected:
-  ~GcpReauthCredentialRunnableTest() override;
+class GcpReauthCredentialGlsRunnerTest : public GlsRunnerTestBase {};
 
-  void SetUp() override;
-
-  FakeGlsRunHelper* run_helper() { return &run_helper_; }
-
- private:
-  FakeGlsRunHelper run_helper_;
-};
-
-GcpReauthCredentialRunnableTest::~GcpReauthCredentialRunnableTest() = default;
-
-void GcpReauthCredentialRunnableTest::SetUp() {
-  run_helper_.SetUp();
-}
-
-TEST_F(GcpReauthCredentialRunnableTest, NormalReauth) {
+TEST_F(GcpReauthCredentialGlsRunnerTest, NormalReauth) {
   USES_CONVERSION;
   CredentialProviderSigninDialogTestDataStorage test_data_storage;
   CComBSTR username = L"foo_bar";
@@ -198,7 +185,7 @@
   ASSERT_EQ(S_OK, reauth->SetOSUserInfo(sid, username));
   ASSERT_EQ(S_OK, reauth->SetEmailForReauth(email));
 
-  CComPtr<testing::ITestCredential> test;
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
   ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string()));
 
@@ -215,7 +202,7 @@
   ASSERT_STREQ(test->GetErrorText(), NULL);
 }
 
-TEST_F(GcpReauthCredentialRunnableTest, DISABLED_GaiaIdMismatch) {
+TEST_F(GcpReauthCredentialGlsRunnerTest, GaiaIdMismatch) {
   USES_CONVERSION;
   CredentialProviderSigninDialogTestDataStorage test_data_storage;
   CComBSTR username = L"foo_bar";
@@ -224,14 +211,12 @@
   CComBSTR email = A2COLE(test_data_storage.GetSuccessEmail().c_str());
 
   // Create a fake user to reauth.
-  OSUserManager* manager = OSUserManager::Get();
   CComBSTR sid;
-  DWORD error;
-  ASSERT_EQ(S_OK, manager->AddUser(username, password, full_name, L"comment",
-                                   true, &sid, &error));
-  ASSERT_EQ(S_OK, SetUserProperty(
-                      OLE2CW(sid), L"id",
-                      base::UTF8ToUTF16(test_data_storage.GetSuccessId())));
+  ASSERT_EQ(S_OK,
+            fake_os_user_manager()->CreateTestOSUser(
+                OLE2CW(username), OLE2CW(password), OLE2CW(full_name),
+                L"comment", base::UTF8ToUTF16(test_data_storage.GetSuccessId()),
+                base::string16(), &sid));
 
   std::string unexpected_gaia_id = "unexpected-gaia-id";
   FakeGaiaCredentialProvider provider;
@@ -248,7 +233,7 @@
   ASSERT_EQ(S_OK, reauth->SetOSUserInfo(sid, username));
   ASSERT_EQ(S_OK, reauth->SetEmailForReauth(email));
 
-  CComPtr<testing::ITestCredential> test;
+  CComPtr<ITestCredential> test;
   ASSERT_EQ(S_OK, cred.QueryInterface(&test));
   ASSERT_EQ(S_OK, test->SetGlsEmailAddress(std::string()));
   ASSERT_EQ(S_OK, test->SetGaiaIdOverride(unexpected_gaia_id));
@@ -267,4 +252,6 @@
                GetStringResource(IDS_EMAIL_MISMATCH_BASE).c_str());
 }
 
+}  // namespace testing
+
 }  // namespace credential_provider
diff --git a/chrome/credential_provider/test/BUILD.gn b/chrome/credential_provider/test/BUILD.gn
index 92f442a7c..cd2c78f 100644
--- a/chrome/credential_provider/test/BUILD.gn
+++ b/chrome/credential_provider/test/BUILD.gn
@@ -20,6 +20,8 @@
     "gcp_gls_output_unittest.cc",
     "gcp_setup_unittests.cc",
     "gcp_test_main.cc",
+    "gls_runner_test_base.cc",
+    "gls_runner_test_base.h",
     "test_credential.h",
   ]
 
diff --git a/chrome/credential_provider/test/com_fakes.cc b/chrome/credential_provider/test/com_fakes.cc
index 55974f5..337c441f 100644
--- a/chrome/credential_provider/test/com_fakes.cc
+++ b/chrome/credential_provider/test/com_fakes.cc
@@ -9,6 +9,7 @@
 #include "base/logging.h"
 #include "chrome/credential_provider/gaiacp/os_user_manager.h"
 #include "chrome/credential_provider/gaiacp/stdafx.h"
+#include "chrome/credential_provider/test/test_credential.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace credential_provider {
diff --git a/chrome/credential_provider/test/com_fakes.h b/chrome/credential_provider/test/com_fakes.h
index 0610461c..434f20c 100644
--- a/chrome/credential_provider/test/com_fakes.h
+++ b/chrome/credential_provider/test/com_fakes.h
@@ -107,7 +107,7 @@
                                      BSTR username,
                                      BSTR password,
                                      BSTR sid,
-                                     BOOL password_stale) override;
+                                     BOOL fire_credentials_changed) override;
   IFACEMETHODIMP HasInternetConnection() override;
 
   // IGaiaCredentialProviderForTesting
diff --git a/chrome/credential_provider/test/fake_gls_run_helper.cc b/chrome/credential_provider/test/fake_gls_run_helper.cc
index 4100ae5..132d46b 100644
--- a/chrome/credential_provider/test/fake_gls_run_helper.cc
+++ b/chrome/credential_provider/test/fake_gls_run_helper.cc
@@ -14,6 +14,7 @@
 #include "chrome/credential_provider/common/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
 #include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h"
+#include "chrome/credential_provider/test/gcp_fakes.h"
 #include "chrome/credential_provider/test/test_credential.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/multiprocess_func_list.h"
@@ -32,10 +33,9 @@
 
 // Corresponding default email and username for tests that don't override them.
 const char kDefaultEmail[] = "foo@gmail.com";
+const char kDefaultGaiaId[] = "test-gaia-id";
 const wchar_t kDefaultUsername[] = L"foo";
 
-}  // namespace testing
-
 namespace {
 
 // Generates a common signin result given an email pass through the command
@@ -73,9 +73,8 @@
   } else {
     EXPECT_EQ(gls_email, std::string());
   }
-  constexpr char default_gaia_id[] = "1234567890";
   if (expected_gaia_id.empty())
-    expected_gaia_id = default_gaia_id;
+    expected_gaia_id = kDefaultGaiaId;
   base::DictionaryValue dict;
   if (!gaia_id_override.empty() && gaia_id_override != expected_gaia_id) {
     dict.SetInteger(kKeyExitCode, kUiecEMailMissmatch);
@@ -103,12 +102,12 @@
 }
 }  // namespace
 
-FakeGlsRunHelper::FakeGlsRunHelper() {
+FakeGlsRunHelper::FakeGlsRunHelper(FakeOSUserManager* fake_os_user_manager) {
   // Create the special gaia account used to run GLS and save its password.
 
   BSTR sid;
   DWORD error;
-  EXPECT_EQ(S_OK, fake_os_user_manager_.AddUser(
+  EXPECT_EQ(S_OK, fake_os_user_manager->AddUser(
                       kDefaultGaiaAccountName, L"password", L"fullname",
                       L"comment", true, &sid, &error));
 
@@ -120,13 +119,6 @@
 
 FakeGlsRunHelper::~FakeGlsRunHelper() = default;
 
-void FakeGlsRunHelper::SetUp() {
-  // Make sure not to read random GCPW settings from the machine that is running
-  // the tests.
-  ASSERT_NO_FATAL_FAILURE(
-      registry_override_.OverrideRegistry(HKEY_LOCAL_MACHINE));
-}
-
 HRESULT FakeGlsRunHelper::StartLogonProcess(ICredentialProviderCredential* cred,
                                             bool succeeds) {
   BOOL auto_login;
@@ -154,18 +146,19 @@
 HRESULT FakeGlsRunHelper::WaitForLogonProcess(
     ICredentialProviderCredential* cred) {
   CComPtr<testing::ITestCredential> test;
-  EXPECT_EQ(S_OK, cred->QueryInterface(__uuidof(testing::ITestCredential),
-                                       reinterpret_cast<void**>(&test)));
-  EXPECT_EQ(S_OK, test->WaitForGls());
-
-  return S_OK;
+  HRESULT hr = cred->QueryInterface(__uuidof(testing::ITestCredential),
+                                    reinterpret_cast<void**>(&test));
+  if (FAILED(hr))
+    return hr;
+  return test->WaitForGls();
 }
 
 HRESULT FakeGlsRunHelper::StartLogonProcessAndWait(
     ICredentialProviderCredential* cred) {
-  EXPECT_EQ(S_OK, StartLogonProcess(cred, /*succeeds=*/true));
-  EXPECT_EQ(S_OK, WaitForLogonProcess(cred));
-  return S_OK;
+  HRESULT hr = StartLogonProcess(cred, /*succeeds=*/true);
+  if (FAILED(hr))
+    return hr;
+  return WaitForLogonProcess(cred);
 }
 
 // static
@@ -191,4 +184,6 @@
   return S_OK;
 }
 
+}  // namespace testing
+
 }  // namespace credential_provider
diff --git a/chrome/credential_provider/test/fake_gls_run_helper.h b/chrome/credential_provider/test/fake_gls_run_helper.h
index 7769054c..97d9e4b 100644
--- a/chrome/credential_provider/test/fake_gls_run_helper.h
+++ b/chrome/credential_provider/test/fake_gls_run_helper.h
@@ -5,46 +5,45 @@
 #ifndef CHROME_CREDENTIAL_PROVIDER_TEST_FAKE_GLS_RUN_HELPER_H_
 #define CHROME_CREDENTIAL_PROVIDER_TEST_FAKE_GLS_RUN_HELPER_H_
 
+#include <atlcomcli.h>
+
+#include "base/strings/string16.h"
+
 struct ICredentialProviderCredential;
 
-#include "base/test/test_reg_util_win.h"
-#include "chrome/credential_provider/test/com_fakes.h"
-#include "chrome/credential_provider/test/gcp_fakes.h"
+namespace base {
+class CommandLine;
+}
 
 namespace credential_provider {
 
+class FakeOSUserManager;
+
 namespace testing {
+
 extern const char kDefaultEmail[];
+extern const char kDefaultGaiaId[];
 extern const wchar_t kDefaultUsername[];
-}  // namespace testing
 
 // Helper class used to run and wait for a fake GLS process to validate the
 // functionality of a GCPW credential.
 class FakeGlsRunHelper {
  public:
-  FakeGlsRunHelper();
+  explicit FakeGlsRunHelper(FakeOSUserManager* fake_os_user_manager);
   ~FakeGlsRunHelper();
 
-  void SetUp();
-
   HRESULT StartLogonProcess(ICredentialProviderCredential* cred, bool succeeds);
   HRESULT WaitForLogonProcess(ICredentialProviderCredential* cred);
   HRESULT StartLogonProcessAndWait(ICredentialProviderCredential* cred);
-
-  FakeOSUserManager* fake_os_user_manager() { return &fake_os_user_manager_; }
   static HRESULT GetFakeGlsCommandline(
       const std::string& gls_email,
       const std::string& gaia_id_override,
       const base::string16& start_gls_event_name,
       base::CommandLine* command_line);
-
- private:
-  FakeOSProcessManager fake_os_process_manager_;
-  FakeOSUserManager fake_os_user_manager_;
-  FakeScopedLsaPolicyFactory fake_scoped_lsa_policy_factory_;
-  registry_util::RegistryOverrideManager registry_override_;
 };
 
+}  // namespace testing
+
 }  // namespace credential_provider
 
 #endif  // CHROME_CREDENTIAL_PROVIDER_TEST_FAKE_GLS_RUN_HELPER_H_
diff --git a/chrome/credential_provider/test/gcp_fakes.cc b/chrome/credential_provider/test/gcp_fakes.cc
index 11d98f8..dfe25ef 100644
--- a/chrome/credential_provider/test/gcp_fakes.cc
+++ b/chrome/credential_provider/test/gcp_fakes.cc
@@ -5,18 +5,25 @@
 #include "chrome/credential_provider/test/gcp_fakes.h"
 
 #include <windows.h>
+
 #include <lm.h>
 #include <sddl.h>
 
+#include <atlcomcli.h>
 #include <atlconv.h>
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/win/scoped_handle.h"
 #include "base/win/scoped_process_information.h"
+#include "chrome/credential_provider/common/gcp_strings.h"
 #include "chrome/credential_provider/gaiacp/logging.h"
+#include "chrome/credential_provider/gaiacp/reg_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace credential_provider {
@@ -25,9 +32,9 @@
 
 HRESULT CreateArbitrarySid(DWORD subauth0, PSID* sid) {
   SID_IDENTIFIER_AUTHORITY Authority = {SECURITY_NON_UNIQUE_AUTHORITY};
-  if (!::AllocateAndInitializeSid(&Authority, 1, subauth0, 0, 0, 0, 0, 0,
-                                  0, 0, sid)) {
-    return(HRESULT_FROM_WIN32(::GetLastError()));
+  if (!::AllocateAndInitializeSid(&Authority, 1, subauth0, 0, 0, 0, 0, 0, 0, 0,
+                                  sid)) {
+    return (HRESULT_FROM_WIN32(::GetLastError()));
   }
   return S_OK;
 }
@@ -75,8 +82,8 @@
   PROCESS_INFORMATION new_procinfo = {};
   // Pass a copy of the command line string to CreateProcessW() because this
   // function could change the string.
-  std::unique_ptr<wchar_t, void (*)(void*)>
-      cmdline(_wcsdup(command_line.GetCommandLineString().c_str()), std::free);
+  std::unique_ptr<wchar_t, void (*)(void*)> cmdline(
+      _wcsdup(command_line.GetCommandLineString().c_str()), std::free);
   if (!::CreateProcessW(command_line.GetProgram().value().c_str(),
                         cmdline.get(), nullptr, nullptr, TRUE, CREATE_SUSPENDED,
                         nullptr, nullptr, &local_startupinfo, &new_procinfo)) {
@@ -253,6 +260,34 @@
   return CreateArbitrarySid(++next_rid_, sid);
 }
 
+HRESULT FakeOSUserManager::CreateTestOSUser(const base::string16& username,
+                                            const base::string16& password,
+                                            const base::string16& fullname,
+                                            const base::string16& comment,
+                                            const base::string16& gaia_id,
+                                            const base::string16& email,
+                                            BSTR* sid) {
+  DWORD error;
+  HRESULT hr = AddUser(username.c_str(), password.c_str(), fullname.c_str(),
+                       comment.c_str(), true, sid, &error);
+  if (FAILED(hr))
+    return hr;
+
+  if (!gaia_id.empty()) {
+    hr = SetUserProperty(OLE2CW(*sid), base::UTF8ToUTF16(kKeyId), gaia_id);
+    if (FAILED(hr))
+      return hr;
+  }
+
+  if (!email.empty()) {
+    hr = SetUserProperty(OLE2CW(*sid), base::UTF8ToUTF16(kKeyEmail), email);
+    if (FAILED(hr))
+      return hr;
+  }
+
+  return S_OK;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 FakeScopedLsaPolicyFactory::FakeScopedLsaPolicyFactory()
diff --git a/chrome/credential_provider/test/gcp_fakes.h b/chrome/credential_provider/test/gcp_fakes.h
index 963d4176..d253442 100644
--- a/chrome/credential_provider/test/gcp_fakes.h
+++ b/chrome/credential_provider/test/gcp_fakes.h
@@ -6,6 +6,9 @@
 #define CHROME_CREDENTIAL_PROVIDER_TEST_GCP_FAKES_H_
 
 #include <map>
+#include <memory>
+#include <string>
+#include <vector>
 
 #include "base/strings/string16.h"
 #include "base/win/scoped_handle.h"
@@ -13,6 +16,7 @@
 #include "chrome/credential_provider/gaiacp/os_user_manager.h"
 #include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h"
 #include "chrome/credential_provider/gaiacp/scoped_user_profile.h"
+#include "chrome/credential_provider/gaiacp/win_http_url_fetcher.h"
 
 namespace credential_provider {
 
@@ -91,6 +95,21 @@
   // Creates a new unique sid.  Free returned sid with FreeSid().
   HRESULT CreateNewSID(PSID* sid);
 
+  // Creates a fake user with the given |username|, |password|, |fullname|,
+  // |comment|. If |gaia_id| is non-empty, also associates the user with
+  // the given gaia id. If |email| is non-empty, sets the email to use for
+  // reauth to be this one.
+  // |sid| is allocated and filled with the SID of the new user.
+  HRESULT CreateTestOSUser(const base::string16& username,
+                           const base::string16& password,
+                           const base::string16& fullname,
+                           const base::string16& comment,
+                           const base::string16& gaia_id,
+                           const base::string16& email,
+                           BSTR* sid);
+
+  size_t GetUserCount() const { return username_to_info_.size(); }
+
  private:
   OSUserManager* original_manager_;
   DWORD next_rid_ = 0;
diff --git a/chrome/credential_provider/test/gls_runner_test_base.cc b/chrome/credential_provider/test/gls_runner_test_base.cc
new file mode 100644
index 0000000..e7f564a
--- /dev/null
+++ b/chrome/credential_provider/test/gls_runner_test_base.cc
@@ -0,0 +1,24 @@
+// 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.
+
+#include "gls_runner_test_base.h"
+
+namespace credential_provider {
+
+namespace testing {
+
+GlsRunnerTestBase::GlsRunnerTestBase() : run_helper_(&fake_os_user_manager_) {}
+
+GlsRunnerTestBase::~GlsRunnerTestBase() = default;
+
+void GlsRunnerTestBase::SetUp() {
+  // Make sure not to read random GCPW settings from the machine that is running
+  // the tests.
+  ASSERT_NO_FATAL_FAILURE(
+      registry_override_.OverrideRegistry(HKEY_LOCAL_MACHINE));
+}
+
+}  // namespace testing
+
+}  // namespace credential_provider
diff --git a/chrome/credential_provider/test/gls_runner_test_base.h b/chrome/credential_provider/test/gls_runner_test_base.h
new file mode 100644
index 0000000..4f01554
--- /dev/null
+++ b/chrome/credential_provider/test/gls_runner_test_base.h
@@ -0,0 +1,42 @@
+// 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.
+
+#ifndef CHROME_CREDENTIAL_PROVIDER_TEST_GLS_RUNNER_TEST_BASE_H_
+#define CHROME_CREDENTIAL_PROVIDER_TEST_GLS_RUNNER_TEST_BASE_H_
+
+#include "base/test/test_reg_util_win.h"
+#include "chrome/credential_provider/test/com_fakes.h"
+#include "chrome/credential_provider/test/fake_gls_run_helper.h"
+#include "chrome/credential_provider/test/gcp_fakes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace credential_provider {
+
+namespace testing {
+
+// Helper class used to run and wait for a fake GLS process to validate the
+// functionality of a GCPW credential.
+class GlsRunnerTestBase : public ::testing::Test {
+ protected:
+  GlsRunnerTestBase();
+  ~GlsRunnerTestBase() override;
+
+  void SetUp() override;
+  FakeGlsRunHelper* run_helper() { return &run_helper_; }
+
+  FakeOSUserManager* fake_os_user_manager() { return &fake_os_user_manager_; }
+
+ private:
+  FakeOSProcessManager fake_os_process_manager_;
+  FakeOSUserManager fake_os_user_manager_;
+  FakeScopedLsaPolicyFactory fake_scoped_lsa_policy_factory_;
+  registry_util::RegistryOverrideManager registry_override_;
+  FakeGlsRunHelper run_helper_;
+};
+
+}  // namespace testing
+
+}  // namespace credential_provider
+
+#endif  // CHROME_CREDENTIAL_PROVIDER_TEST_GLS_RUNNER_TEST_BASE_H_
diff --git a/chrome/credential_provider/test/test_credential.h b/chrome/credential_provider/test/test_credential.h
index a0d5168..db137e2 100644
--- a/chrome/credential_provider/test/test_credential.h
+++ b/chrome/credential_provider/test/test_credential.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_CREDENTIAL_PROVIDER_TEST_TEST_CREDENTIAL_H_
 #define CHROME_CREDENTIAL_PROVIDER_TEST_TEST_CREDENTIAL_H_
 
+#include <memory>
+#include <string>
+
 #include <atlbase.h>
 #include <atlcom.h>
 #include <atlcomcli.h>
@@ -13,7 +16,7 @@
 #include "base/strings/string16.h"
 #include "base/synchronization/waitable_event.h"
 #include "chrome/credential_provider/common/gcp_strings.h"
-#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
+#include "chrome/credential_provider/gaiacp/gaia_credential_base.h"
 #include "chrome/credential_provider/test/fake_gls_run_helper.h"
 
 namespace base {
@@ -35,10 +38,11 @@
   virtual HRESULT STDMETHODCALLTYPE
   SetStartGlsEventName(const base::string16& event_name) = 0;
   virtual BSTR STDMETHODCALLTYPE GetFinalUsername() = 0;
+  virtual std::string STDMETHODCALLTYPE GetFinalEmail() = 0;
   virtual BSTR STDMETHODCALLTYPE GetErrorText() = 0;
   virtual bool STDMETHODCALLTYPE AreCredentialsValid() = 0;
-  virtual bool STDMETHODCALLTYPE AreWindowsCredentialsAvailable() = 0;
-  virtual bool STDMETHODCALLTYPE AreWindowsCredentialsValid() = 0;
+  virtual bool STDMETHODCALLTYPE CanAttemptWindowsLogon() = 0;
+  virtual bool STDMETHODCALLTYPE IsWindowsPasswordValidForStoredUser() = 0;
   virtual void STDMETHODCALLTYPE
   SetWindowsPassword(const CComBSTR& windows_password) = 0;
 };
@@ -49,11 +53,12 @@
 // CGaiaCredentialBase should be the base for this test class.
 // A CGaiaCredentialBase is required to call base class functions in the
 // following ITestCredential implementations:
-// GetFinalUsername, AreCredentialsValid, AreWindowsCredentialsAvailable,
-// AreWindowsCredentialsValid, DisplayErrorInUI, GetBaseGlsCommandline
-// Also the following IGaiaCredential function needs to be overridden to
-// notify the completion of the fake GLS that this credential runs:
-// OnUserAuthenticated.
+// GetFinalUsername, GetFinalEmail, AreCredentialsValid,
+// CanAttemptWindowsLogon, IsWindowsPasswordValidForStoredUser,
+// SetWindowsPassword.
+// Also the following IGaiaCredential/CGaiaCredentialBase functions need to be
+// overridden: OnUserAuthenticated, ReportError, GetBaseGlsCommandline,
+// DisplayErrorInUI, ForkGaiaLogonStub, ResetInternalState.
 template <class T>
 class ATL_NO_VTABLE CTestCredentialBase : public T, public ITestCredential {
  public:
@@ -67,30 +72,46 @@
   IFACEMETHODIMP SetStartGlsEventName(
       const base::string16& event_name) override;
   BSTR STDMETHODCALLTYPE GetFinalUsername() override;
+  std::string STDMETHODCALLTYPE GetFinalEmail() override;
   BSTR STDMETHODCALLTYPE GetErrorText() override;
   bool STDMETHODCALLTYPE AreCredentialsValid() override;
-  bool STDMETHODCALLTYPE AreWindowsCredentialsAvailable() override;
-  bool STDMETHODCALLTYPE AreWindowsCredentialsValid() override;
+  bool STDMETHODCALLTYPE CanAttemptWindowsLogon() override;
+  bool STDMETHODCALLTYPE IsWindowsPasswordValidForStoredUser() override;
   void STDMETHODCALLTYPE
   SetWindowsPassword(const CComBSTR& windows_password) override;
 
+  void SignalGlsCompletion();
+
   // IGaiaCredential.
   IFACEMETHODIMP OnUserAuthenticated(BSTR authentication_info,
                                      BSTR* status_text) override;
-
-  // Overrides to build a dummy command line for testing.
-  HRESULT GetBaseGlsCommandline(base::CommandLine* command_line) override;
+  IFACEMETHODIMP ReportError(LONG status,
+                             LONG substatus,
+                             BSTR status_text) override;
+  // CGaiaCredentialBase.
 
   // Override to catch completion of the GLS process on failure and also log
   // the error message.
   void DisplayErrorInUI(LONG status, LONG substatus, BSTR status_text) override;
 
+  // Overrides to build a dummy command line for testing.
+  HRESULT GetBaseGlsCommandline(base::CommandLine* command_line) override;
+
+  // Overrides to check correct startup of GLS process.
+  HRESULT ForkGaiaLogonStub(
+      OSProcessManager* process_manager,
+      const base::CommandLine& command_line,
+      CGaiaCredentialBase::UIProcessInfo* uiprocinfo) override;
+
+  void ResetInternalState() override;
+
   std::string gls_email_;
   std::string gaia_id_override_;
   base::WaitableEvent gls_done_;
   base::win::ScopedHandle process_continue_event_;
   base::string16 start_gls_event_name_;
   CComBSTR error_text_;
+  bool gls_process_started_ = false;
 };
 
 template <class T>
@@ -116,7 +137,8 @@
 
 template <class T>
 HRESULT CTestCredentialBase<T>::WaitForGls() {
-  return gls_done_.TimedWait(base::TimeDelta::FromSeconds(30))
+  return !gls_process_started_ ||
+                 gls_done_.TimedWait(base::TimeDelta::FromSeconds(30))
              ? S_OK
              : HRESULT_FROM_WIN32(WAIT_TIMEOUT);
 }
@@ -136,6 +158,22 @@
 }
 
 template <class T>
+std::string CTestCredentialBase<T>::GetFinalEmail() {
+  const base::DictionaryValue* results = this->get_authentication_results();
+
+  if (!results)
+    return std::string();
+
+  const base::Value* email_value =
+      results->FindKeyOfType(kKeyEmail, base::Value::Type::STRING);
+
+  if (!email_value)
+    return std::string();
+
+  return email_value->GetString();
+}
+
+template <class T>
 BSTR CTestCredentialBase<T>::GetErrorText() {
   return error_text_;
 }
@@ -146,14 +184,14 @@
 }
 
 template <class T>
-bool CTestCredentialBase<T>::AreWindowsCredentialsAvailable() {
-  return T::AreWindowsCredentialsAvailable();
+bool CTestCredentialBase<T>::CanAttemptWindowsLogon() {
+  return T::CanAttemptWindowsLogon();
 }
 
 template <class T>
-bool CTestCredentialBase<T>::AreWindowsCredentialsValid() {
-  return T::AreWindowsCredentialsValid(this->get_current_windows_password()) ==
-         S_OK;
+bool CTestCredentialBase<T>::IsWindowsPasswordValidForStoredUser() {
+  return T::IsWindowsPasswordValidForStoredUser(
+             this->get_current_windows_password()) == S_OK;
 }
 
 template <class T>
@@ -163,23 +201,55 @@
 }
 
 template <class T>
-HRESULT CTestCredentialBase<T>::OnUserAuthenticated(BSTR authentication_info,
-                                                    BSTR* status_text) {
-  HRESULT hr = CGaiaCredentialBase::OnUserAuthenticated(authentication_info,
-                                                        status_text);
+void CTestCredentialBase<T>::SignalGlsCompletion() {
   gls_done_.Signal();
-  return hr;
 }
 
 template <class T>
 HRESULT CTestCredentialBase<T>::GetBaseGlsCommandline(
     base::CommandLine* command_line) {
-  HRESULT hr = FakeGlsRunHelper::GetFakeGlsCommandline(
+  return FakeGlsRunHelper::GetFakeGlsCommandline(
       gls_email_, gaia_id_override_, start_gls_event_name_, command_line);
+}
 
-  // Reset the manual event since GLS will be started upon return.
-  gls_done_.Reset();
+template <class T>
+HRESULT CTestCredentialBase<T>::ForkGaiaLogonStub(
+    OSProcessManager* process_manager,
+    const base::CommandLine& command_line,
+    CGaiaCredentialBase::UIProcessInfo* uiprocinfo) {
+  HRESULT hr = T::ForkGaiaLogonStub(process_manager, command_line, uiprocinfo);
 
+  if (SUCCEEDED(hr)) {
+    gls_process_started_ = true;
+    // Reset the manual event since GLS has started.
+    gls_done_.Reset();
+  }
+
+  return hr;
+}
+
+template <class T>
+HRESULT CTestCredentialBase<T>::OnUserAuthenticated(BSTR authentication_info,
+                                                    BSTR* status_text) {
+  HRESULT hr = T::OnUserAuthenticated(authentication_info, status_text);
+  // Only signal completion if OnUserAuthenticated succeeded otherwise
+  // there will be a call to ReportError right after which should signal
+  // the completion. This is needed to prevent a race condition in tests
+  // where it checks for a failure message, but the failure message is
+  // set after gls_done_ is signalled causing the test to be flaky.
+  if (SUCCEEDED(hr))
+    SignalGlsCompletion();
+  return hr;
+}
+
+template <class T>
+HRESULT CTestCredentialBase<T>::ReportError(LONG status,
+                                            LONG substatus,
+                                            BSTR status_text) {
+  // This function is called instead of (or after) OnUserAuthenticated() when
+  // errors occur, so signal that GLS is done.
+  HRESULT hr = T::ReportError(status, substatus, status_text);
+  SignalGlsCompletion();
   return hr;
 }
 
@@ -187,10 +257,14 @@
 void CTestCredentialBase<T>::DisplayErrorInUI(LONG status,
                                               LONG substatus,
                                               BSTR status_text) {
-  // This function is called instead of OnUserAuthenticated() when errors occur,
-  // so signal that GLS is done.
   error_text_ = status_text;
-  gls_done_.Signal();
+  T::DisplayErrorInUI(status, substatus, status_text);
+}
+
+template <class T>
+void CTestCredentialBase<T>::ResetInternalState() {
+  gls_process_started_ = false;
+  T::ResetInternalState();
 }
 
 // This class is used to implement a test credential based off a fully
diff --git a/chrome/installer/mac/sign_app.sh.in b/chrome/installer/mac/sign_app.sh.in
index 23ad384..16edd99 100644
--- a/chrome/installer/mac/sign_app.sh.in
+++ b/chrome/installer/mac/sign_app.sh.in
@@ -58,7 +58,7 @@
 browser_app="${app_path}"
 framework="${versioned_dir}/@MAC_PRODUCT_NAME@ Framework.framework"
 notification_service="${framework}/XPCServices/AlertNotificationService.xpc"
-crashpad_handler="${framework}/Helpers/crashpad_handler"
+crashpad_handler="${framework}/Helpers/chrome_crashpad_handler"
 helper_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper.app"
 app_mode_loader_app="${framework}/Resources/app_mode_loader.app"
 app_mode_loader="${app_mode_loader_app}/Contents/MacOS/app_mode_loader"
diff --git a/chrome/installer/mac/sign_versioned_dir.sh.in b/chrome/installer/mac/sign_versioned_dir.sh.in
index 2a784e2..b5388e90 100644
--- a/chrome/installer/mac/sign_versioned_dir.sh.in
+++ b/chrome/installer/mac/sign_versioned_dir.sh.in
@@ -87,7 +87,7 @@
 
 framework="${versioned_dir}/@MAC_PRODUCT_NAME@ Framework.framework"
 notification_service="${framework}/XPCServices/AlertNotificationService.xpc"
-crashpad_handler="${framework}/Helpers/crashpad_handler"
+crashpad_handler="${framework}/Helpers/chrome_crashpad_handler"
 helper_app="${versioned_dir}/@MAC_PRODUCT_NAME@ Helper.app"
 app_mode_loader_app="${framework}/Resources/app_mode_loader.app"
 app_mode_loader="${app_mode_loader_app}/Contents/MacOS/app_mode_loader"
diff --git a/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc b/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc
index 851753b..74d6a2c 100644
--- a/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc
+++ b/chrome/renderer/extensions/file_browser_handler_custom_bindings.cc
@@ -58,8 +58,7 @@
             ->Get(v8::String::NewFromUtf8(isolate, "fileIsDirectory",
                                           v8::NewStringType::kInternalized)
                       .ToLocalChecked())
-            ->BooleanValue(context->v8_context())
-            .FromMaybe(false);
+            ->BooleanValue(isolate);
     blink::WebDOMFileSystem::EntryType entry_type =
         is_directory ? blink::WebDOMFileSystem::kEntryTypeDirectory
                      : blink::WebDOMFileSystem::kEntryTypeFile;
diff --git a/chrome/test/data/extensions/api_test/messaging/externally_connectable/sites/assertions.js b/chrome/test/data/extensions/api_test/messaging/externally_connectable/sites/assertions.js
index f62aa588..fe069cb 100644
--- a/chrome/test/data/extensions/api_test/messaging/externally_connectable/sites/assertions.js
+++ b/chrome/test/data/extensions/api_test/messaging/externally_connectable/sites/assertions.js
@@ -94,6 +94,11 @@
 // Make the messages sent vaguely complex, but unambiguously JSON-ifiable.
 var kMessage = [{'a': {'b': 10}}, 20, 'c\x10\x11'];
 
+// Text of the error message set in |chrome.runtime.lastError| when the
+// messaging target does not exist.
+var kCouldNotEstablishConnection =
+    'Could not establish connection. Receiving end does not exist.';
+
 // Our tab's location. Normally this would be our document's location but if
 // we're an iframe it will be the location of the parent - in which case,
 // expect to be told.
@@ -111,8 +116,6 @@
 function checkLastError(reply) {
   if (!chrome.runtime.lastError)
     return true;
-  var kCouldNotEstablishConnection =
-    'Could not establish connection. Receiving end does not exist.';
   if (chrome.runtime.lastError.message == kCouldNotEstablishConnection)
     reply(results.COULD_NOT_ESTABLISH_CONNECTION_ERROR);
   else
diff --git a/chrome/test/data/extensions/api_test/passwords_private/test.js b/chrome/test/data/extensions/api_test/passwords_private/test.js
index 59e74b6e..c9ee605 100644
--- a/chrome/test/data/extensions/api_test/passwords_private/test.js
+++ b/chrome/test/data/extensions/api_test/passwords_private/test.js
@@ -14,13 +14,11 @@
       if (numCalls == 1) {
         chrome.passwordsPrivate.changeSavedPassword(0, 'new_user');
       } else if (numCalls == 2) {
-        chrome.test.assertEq(
-            'new_user', savedPasswordsList[0].loginPair.username);
+        chrome.test.assertEq('new_user', savedPasswordsList[0].username);
         chrome.passwordsPrivate.changeSavedPassword(
             0, 'another_user', 'new_pass');
       } else if (numCalls == 3) {
-        chrome.test.assertEq(
-            'another_user', savedPasswordsList[0].loginPair.username);
+        chrome.test.assertEq('another_user', savedPasswordsList[0].username);
         chrome.test.assertEq(
             'new_pass'.length, savedPasswordsList[0].numCharactersInPassword);
         chrome.test.succeed();
@@ -101,10 +99,10 @@
       var idSet = new Set();
       for (var i = 0; i < list.length; ++i) {
         var entry = list[i];
-        chrome.test.assertTrue(!!entry.loginPair);
-        chrome.test.assertTrue(!!entry.loginPair.urls.origin);
-        chrome.test.assertTrue(!!entry.loginPair.urls.shown);
-        chrome.test.assertTrue(!!entry.loginPair.urls.link);
+        chrome.test.assertTrue(!!entry);
+        chrome.test.assertTrue(!!entry.urls.origin);
+        chrome.test.assertTrue(!!entry.urls.shown);
+        chrome.test.assertTrue(!!entry.urls.link);
         idSet.add(entry.id);
       }
 
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js b/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
index 04d1eea..dc0f2666 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
@@ -184,4 +184,29 @@
       }));
     });
   },
+
+  function testRedirectToUrlWithExtraHeadersListener() {
+    // Set a cookie so the cookie request header is set.
+    navigateAndWait(getSetCookieUrl('foo', 'bar'), function() {
+      var finalURL = getServerURL('echoheader?Cookie');
+      var url = getServerURL('server-redirect?' + finalURL);
+      var listener = callbackPass(function(details) {
+        removeHeader(details.requestHeaders, 'cookie');
+        return {requestHeaders: details.requestHeaders};
+      });
+      chrome.webRequest.onBeforeSendHeaders.addListener(listener,
+          {urls: [finalURL]}, ['requestHeaders', 'blocking', 'extraHeaders']);
+
+      navigateAndWait(url, function(tab) {
+        chrome.test.assertEq(finalURL, tab.url);
+        chrome.webRequest.onBeforeSendHeaders.removeListener(listener);
+        chrome.tabs.executeScript(tab.id, {
+          code: 'document.body.innerText'
+        }, callbackPass(function(results) {
+          chrome.test.assertTrue(results[0].indexOf('bar') == -1,
+              'Header not removed.');
+        }));
+      });
+    });
+  },
 ]);
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_redirects.js b/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
index d4c19779..6d593a9 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
@@ -27,203 +27,253 @@
   });
 }
 
-runTests([
-  function redirectToDataUrlOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: dataURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+chrome.test.getConfig(function(config) {
+  var onHeadersReceivedExtraInfoSpec = ['blocking'];
+  if (config.customArg === 'useExtraHeaders')
+    onHeadersReceivedExtraInfoSpec.push('extraHeaders');
 
-    assertRedirectSucceeds(url, dataURL, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+  runTests([
+    function redirectToDataUrlOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: dataURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function redirectToAboutUrlOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: aboutURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, dataURL, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, aboutURL, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function redirectToAboutUrlOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: aboutURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function redirectToNonWebAccessibleUrlOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: getURLNonWebAccessible()};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, aboutURL, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function redirectToNonWebAccessibleUrlOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: getURLNonWebAccessible()};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function redirectToServerRedirectOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' + getURLWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLWebAccessible(), function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function redirectToServerRedirectOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function serverRedirectThenExtensionRedirectOnHeadersReceived() {
-    var url_1 = getServerURL('echo');
-    var url_2 = getURLWebAccessible();
-    var serverRedirect = getServerURL('server-redirect?' + url_1);
-    var listener = function(details) {
-      return {redirectUrl: url_2};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(
-      listener,
-      { urls: [url_1] },
-      ["blocking"]
-    );
+      assertRedirectSucceeds(url, getURLWebAccessible(), function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(serverRedirect, url_2, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function serverRedirectThenExtensionRedirectOnHeadersReceived() {
+      var url_1 = getServerURL('echo');
+      var url_2 = getURLWebAccessible();
+      var serverRedirect = getServerURL('server-redirect?' + url_1);
+      var listener = function(details) {
+        return {redirectUrl: url_2};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(
+        listener,
+        { urls: [url_1] },
+        ["blocking"]
+      );
 
-  function redirectToUnallowedServerRedirectOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' +
-        getURLNonWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(serverRedirect, url_2, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    // The page should be redirected to redirectURL, but not to the non web
-    // accessible URL.
-    assertRedirectSucceeds(url, redirectURL, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function redirectToUnallowedServerRedirectOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLNonWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function redirectToDataUrlOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: dataURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      // The page should be redirected to redirectURL, but not to the non web
+      // accessible URL.
+      assertRedirectSucceeds(url, redirectURL, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, dataURL, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToDataUrlOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: dataURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function redirectToAboutUrlOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: aboutURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, dataURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, aboutURL, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToAboutUrlOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: aboutURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function redirectToNonWebAccessibleUrlOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: getURLNonWebAccessible()};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, aboutURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToNonWebAccessibleUrlOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: getURLNonWebAccessible()};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function redirectToServerRedirectOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' + getURLWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLWebAccessible(), function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToServerRedirectOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  // A server redirect immediately followed by an extension redirect.
-  // Regression test for:
-  // - https://crbug.com/882661
-  // - https://crbug.com/880741
-  function serverRedirectThenExtensionRedirectOnBeforeRequest() {
-    var url_1 = getServerURL('echo');
-    var url_2 = getURLWebAccessible();
-    var serverRedirect = getServerURL('server-redirect?' + url_1);
-    var listener = function(details) {
-      return {redirectUrl: url_2};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(
-      listener,
-      { urls: [url_1] },
-      ["blocking"]
-    );
+      assertRedirectSucceeds(url, getURLWebAccessible(), function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(serverRedirect, url_2, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    // A server redirect immediately followed by an extension redirect.
+    // Regression test for:
+    // - https://crbug.com/882661
+    // - https://crbug.com/880741
+    function serverRedirectThenExtensionRedirectOnBeforeRequest() {
+      var url_1 = getServerURL('echo');
+      var url_2 = getURLWebAccessible();
+      var serverRedirect = getServerURL('server-redirect?' + url_1);
+      var listener = function(details) {
+        return {redirectUrl: url_2};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(
+        listener,
+        { urls: [url_1] },
+        ["blocking"]
+      );
 
-  function redirectToUnallowedServerRedirectOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' +
-        getURLNonWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(serverRedirect, url_2, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    // The page should be redirected to redirectURL, but not to the non web
-    // accessible URL.
-    assertRedirectSucceeds(url, redirectURL, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToUnallowedServerRedirectOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLNonWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function redirectToAboutUrlWithServerRedirect() {
-    assertRedirectFails(getServerURL('server-redirect?' + aboutURL));
-  },
+      // The page should be redirected to redirectURL, but not to the non web
+      // accessible URL.
+      assertRedirectSucceeds(url, redirectURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-  function redirectToDataUrlWithServerRedirect() {
-    assertRedirectFails(getServerURL('server-redirect?' + dataURL));
-  },
+    function redirectToAboutUrlWithServerRedirect() {
+      assertRedirectFails(getServerURL('server-redirect?' + aboutURL));
+    },
 
-  function redirectToNonWebAccessibleUrlWithServerRedirect() {
-    assertRedirectFails(
-        getServerURL('server-redirect?' + getURLNonWebAccessible()));
-  },
+    function redirectToDataUrlWithServerRedirect() {
+      assertRedirectFails(getServerURL('server-redirect?' + dataURL));
+    },
 
-  function redirectToWebAccessibleUrlWithServerRedirect() {
-    assertRedirectSucceeds(
-        getServerURL('server-redirect?' + getURLWebAccessible()),
-        getURLWebAccessible());
-  },
-]);
+    function redirectToNonWebAccessibleUrlWithServerRedirect() {
+      assertRedirectFails(
+          getServerURL('server-redirect?' + getURLNonWebAccessible()));
+    },
+
+    function redirectToWebAccessibleUrlWithServerRedirect() {
+      assertRedirectSucceeds(
+          getServerURL('server-redirect?' + getURLWebAccessible()),
+          getURLWebAccessible());
+    },
+
+    function beforeRequestRedirectAfterServerRedirect() {
+      var finalURL = getServerURL('echo?foo');
+      var intermediateURL = getServerURL('echo?bar');
+      var redirectURL = getServerURL('server-redirect?' + intermediateURL);
+
+      var onBeforeSendHeadersListener = function(details) {
+        chrome.test.assertFalse(details.url == intermediateURL,
+            'intermediateURL should be redirected before the request starts.');
+      };
+      // Make sure all URLs use the extraHeaders path to expose
+      // http://crbug.com/918761.
+      chrome.webRequest.onBeforeSendHeaders.addListener(
+          onBeforeSendHeadersListener,
+          {urls: ['<all_urls>']}, ['blocking', 'extraHeaders']);
+
+      var onBeforeRequestListener = function(details) {
+        return {redirectUrl: finalURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(onBeforeRequestListener,
+          {urls: [intermediateURL]}, ['blocking']);
+
+      assertRedirectSucceeds(redirectURL, finalURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(
+            onBeforeRequestListener);
+        chrome.webRequest.onBeforeSendHeaders.removeListener(
+            onBeforeSendHeadersListener);
+      });
+    },
+
+    function serverRedirectChain() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getServerURL('server-redirect?' + url));
+      var listener = function(details) {};
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: ['<all_urls>']}, onHeadersReceivedExtraInfoSpec);
+
+      assertRedirectSucceeds(redirectURL, url, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
+  ]);
+});
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_subresource_redirects.js b/chrome/test/data/extensions/api_test/webrequest/test_subresource_redirects.js
index c3686c3..b845b8e 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_subresource_redirects.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_subresource_redirects.js
@@ -46,129 +46,137 @@
   });
 }
 
-runTests([
-  function subresourceRedirectToDataUrlOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: dataURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+chrome.test.getConfig(function(config) {
+  var onHeadersReceivedExtraInfoSpec = ['blocking'];
+  if (config.customArg === 'useExtraHeaders')
+    onHeadersReceivedExtraInfoSpec.push('extraHeaders');
 
-    assertRedirectSucceeds(url, dataURL, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+  runTests([
+    function subresourceRedirectToDataUrlOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: dataURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function subresourceRedirectToNonWebAccessibleUrlOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: getURLNonWebAccessible()};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, dataURL, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function subresourceRedirectToNonWebAccessibleUrlOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: getURLNonWebAccessible()};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function subresourceRedirectToServerRedirectOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' + getURLWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLWebAccessible(), function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function subresourceRedirectToServerRedirectOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function subresourceRedirectToUnallowedServerRedirectOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' +
-        getURLNonWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, getURLWebAccessible(), function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectFails(url, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function subresourceRedirectToUnallowedServerRedirectOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLNonWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function subresourceRedirectToDataUrlOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: dataURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectFails(url, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, dataURL, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function subresourceRedirectToDataUrlOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: dataURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function subresourceRedirectToNonWebAccessibleUrlOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: getURLNonWebAccessible()};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, dataURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function subresourceRedirectToNonWebAccessibleUrlOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: getURLNonWebAccessible()};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function subresourceRedirectToServerRedirectOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' + getURLWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLWebAccessible(), function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function subresourceRedirectToServerRedirectOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function subresourceRedirectToUnallowedServerRedirectOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' +
-        getURLNonWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, getURLWebAccessible(), function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectFails(url, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function subresourceRedirectToUnallowedServerRedirectOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLNonWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function subresourceRedirectToDataUrlWithServerRedirect() {
-    assertRedirectFails(getServerURL('server-redirect?' + dataURL));
-  },
+      assertRedirectFails(url, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-  function subresourceRedirectToNonWebAccessibleWithServerRedirect() {
-    assertRedirectFails(
-        getServerURL('server-redirect?' + getURLNonWebAccessible()));
-  },
+    function subresourceRedirectToDataUrlWithServerRedirect() {
+      assertRedirectFails(getServerURL('server-redirect?' + dataURL));
+    },
 
-  function subresourceRedirectToWebAccessibleWithServerRedirect() {
-    assertRedirectSucceeds(
-        getServerURL('server-redirect?' + getURLWebAccessible()),
-        getURLWebAccessible());
-  },
-]);
+    function subresourceRedirectToNonWebAccessibleWithServerRedirect() {
+      assertRedirectFails(
+          getServerURL('server-redirect?' + getURLNonWebAccessible()));
+    },
+
+    function subresourceRedirectToWebAccessibleWithServerRedirect() {
+      assertRedirectSucceeds(
+          getServerURL('server-redirect?' + getURLWebAccessible()),
+          getURLWebAccessible());
+    },
+  ]);
+});
diff --git a/chrome/test/data/webui/app_management/reducers_test.js b/chrome/test/data/webui/app_management/reducers_test.js
index cfac2873..9ffc4c7 100644
--- a/chrome/test/data/webui/app_management/reducers_test.js
+++ b/chrome/test/data/webui/app_management/reducers_test.js
@@ -130,6 +130,22 @@
     assertEquals(PageType.MAIN, state.currentPage.pageType);
   });
 
+  test('state updates when search starts', function() {
+    // State updates when a search term has been typed in.
+    let action = app_management.actions.setSearchTerm('searchTerm');
+    state = app_management.reduceAction(state, action);
+
+    assertEquals('searchTerm', state.search.term);
+    assertEquals(PageType.SEARCH, state.currentPage.pageType);
+
+    // Search disappears when there is no term entered.
+    action = app_management.actions.clearSearch();
+    state = app_management.reduceAction(state, action);
+    assertEquals(PageType.MAIN, state.currentPage.pageType);
+
+    assertEquals(null, state.search.term);
+  });
+
   test('state updates when changing to notifications page', function() {
     const action = app_management.actions.changePage(PageType.NOTIFICATIONS);
     state = app_management.reduceAction(state, action);
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
index 3cc0dc0..9bf7f0a5a 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.js
@@ -25,14 +25,12 @@
   id = id || 0;
 
   return {
-    loginPair: {
-      urls: {
-        origin: 'http://' + url + '/login',
-        shown: url,
-        link: 'http://' + url + '/login',
-      },
-      username: username,
+    urls: {
+      origin: 'http://' + url + '/login',
+      shown: url,
+      link: 'http://' + url + '/login',
     },
+    username: username,
     numCharactersInPassword: passwordLength,
     id: id,
   };
diff --git a/chrome/test/data/webui/settings/passwords_section_test.js b/chrome/test/data/webui/settings/passwords_section_test.js
index 4c31181..938ac553 100644
--- a/chrome/test/data/webui/settings/passwords_section_test.js
+++ b/chrome/test/data/webui/settings/passwords_section_test.js
@@ -22,11 +22,9 @@
       assert(node);
       const passwordInfo = passwordList[index];
       assertEquals(
-          passwordInfo.loginPair.urls.shown,
-          node.$$('#originUrl').textContent.trim());
-      assertEquals(
-          passwordInfo.loginPair.urls.link, node.$$('#originUrl').href);
-      assertEquals(passwordInfo.loginPair.username, node.$$('#username').value);
+          passwordInfo.urls.shown, node.$$('#originUrl').textContent.trim());
+      assertEquals(passwordInfo.urls.link, node.$$('#originUrl').href);
+      assertEquals(passwordInfo.username, node.$$('#username').value);
       assertEquals(
           passwordInfo.numCharactersInPassword,
           node.$$('#password').value.length);
@@ -146,7 +144,7 @@
    */
   function listContainsUrl(passwordList, url) {
     for (let i = 0; i < passwordList.length; ++i) {
-      if (passwordList[i].loginPair.urls.origin == url) {
+      if (passwordList[i].urls.origin == url) {
         return true;
       }
     }
diff --git a/chrome/test/data/webui/settings/people_page_sync_page_test.js b/chrome/test/data/webui/settings/people_page_sync_page_test.js
index a444757..61a6410e 100644
--- a/chrome/test/data/webui/settings/people_page_sync_page_test.js
+++ b/chrome/test/data/webui/settings/people_page_sync_page_test.js
@@ -540,19 +540,20 @@
 
         // Clicking the setup cancel button opens the 'Cancel sync?' dialog.
         cancelButton.click();
-        Polymer.dom.flush();
+        return test_util.eventToPromise('cr-dialog-open', syncPage)
+            .then(() => {
+              assertEquals(settings.routes.SYNC, settings.getCurrentRoute());
+              assertTrue(syncPage.$$('#setupCancelDialog').open);
 
-        assertEquals(settings.routes.SYNC, settings.getCurrentRoute());
-        assertTrue(!!syncPage.$$('#setupCancelDialog'));
-        assertTrue(syncPage.$$('#setupCancelDialog').open);
+              // Clicking the cancel button on the 'Cancel sync?' dialog closes
+              // the dialog and removes it from the DOM.
+              syncPage.$$('#setupCancelDialog')
+                  .querySelector('.cancel-button')
+                  .click();
 
-        // Clicking the cancel button on the 'Cancel sync?' dialog closes the
-        // dialog and removes it from the DOM.
-        syncPage.$$('#setupCancelDialog')
-            .querySelector('.cancel-button')
-            .click();
-        return test_util
-            .eventToPromise('close', syncPage.$$('#setupCancelDialog'))
+              return test_util.eventToPromise(
+                  'close', syncPage.$$('#setupCancelDialog'));
+            })
             .then(() => {
               Polymer.dom.flush();
               assertEquals(settings.routes.SYNC, settings.getCurrentRoute());
@@ -561,18 +562,19 @@
               // Clicking the setup cancel button shows the 'Cancel sync?'
               // dialog again.
               cancelButton.click();
-              Polymer.dom.flush();
+              return test_util.eventToPromise('cr-dialog-open', syncPage);
+            })
+            .then(() => {
               assertTrue(syncPage.$$('#setupCancelDialog').open);
 
               // Clicking the confirm button on the dialog aborts sync.
               syncPage.$$('#setupCancelDialog')
                   .querySelector('.action-button')
                   .click();
-
-              return browserProxy.whenCalled('didNavigateAwayFromSyncPage')
-                  .then(abort => {
-                    assertTrue(abort);
-                  });
+              return browserProxy.whenCalled('didNavigateAwayFromSyncPage');
+            })
+            .then(abort => {
+              assertTrue(abort);
             });
       });
 
@@ -614,17 +616,18 @@
         // Navigating away while setup is in progress opens the 'Cancel sync?'
         // dialog.
         settings.navigateTo(settings.routes.BASIC);
-        Polymer.dom.flush();
+        return test_util.eventToPromise('cr-dialog-open', syncPage)
+            .then(() => {
+              assertEquals(settings.routes.SYNC, settings.getCurrentRoute());
+              assertTrue(syncPage.$$('#setupCancelDialog').open);
 
-        assertEquals(settings.routes.SYNC, settings.getCurrentRoute());
-        assertTrue(syncPage.$$('#setupCancelDialog').open);
+              // Clicking the confirm button on the dialog aborts sync.
+              syncPage.$$('#setupCancelDialog')
+                  .querySelector('.action-button')
+                  .click();
 
-        // Clicking the confirm button on the dialog aborts sync.
-        syncPage.$$('#setupCancelDialog')
-            .querySelector('.action-button')
-            .click();
-
-        return browserProxy.whenCalled('didNavigateAwayFromSyncPage')
+              return browserProxy.whenCalled('didNavigateAwayFromSyncPage');
+            })
             .then(abort => {
               assertTrue(abort);
             });
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 62ed305..55992d93 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -197,6 +197,8 @@
     "sync_utils.h",
     "test_data_creator.cc",
     "test_data_creator.h",
+    "travel_field.cc",
+    "travel_field.h",
     "ui/card_unmask_prompt_controller.h",
     "ui/card_unmask_prompt_controller_impl.cc",
     "ui/card_unmask_prompt_controller_impl.h",
diff --git a/components/autofill/core/browser/form_field.cc b/components/autofill/core/browser/form_field.cc
index 3328e18..460de2a 100644
--- a/components/autofill/core/browser/form_field.cc
+++ b/components/autofill/core/browser/form_field.cc
@@ -24,6 +24,7 @@
 #include "components/autofill/core/browser/name_field.h"
 #include "components/autofill/core/browser/phone_field.h"
 #include "components/autofill/core/browser/search_field.h"
+#include "components/autofill/core/browser/travel_field.h"
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/autofill/core/common/autofill_regexes.h"
 #include "components/autofill/core/common/autofill_util.h"
@@ -31,14 +32,15 @@
 namespace autofill {
 
 // There's an implicit precedence determined by the values assigned here. Email
-// is currently the most important followed by Phone, Address, Credit Card,
-// Name, and Search.
+// is currently the most important followed by Phone, Travel, Address,
+// Credit Card, Name, and Search.
 const float FormField::kBaseEmailParserScore = 1.4f;
 const float FormField::kBasePhoneParserScore = 1.3f;
-const float FormField::kBaseAddressParserScore = 1.2f;
-const float FormField::kBaseCreditCardParserScore = 1.1f;
-const float FormField::kBaseNameParserScore = 1.0f;
-const float FormField::kBaseSearchParserScore = 0.9f;
+const float FormField::kBaseTravelParserScore = 1.2f;
+const float FormField::kBaseAddressParserScore = 1.1f;
+const float FormField::kBaseCreditCardParserScore = 1.0f;
+const float FormField::kBaseNameParserScore = 0.9f;
+const float FormField::kBaseSearchParserScore = 0.8f;
 
 // static
 FieldCandidatesMap FormField::ParseFormFields(
@@ -69,6 +71,9 @@
   // Phone pass.
   ParseFormFieldsPass(PhoneField::Parse, processed_fields, &field_candidates);
 
+  // Travel pass.
+  ParseFormFieldsPass(TravelField::Parse, processed_fields, &field_candidates);
+
   // Address pass.
   ParseFormFieldsPass(autofill::AddressField::Parse, processed_fields,
                       &field_candidates);
diff --git a/components/autofill/core/browser/form_field.h b/components/autofill/core/browser/form_field.h
index c14e88f..d62888d 100644
--- a/components/autofill/core/browser/form_field.h
+++ b/components/autofill/core/browser/form_field.h
@@ -60,6 +60,7 @@
   // Initial values assigned to FieldCandidates by their corresponding parsers.
   static const float kBaseEmailParserScore;
   static const float kBasePhoneParserScore;
+  static const float kBaseTravelParserScore;
   static const float kBaseAddressParserScore;
   static const float kBaseCreditCardParserScore;
   static const float kBaseNameParserScore;
diff --git a/components/autofill/core/browser/travel_field.cc b/components/autofill/core/browser/travel_field.cc
new file mode 100644
index 0000000..cee6b34
--- /dev/null
+++ b/components/autofill/core/browser/travel_field.cc
@@ -0,0 +1,53 @@
+// Copyright 2019 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.
+
+#include "components/autofill/core/browser/travel_field.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/common/autofill_regex_constants.h"
+
+namespace autofill {
+
+TravelField::~TravelField() = default;
+
+// static
+std::unique_ptr<FormField> TravelField::Parse(AutofillScanner* scanner) {
+  if (!scanner || scanner->IsEnd()) {
+    return nullptr;
+  }
+
+  auto travel_field = std::make_unique<TravelField>();
+  if (ParseField(scanner, base::UTF8ToUTF16(kPassportRe),
+                 &travel_field->passport_) ||
+      ParseField(scanner, base::UTF8ToUTF16(kTravelOriginRe),
+                 &travel_field->origin_) ||
+      ParseField(scanner, base::UTF8ToUTF16(kTravelDestinationRe),
+                 &travel_field->destination_) ||
+      ParseField(scanner, base::UTF8ToUTF16(kFlightRe),
+                 &travel_field->flight_)) {
+    // If any regex matches, then we found a travel field.
+    return std::move(travel_field);
+  }
+
+  return nullptr;
+}
+
+void TravelField::AddClassifications(
+    FieldCandidatesMap* field_candidates) const {
+  // Simply tag all the fields as unknown types. Travel is currently used as
+  // filter.
+  AddClassification(passport_, UNKNOWN_TYPE, kBaseTravelParserScore,
+                    field_candidates);
+  AddClassification(origin_, UNKNOWN_TYPE, kBaseTravelParserScore,
+                    field_candidates);
+  AddClassification(destination_, UNKNOWN_TYPE, kBaseTravelParserScore,
+                    field_candidates);
+  AddClassification(flight_, UNKNOWN_TYPE, kBaseTravelParserScore,
+                    field_candidates);
+}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/travel_field.h b/components/autofill/core/browser/travel_field.h
new file mode 100644
index 0000000..0608d26
--- /dev/null
+++ b/components/autofill/core/browser/travel_field.h
@@ -0,0 +1,33 @@
+// Copyright 2019 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.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_TRAVEL_FIELD_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_TRAVEL_FIELD_H_
+
+#include <memory>
+
+#include "components/autofill/core/browser/autofill_scanner.h"
+#include "components/autofill/core/browser/form_field.h"
+
+namespace autofill {
+
+class TravelField : public FormField {
+ public:
+  ~TravelField() override;
+
+  static std::unique_ptr<FormField> Parse(AutofillScanner* scanner);
+
+ protected:
+  void AddClassifications(FieldCandidatesMap* field_candidates) const override;
+
+ private:
+  // All of the following fields are optional.
+  AutofillField* passport_;
+  AutofillField* origin_;
+  AutofillField* destination_;
+  AutofillField* flight_;
+};
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_TRAVEL_FIELD_H_
diff --git a/components/autofill/core/common/autofill_regex_constants.cc b/components/autofill/core/common/autofill_regex_constants.cc
index d861aa9e..2f7f948 100644
--- a/components/autofill/core/common/autofill_regex_constants.cc
+++ b/components/autofill/core/common/autofill_regex_constants.cc
@@ -21,15 +21,15 @@
 const char kAddressNameIgnoredRe[] = "address.*nickname|address.*label";
 const char kCompanyRe[] =
     "company|business|organization|organisation"
-    "|firma|firmenname"    // de-DE
-    "|empresa"             // es
-    "|societe|société"     // fr-FR
-    "|ragione.?sociale"    // it-IT
-    "|会社"                // ja-JP
-    "|название.?компании"  // ru
-    "|单位|公司"           // zh-CN
-    "|شرکت"                // fa
-    "|회사|직장";          // ko-KR
+    "|(?<!con)firma|firmenname"  // de-DE
+    "|empresa"                   // es
+    "|societe|société"           // fr-FR
+    "|ragione.?sociale"          // it-IT
+    "|会社"                      // ja-JP
+    "|название.?компании"        // ru
+    "|单位|公司"                 // zh-CN
+    "|شرکت"                      // fa
+    "|회사|직장";                // ko-KR
 const char kAddressLine1Re[] =
     "^address$|address[_-]?line(one)?|address1|addr1|street"
     "|(?:shipping|billing)address$"
@@ -79,11 +79,11 @@
 const char kAddressLookupRe[] = "lookup";
 const char kCountryRe[] =
     "country|countries"
-    "|país|pais"  // es
-    "|国"         // ja-JP
-    "|国家"       // zh-CN
-    "|국가|나라"  // ko-KR
-    "|کشور";      // fa
+    "|país|pais"       // es
+    "|(?<!(入|出))国"  // ja-JP
+    "|国家"            // zh-CN
+    "|국가|나라"       // ko-KR
+    "|کشور";           // fa
 const char kCountryLocationRe[] = "location";
 const char kZipCodeRe[] =
     "zip|postal|post.*code|pcode"
@@ -243,6 +243,7 @@
 const char kEmailRe[] =
     "e.?mail"
     "|courriel"                                    // fr
+    "|correo.*electr(o|ó)nico"                     // es-ES
     "|メールアドレス"                              // ja-JP
     "|Электронной.?Почты"                          // ru
     "|邮件|邮箱"                                   // zh-CN
@@ -262,7 +263,7 @@
     "^name|full.?name|your.?name|customer.?name|bill.?name|ship.?name"
     "|name.*first.*last|firstandlastname"
     "|nombre.*y.*apellidos"  // es
-    "|^nom"                  // fr-FR
+    "|^nom(?!bre)"           // fr-FR
     "|お名前|氏名"           // ja-JP
     "|^nome"                 // pt-BR, pt-PT
     "|نام.*نام.*خانوادگی"    // fa
@@ -286,11 +287,15 @@
 const char kMiddleNameRe[] =
     "middle.*name|mname|middle$"
     "|apellido.?materno|lastlastname";  // es
+
+// TODO(crbug.com/928851): Revisit "morada" from pt-PT as it means address and
+// not "last name", and "surename" in pt-PT as it's not Portuguese (or any
+// language?).
 const char kLastNameRe[] =
     "last.*name|lname|surname|last$|secondname|family.*name"
     "|nachname"                            // de-DE
-    "|apellido"                            // es
-    "|famille|^nom"                        // fr-FR
+    "|apellidos?"                          // es
+    "|famille|^nom(?!bre)"                 // fr-FR
     "|cognome"                             // it-IT
     "|姓"                                  // ja-JP
     "|morada|apelidos|surename|sobrenome"  // pt-BR, pt-PT
@@ -329,6 +334,28 @@
     "|ramal";  // pt-BR, pt-PT
 
 /////////////////////////////////////////////////////////////////////////////
+// travel_field.cc
+/////////////////////////////////////////////////////////////////////////////
+
+const char kPassportRe[] =
+    "document.*number|passport"     // en-US
+    "|passeport"                    // fr-FR
+    "|numero.*documento|pasaporte"  // es-ES
+    "|書類";                        // ja-JP
+const char kTravelOriginRe[] =
+    "point.*of.*entry|arrival"                // en-US
+    "|punto.*internaci(o|ó)n|fecha.*llegada"  // es-ES
+    "|入国";                                  // ja-JP
+const char kTravelDestinationRe[] =
+    "departure"               // en-US
+    "|fecha.*salida|destino"  // es-ES
+    "|出国";                  // ja-JP
+const char kFlightRe[] =
+    "airline|flight"                    // en-US
+    "|aerol(i|í)nea|n(u|ú)mero.*vuelo"  // es-ES
+    "|便名|航空会社";                   // ja-JP
+
+/////////////////////////////////////////////////////////////////////////////
 // validation.cc
 /////////////////////////////////////////////////////////////////////////////
 const char kUPIVirtualPaymentAddressRe[] =
diff --git a/components/autofill/core/common/autofill_regex_constants.h b/components/autofill/core/common/autofill_regex_constants.h
index bcb18d1c..8583722 100644
--- a/components/autofill/core/common/autofill_regex_constants.h
+++ b/components/autofill/core/common/autofill_regex_constants.h
@@ -56,6 +56,10 @@
 extern const char kPhoneSuffixRe[];
 extern const char kPhoneExtensionRe[];
 extern const char kSearchTermRe[];
+extern const char kPassportRe[];
+extern const char kTravelOriginRe[];
+extern const char kTravelDestinationRe[];
+extern const char kFlightRe[];
 
 // Used to match field data that might be a UPI Virtual Payment Address.
 // See:
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 125113e..bf0e00d 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -87,7 +87,8 @@
     "metrics.cc",
     "metrics.h",
     "overlay_state.h",
-    "payment_information.h",
+    "payment_request.cc",
+    "payment_request.h",
     "protocol_utils.cc",
     "protocol_utils.h",
     "rectf.cc",
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 1c5ebf2..f30b18a3 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -30,7 +30,6 @@
 
 namespace autofill_assistant {
 class ClientMemory;
-struct PaymentInformation;
 
 // Action delegate called when processing actions.
 class ActionDelegate {
@@ -91,9 +90,7 @@
   // Asks the user to provide the data used by UseAddressAction and
   // UseCreditCardAction.
   virtual void GetPaymentInformation(
-      payments::mojom::PaymentOptionsPtr payment_options,
-      base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
-      const std::vector<std::string>& supported_basic_card_networks) = 0;
+      std::unique_ptr<PaymentRequestOptions> options) = 0;
 
   using GetFullCardCallback =
       base::OnceCallback<void(std::unique_ptr<autofill::CreditCard> card,
diff --git a/components/autofill_assistant/browser/actions/get_payment_information_action.cc b/components/autofill_assistant/browser/actions/get_payment_information_action.cc
index b3dc9a0..7b838dd 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action.cc
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action.cc
@@ -4,6 +4,7 @@
 
 #include "components/autofill_assistant/browser/actions/get_payment_information_action.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
@@ -33,8 +34,7 @@
   const GetPaymentInformationProto& get_payment_information =
       proto_.get_payment_information();
 
-  payments::mojom::PaymentOptionsPtr payment_options =
-      payments::mojom::PaymentOptions::New();
+  auto payment_options = std::make_unique<PaymentRequestOptions>();
   if (get_payment_information.has_contact_details()) {
     auto contact_details = get_payment_information.contact_details();
     payment_options->request_payer_email =
@@ -44,23 +44,20 @@
         contact_details.request_payer_phone();
   }
 
-  std::vector<std::string> supported_basic_card_networks;
   std::copy(get_payment_information.supported_basic_card_networks().begin(),
             get_payment_information.supported_basic_card_networks().end(),
-            std::back_inserter(supported_basic_card_networks));
+            std::back_inserter(payment_options->supported_basic_card_networks));
 
   payment_options->request_shipping =
       !get_payment_information.shipping_address_name().empty();
-
-  delegate->GetPaymentInformation(
-      std::move(payment_options),
+  payment_options->callback =
       base::BindOnce(&GetPaymentInformationAction::OnGetPaymentInformation,
                      weak_ptr_factory_.GetWeakPtr(), delegate,
-                     std::move(get_payment_information), std::move(callback)),
-      supported_basic_card_networks);
+                     std::move(get_payment_information), std::move(callback));
   if (get_payment_information.has_prompt()) {
     delegate->SetStatusMessage(get_payment_information.prompt());
   }
+  delegate->GetPaymentInformation(std::move(payment_options));
 }
 
 void GetPaymentInformationAction::OnGetPaymentInformation(
diff --git a/components/autofill_assistant/browser/actions/get_payment_information_action.h b/components/autofill_assistant/browser/actions/get_payment_information_action.h
index cce34f8..2a28b94 100644
--- a/components/autofill_assistant/browser/actions/get_payment_information_action.h
+++ b/components/autofill_assistant/browser/actions/get_payment_information_action.h
@@ -12,7 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill_assistant/browser/actions/action.h"
-#include "components/autofill_assistant/browser/payment_information.h"
+#include "components/autofill_assistant/browser/payment_request.h"
 
 namespace autofill_assistant {
 
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 99bc53f..5e6ae9a 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -93,12 +93,8 @@
                void(const Selector& selector,
                     base::OnceCallback<void(bool)> callback));
 
-  MOCK_METHOD3(
-      GetPaymentInformation,
-      void(payments::mojom::PaymentOptionsPtr payment_options,
-           base::OnceCallback<void(std::unique_ptr<PaymentInformation>)>
-               callback,
-           const std::vector<std::string>& supported_basic_card_networks));
+  MOCK_METHOD1(GetPaymentInformation,
+               void(std::unique_ptr<PaymentRequestOptions> options));
 
   MOCK_METHOD1(GetFullCard, void(GetFullCardCallback callback));
 
diff --git a/components/autofill_assistant/browser/actions/show_progress_bar_action.cc b/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
index 88fad8bd..abc8560 100644
--- a/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
+++ b/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
@@ -24,11 +24,6 @@
 void ShowProgressBarAction::InternalProcessAction(
     ActionDelegate* delegate,
     ProcessActionCallback callback) {
-  if (proto_.show_progress_bar().progress() == 0) {
-    // Old script might still contain a ShowProgressBar action that clears the
-    // progress. Ignore these.
-    return;
-  }
   if (!proto_.show_progress_bar().message().empty()) {
     delegate->SetStatusMessage(proto_.show_progress_bar().message());
   }
diff --git a/components/autofill_assistant/browser/client.h b/components/autofill_assistant/browser/client.h
index 1bb17d42..d8fa36f 100644
--- a/components/autofill_assistant/browser/client.h
+++ b/components/autofill_assistant/browser/client.h
@@ -7,6 +7,8 @@
 
 #include <string>
 
+#include "components/autofill_assistant/browser/metrics.h"
+
 namespace autofill {
 class PersonalDataManager;
 }  // namespace autofill
@@ -21,6 +23,12 @@
  public:
   virtual ~Client() = default;
 
+  // Show the UI, creates one if necessary.
+  virtual void ShowUI() = 0;
+
+  // Destroys the UI, but keep the controller, if any exists.
+  virtual void DestroyUI() = 0;
+
   // Returns the API key to be used for requests to the backend.
   virtual std::string GetApiKey() = 0;
 
@@ -48,7 +56,7 @@
 
   // Stops autofill assistant for the current WebContents, both controller and
   // UI.
-  virtual void Stop() = 0;
+  virtual void Shutdown(Metrics::DropOutReason reason) = 0;
 
  protected:
   Client() = default;
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 81185d6..e11a6c70 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -55,7 +55,6 @@
     "WEBSITE_VISITED_BEFORE";
 
 static const char* const kTrueValue = "true";
-
 }  // namespace
 
 Controller::Controller(content::WebContents* web_contents, Client* client)
@@ -184,9 +183,18 @@
   std::move(callback).Run();
 }
 
+void Controller::StopAndShutdown(Metrics::DropOutReason reason) {
+  ClearDetails();
+  SetChips(nullptr);
+  SetPaymentRequestOptions(nullptr);
+  EnterState(AutofillAssistantState::STOPPED);
+  client_->Shutdown(reason);
+}
+
 void Controller::EnterState(AutofillAssistantState state) {
   if (state_ == state)
     return;
+
   DCHECK_NE(state_, AutofillAssistantState::STOPPED)
       << "Unexpected transition from STOPPED to " << static_cast<int>(state);
 
@@ -319,26 +327,27 @@
 
   switch (result.at_end) {
     case ScriptExecutor::SHUTDOWN:
-      GetUiController()->Shutdown(Metrics::SCRIPT_SHUTDOWN);
+      client_->Shutdown(Metrics::SCRIPT_SHUTDOWN);
       return;
+
     case ScriptExecutor::TERMINATE:
       // TODO(crbug.com/806868): Distinguish shutdown from terminate: Users
       // should be allowed to undo shutdown, but not terminate.
       //
-      // This is coming from a client Stop() to clean up and we already counted
-      // it as a stop event. The code here is only executed if no script was
-      // running, so there may be some double counting.
-      GetUiController()->Shutdown(Metrics::SAFETY_NET_TERMINATE);
+      // There should have been a previous call to Terminate() that set the
+      // reason, thus SAFETY_NET_TERMINATE should never be logged, unless
+      // there's a bug.
+      DCHECK_NE(terminate_reason_, Metrics::SAFETY_NET_TERMINATE);
+      client_->Shutdown(terminate_reason_);
       return;
 
     case ScriptExecutor::SHUTDOWN_GRACEFULLY:
       GetWebController()->ClearCookie();
-      stop_reason_ = Metrics::SCRIPT_SHUTDOWN;
-      EnterState(AutofillAssistantState::STOPPED);
+      StopAndShutdown(Metrics::SCRIPT_SHUTDOWN);
       return;
 
     case ScriptExecutor::CLOSE_CUSTOM_TAB:
-      GetUiController()->Close();
+      client_->Shutdown(Metrics::CUSTOM_TAB_CLOSED);
       return;
 
     case ScriptExecutor::RESTART:
@@ -358,20 +367,6 @@
   GetOrCheckScripts(web_contents()->GetLastCommittedURL());
 }
 
-void Controller::OnFatalError(const std::string& error_message,
-                              Metrics::DropOutReason reason) {
-  LOG(ERROR) << "Autofill Assistant has encountered an error and is shutting "
-                "down. Reason: "
-             << static_cast<int>(reason);
-  if (state_ == AutofillAssistantState::STOPPED)
-    return;
-
-  StopPeriodicScriptChecks();
-  SetStatusMessage(error_message);
-  stop_reason_ = reason;
-  EnterState(AutofillAssistantState::STOPPED);
-}
-
 bool Controller::MaybeAutostartScript(
     const std::vector<ScriptHandle>& runnable_scripts) {
   // We want to g through all runnable autostart interrupts first, one at a
@@ -454,8 +449,9 @@
     NOTREACHED();
     return;
   }
-  EnterState(AutofillAssistantState::STARTING);
   parameters_ = parameters;
+  EnterState(AutofillAssistantState::STARTING);
+  client_->ShowUI();
   if (IsCookieExperimentEnabled()) {
     GetWebController()->HasCookie(
         base::BindOnce(&Controller::OnGetCookie,
@@ -470,10 +466,16 @@
   return state_;
 }
 
-bool Controller::Terminate() {
+bool Controller::Terminate(Metrics::DropOutReason reason) {
   StopPeriodicScriptChecks();
-  if (script_tracker_)
-    return script_tracker_->Terminate();
+  if (!will_shutdown_) {
+    will_shutdown_ = true;
+    GetUiController()->WillShutdown(reason);
+  }
+  if (script_tracker_ && !script_tracker_->Terminate()) {
+    terminate_reason_ = reason;
+    return false;
+  }
   return true;
 }
 
@@ -516,8 +518,18 @@
   return output_js;
 }
 
-Metrics::DropOutReason Controller::GetDropOutReason() const {
-  return stop_reason_;
+const PaymentRequestOptions* Controller::GetPaymentRequestOptions() const {
+  return payment_request_options_.get();
+}
+
+void Controller::SetPaymentInformation(
+    std::unique_ptr<PaymentInformation> payment_information) {
+  if (!payment_request_options_)
+    return;
+
+  auto callback = std::move(payment_request_options_->callback);
+  SetPaymentRequestOptions(nullptr);
+  std::move(callback).Run(std::move(payment_information));
 }
 
 void Controller::GetTouchableArea(std::vector<RectF>* area) const {
@@ -525,6 +537,19 @@
     touchable_element_area_->GetArea(area);
 }
 
+void Controller::OnFatalError(const std::string& error_message,
+                              Metrics::DropOutReason reason) {
+  LOG(ERROR) << "Autofill Assistant has encountered an error and is shutting "
+                "down. Reason: "
+             << static_cast<int>(reason);
+  if (state_ == AutofillAssistantState::STOPPED)
+    return;
+
+  StopPeriodicScriptChecks();
+  SetStatusMessage(error_message);
+  StopAndShutdown(reason);
+}
+
 void Controller::OnNoRunnableScriptsAnymore() {
   if (script_tracker()->running())
     return;
@@ -597,7 +622,7 @@
 }
 
 void Controller::DidAttachInterstitialPage() {
-  GetUiController()->Shutdown(Metrics::INTERSTITIAL_PAGE);
+  client_->Shutdown(Metrics::INTERSTITIAL_PAGE);
 }
 
 void Controller::DidFinishLoad(content::RenderFrameHost* render_frame_host,
@@ -642,7 +667,15 @@
 }
 
 void Controller::RenderProcessGone(base::TerminationStatus status) {
-  GetUiController()->Shutdown(Metrics::RENDER_PROCESS_GONE);
+  client_->Shutdown(Metrics::RENDER_PROCESS_GONE);
+}
+
+void Controller::OnWebContentsFocused(
+    content::RenderWidgetHost* render_widget_host) {
+  if (state_ != AutofillAssistantState::INACTIVE &&
+      state_ != AutofillAssistantState::STOPPED) {
+    client_->ShowUI();
+  }
 }
 
 void Controller::LoadProgressChanged(content::WebContents* source,
@@ -667,14 +700,26 @@
   return iter != parameters_.end() && iter->second == "1";
 }
 
+void Controller::OnTouchableAreaChanged(const std::vector<RectF>& areas) {
+  GetUiController()->OnTouchableAreaChanged(areas);
+}
+
+void Controller::SetPaymentRequestOptions(
+    std::unique_ptr<PaymentRequestOptions> options) {
+  DCHECK(!options || options->callback);
+
+  if (payment_request_options_ == nullptr && options == nullptr)
+    return;
+
+  payment_request_options_ = std::move(options);
+  GetUiController()->OnPaymentRequestChanged(payment_request_options_.get());
+}
+
 ElementArea* Controller::touchable_element_area() {
   if (!touchable_element_area_) {
     touchable_element_area_ = std::make_unique<ElementArea>(this);
     touchable_element_area_->SetOnUpdate(base::BindRepeating(
-        &UiController::OnTouchableAreaChanged,
-        // Unretained is safe, since touchable_element_area_ is guaranteed to be
-        // deleted before the UI controller.
-        base::Unretained(GetUiController())));
+        &Controller::OnTouchableAreaChanged, weak_ptr_factory_.GetWeakPtr()));
   }
   return touchable_element_area_.get();
 }
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index ad218db..622d813 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -15,6 +15,7 @@
 #include "components/autofill_assistant/browser/client_memory.h"
 #include "components/autofill_assistant/browser/element_area.h"
 #include "components/autofill_assistant/browser/metrics.h"
+#include "components/autofill_assistant/browser/payment_request.h"
 #include "components/autofill_assistant/browser/script.h"
 #include "components/autofill_assistant/browser/script_executor_delegate.h"
 #include "components/autofill_assistant/browser/script_tracker.h"
@@ -54,9 +55,8 @@
   // Initiates a clean shutdown.
   //
   // This function returns false when it needs more time to properly shut down
-  // the script tracker. It usually means that it either has to wait for a
-  // script to find an appropriate moment to suspend execution or wait for a
-  // script checking round to complete.
+  // the script tracker. In that case, the controller is responsible for calling
+  // Client::Shutdown at the right time for the given reason.
   //
   // A caller is expected to try again later when this function returns false. A
   // return value of true means that the scrip tracker can safely be destroyed.
@@ -64,7 +64,7 @@
   // TODO(crbug.com/806868): Instead of this safety net, the proper fix is to
   // switch to weak pointers everywhere so that dangling callbacks are not an
   // issue.
-  bool Terminate();
+  bool Terminate(Metrics::DropOutReason reason);
 
   // Overrides ScriptExecutorDelegate:
   Service* GetService() override;
@@ -81,8 +81,14 @@
   void ClearDetails() override;
   void SetProgress(int progress) override;
   void SetChips(std::unique_ptr<std::vector<Chip>> chips) override;
+
+  // Stops the controller with |reason| and destroys this. The current status
+  // message must contain the error message.
+  void StopAndShutdown(Metrics::DropOutReason reason);
   void EnterState(AutofillAssistantState state) override;
   bool IsCookieExperimentEnabled() const;
+  void SetPaymentRequestOptions(
+      std::unique_ptr<PaymentRequestOptions> options) override;
 
   // Overrides autofill_assistant::UiDelegate:
   AutofillAssistantState GetState() override;
@@ -93,8 +99,12 @@
   const std::vector<Chip>& GetChips() const override;
   void SelectChip(int chip_index) override;
   std::string GetDebugContext() override;
-  Metrics::DropOutReason GetDropOutReason() const override;
+  const PaymentRequestOptions* GetPaymentRequestOptions() const override;
+  void SetPaymentInformation(
+      std::unique_ptr<PaymentInformation> payment_information) override;
   void GetTouchableArea(std::vector<RectF>* area) const override;
+  void OnFatalError(const std::string& error_message,
+                    Metrics::DropOutReason reason) override;
 
  private:
   friend ControllerTest;
@@ -118,10 +128,6 @@
   void StopPeriodicScriptChecks();
   void OnPeriodicScriptCheck();
 
-  // Shows the given message and stops the controller with |reason|.
-  void OnFatalError(const std::string& error_message,
-                    Metrics::DropOutReason reason);
-
   // Runs autostart scripts from |runnable_scripts|, if the conditions are
   // right. Returns true if a script was auto-started.
   bool MaybeAutostartScript(const std::vector<ScriptHandle>& runnable_scripts);
@@ -153,10 +159,13 @@
       content::NavigationHandle* navigation_handle) override;
   void DocumentAvailableInMainFrame() override;
   void RenderProcessGone(base::TerminationStatus status) override;
+  void OnWebContentsFocused(
+      content::RenderWidgetHost* render_widget_host) override;
 
   // Overrides content::WebContentsDelegate:
   void LoadProgressChanged(content::WebContents* source,
                            double progress) override;
+  void OnTouchableAreaChanged(const std::vector<RectF>& areas);
 
   ElementArea* touchable_element_area();
   ScriptTracker* script_tracker();
@@ -214,8 +223,14 @@
   // Flag indicates whether it is ready to fetch and execute scripts.
   bool started_ = false;
 
-  // Drop out reason set when the controller enters a STOPPED state.
-  Metrics::DropOutReason stop_reason_ = Metrics::AA_START;
+  // A reason passed previously to Terminate(). SAFETY_NET_TERMINATE is a
+  // placeholder.
+  Metrics::DropOutReason terminate_reason_ = Metrics::SAFETY_NET_TERMINATE;
+
+  // True once UiController::WillShutdown has been called.
+  bool will_shutdown_ = false;
+
+  std::unique_ptr<PaymentRequestOptions> payment_request_options_;
 
   // Tracks scripts and script execution. It's kept at the end, as it tend to
   // depend on everything the controller support, through script and script
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index f80260d..8d0c14c 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -58,7 +58,9 @@
   std::string GetAccountEmailAddress() override { return ""; }
   std::string GetLocale() override { return ""; }
   std::string GetCountryCode() override { return ""; }
-  void Stop() override {}
+  MOCK_METHOD1(Shutdown, void(Metrics::DropOutReason reason));
+  MOCK_METHOD0(ShowUI, void());
+  MOCK_METHOD0(DestroyUI, void());
 
  private:
   UiController* ui_controller_;
@@ -156,6 +158,10 @@
     controller_->DidFinishLoad(nullptr, url);
   }
 
+  void SimulateWebContentsFocused() {
+    controller_->OnWebContentsFocused(nullptr);
+  }
+
   void SimulateProgressChanged(double progress) {
     controller_->LoadProgressChanged(web_contents(), progress);
   }
@@ -188,7 +194,7 @@
   std::vector<AutofillAssistantState> states_;
   MockService* mock_service_;
   MockWebController* mock_web_controller_;
-  FakeClient fake_client_;
+  NiceMock<FakeClient> fake_client_;
   NiceMock<MockUiController> mock_ui_controller_;
   content::WebContentsTester* tester_;
 
@@ -308,9 +314,14 @@
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("stop"), _, _, _, _, _))
       .WillOnce(RunOnceCallback<5>(true, actions_response_str));
 
-  EXPECT_CALL(mock_ui_controller_, Shutdown(_));
+  testing::InSequence seq;
+  EXPECT_CALL(fake_client_, Shutdown(Metrics::SCRIPT_SHUTDOWN));
 
   ExecuteScript("stop");
+
+  // Simulates Client::Shutdown(SCRIPT_SHUTDOWN)
+  EXPECT_CALL(mock_ui_controller_, WillShutdown(Metrics::SCRIPT_SHUTDOWN));
+  EXPECT_TRUE(controller_->Terminate(Metrics::SCRIPT_SHUTDOWN));
 }
 
 TEST_F(ControllerTest, Reset) {
@@ -574,4 +585,22 @@
                                    AutofillAssistantState::STOPPED));
 }
 
+TEST_F(ControllerTest, ShowUIWhenStarting) {
+  EXPECT_CALL(fake_client_, ShowUI());
+  Start();
+}
+
+TEST_F(ControllerTest, ShowUIWhenContentsFocused) {
+  SimulateWebContentsFocused();  // must not call ShowUI
+
+  testing::InSequence seq;
+  EXPECT_CALL(fake_client_, ShowUI());
+  Start();  // must call ShowUI
+  EXPECT_CALL(fake_client_, ShowUI());
+  SimulateWebContentsFocused();  // must call ShowUI
+
+  controller_->OnFatalError("test", Metrics::TAB_CHANGED);
+  SimulateWebContentsFocused();  // must not call ShowUI
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index 8b0cd87..d83253d 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -70,4 +70,9 @@
     std::unique_ptr<std::vector<Chip>> chips) {
   chips_ = std::move(chips);
 }
+
+void FakeScriptExecutorDelegate::SetPaymentRequestOptions(
+    std::unique_ptr<PaymentRequestOptions> options) {
+  payment_request_options_ = std::move(options);
+}
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
index fa9c5ec..ea07bec 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -37,6 +37,8 @@
   void ClearDetails() override;
   void SetProgress(int progress) override;
   void SetChips(std::unique_ptr<std::vector<Chip>> chips) override;
+  void SetPaymentRequestOptions(
+      std::unique_ptr<PaymentRequestOptions> options) override;
 
   void SetService(Service* service) { service_ = service; }
 
@@ -58,6 +60,8 @@
 
   std::vector<Chip>* GetChips() { return chips_.get(); }
 
+  PaymentRequestOptions* GetOptions() { return payment_request_options_.get(); }
+
  private:
   Service* service_ = nullptr;
   UiController* ui_controller_ = nullptr;
@@ -68,6 +72,7 @@
   std::string status_message_;
   std::unique_ptr<Details> details_;
   std::unique_ptr<std::vector<Chip>> chips_;
+  std::unique_ptr<PaymentRequestOptions> payment_request_options_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeScriptExecutorDelegate);
 };
diff --git a/components/autofill_assistant/browser/mock_ui_controller.h b/components/autofill_assistant/browser/mock_ui_controller.h
index 1777834..78e54fc 100644
--- a/components/autofill_assistant/browser/mock_ui_controller.h
+++ b/components/autofill_assistant/browser/mock_ui_controller.h
@@ -24,15 +24,10 @@
 
   MOCK_METHOD1(OnStatusMessageChanged, void(const std::string& message));
   MOCK_METHOD1(OnStateChanged, void(AutofillAssistantState));
-  MOCK_METHOD1(Shutdown, void(Metrics::DropOutReason));
-  MOCK_METHOD0(Close, void());
+  MOCK_METHOD1(WillShutdown, void(Metrics::DropOutReason));
   MOCK_METHOD1(OnChipsChanged, void(const std::vector<Chip>& chips));
-  MOCK_METHOD3(
-      GetPaymentInformation,
-      void(payments::mojom::PaymentOptionsPtr payment_options,
-           base::OnceCallback<void(std::unique_ptr<PaymentInformation>)>
-               callback,
-           const std::vector<std::string>& supported_basic_card_networks));
+  MOCK_METHOD1(OnPaymentRequestChanged,
+               void(const PaymentRequestOptions* options));
   MOCK_METHOD1(OnDetailsChanged, void(const Details* details));
   MOCK_METHOD1(OnProgressChanged, void(int progress));
   MOCK_METHOD1(OnTouchableAreaChanged, void(const std::vector<RectF>& areas));
diff --git a/components/autofill_assistant/browser/payment_information.h b/components/autofill_assistant/browser/payment_information.h
deleted file mode 100644
index d4ffdf0..0000000
--- a/components/autofill_assistant/browser/payment_information.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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.
-
-#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PAYMENT_INFORMATION_H_
-#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PAYMENT_INFORMATION_H_
-
-#include <string>
-
-namespace autofill {
-class AutofillProfile;
-class CreditCard;
-}  // namespace autofill
-
-namespace autofill_assistant {
-
-// Struct for holding the payment information data.
-struct PaymentInformation {
-  PaymentInformation();
-  ~PaymentInformation();
-
-  bool succeed;
-  std::unique_ptr<autofill::CreditCard> card;
-  std::unique_ptr<autofill::AutofillProfile> shipping_address;
-  std::unique_ptr<autofill::AutofillProfile> billing_address;
-  std::string payer_name;
-  std::string payer_phone;
-  std::string payer_email;
-  bool is_terms_and_conditions_accepted;
-};
-}  // namespace autofill_assistant
-#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PAYMENT_INFORMATION_H_
diff --git a/components/autofill_assistant/browser/payment_request.cc b/components/autofill_assistant/browser/payment_request.cc
new file mode 100644
index 0000000..8fb37f6
--- /dev/null
+++ b/components/autofill_assistant/browser/payment_request.cc
@@ -0,0 +1,18 @@
+// Copyright 2019 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.
+
+#include "components/autofill_assistant/browser/payment_request.h"
+
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
+
+namespace autofill_assistant {
+
+PaymentInformation::PaymentInformation() = default;
+PaymentInformation::~PaymentInformation() = default;
+
+PaymentRequestOptions::PaymentRequestOptions() = default;
+PaymentRequestOptions::~PaymentRequestOptions() = default;
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/payment_request.h b/components/autofill_assistant/browser/payment_request.h
new file mode 100644
index 0000000..77fa420
--- /dev/null
+++ b/components/autofill_assistant/browser/payment_request.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PAYMENT_REQUEST_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PAYMENT_REQUEST_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+
+namespace autofill {
+class AutofillProfile;
+class CreditCard;
+}  // namespace autofill
+
+namespace autofill_assistant {
+
+// Struct for holding the payment information data.
+struct PaymentInformation {
+  PaymentInformation();
+  ~PaymentInformation();
+
+  bool succeed = false;
+  std::unique_ptr<autofill::CreditCard> card;
+  std::unique_ptr<autofill::AutofillProfile> shipping_address;
+  std::unique_ptr<autofill::AutofillProfile> billing_address;
+  std::string payer_name;
+  std::string payer_phone;
+  std::string payer_email;
+  bool is_terms_and_conditions_accepted = false;
+};
+
+// Struct for holding the payment request options.
+struct PaymentRequestOptions {
+  PaymentRequestOptions();
+  ~PaymentRequestOptions();
+
+  bool request_payer_name = false;
+  bool request_payer_email = false;
+  bool request_payer_phone = false;
+  bool request_shipping = false;
+  std::vector<std::string> supported_basic_card_networks;
+  std::string default_email;
+
+  base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback;
+};
+
+}  // namespace autofill_assistant
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_PAYMENT_REQUEST_H_
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index a21b94cd..e1de1c8 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -123,15 +123,12 @@
 }
 
 void ScriptExecutor::GetPaymentInformation(
-    payments::mojom::PaymentOptionsPtr payment_options,
-    base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
-    const std::vector<std::string>& supported_basic_card_networks) {
+    std::unique_ptr<PaymentRequestOptions> options) {
+  options->callback = base::BindOnce(&ScriptExecutor::OnGetPaymentInformation,
+                                     weak_ptr_factory_.GetWeakPtr(),
+                                     std::move(options->callback));
+  delegate_->SetPaymentRequestOptions(std::move(options));
   delegate_->EnterState(AutofillAssistantState::PROMPT);
-  delegate_->GetUiController()->GetPaymentInformation(
-      std::move(payment_options),
-      base::BindOnce(&ScriptExecutor::OnGetPaymentInformation,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
-      supported_basic_card_networks);
 }
 
 void ScriptExecutor::OnGetPaymentInformation(
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index e342375..18fdcadd 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -111,9 +111,7 @@
   void ClickOrTapElement(const Selector& selector,
                          base::OnceCallback<void(bool)> callback) override;
   void GetPaymentInformation(
-      payments::mojom::PaymentOptionsPtr payment_options,
-      base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
-      const std::vector<std::string>& supported_basic_card_networks) override;
+      std::unique_ptr<PaymentRequestOptions> options) override;
   void GetFullCard(GetFullCardCallback callback) override;
   void Prompt(std::unique_ptr<std::vector<Chip>> chips) override;
   void CancelPrompt() override;
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index c7c2a54a..a2deab1 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -12,6 +12,7 @@
 
 #include "components/autofill_assistant/browser/chip.h"
 #include "components/autofill_assistant/browser/details.h"
+#include "components/autofill_assistant/browser/payment_request.h"
 #include "components/autofill_assistant/browser/state.h"
 
 namespace autofill {
@@ -54,6 +55,8 @@
   virtual std::string GetStatusMessage() const = 0;
   virtual void SetDetails(const Details& details) = 0;
   virtual void ClearDetails() = 0;
+  virtual void SetPaymentRequestOptions(
+      std::unique_ptr<PaymentRequestOptions> options) = 0;
   virtual void SetProgress(int progress) = 0;
   virtual void SetChips(std::unique_ptr<std::vector<Chip>> chips) = 0;
 
diff --git a/components/autofill_assistant/browser/ui_controller.cc b/components/autofill_assistant/browser/ui_controller.cc
index bb1f8c0..077370ea 100644
--- a/components/autofill_assistant/browser/ui_controller.cc
+++ b/components/autofill_assistant/browser/ui_controller.cc
@@ -9,8 +9,17 @@
 
 namespace autofill_assistant {
 
-PaymentInformation::PaymentInformation() : succeed(false) {}
+UiController::UiController() = default;
+UiController::~UiController() = default;
 
-PaymentInformation::~PaymentInformation() = default;
+void UiController::OnStateChanged(AutofillAssistantState new_state) {}
+void UiController::OnStatusMessageChanged(const std::string& message) {}
+void UiController::WillShutdown(Metrics::DropOutReason reason) {}
+void UiController::OnChipsChanged(const std::vector<Chip>& chips) {}
+void UiController::OnPaymentRequestChanged(
+    const PaymentRequestOptions* options) {}
+void UiController::OnDetailsChanged(const Details* details) {}
+void UiController::OnProgressChanged(int progress) {}
+void UiController::OnTouchableAreaChanged(const std::vector<RectF>& areas) {}
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/ui_controller.h b/components/autofill_assistant/browser/ui_controller.h
index d15be9e3..d90beef7d 100644
--- a/components/autofill_assistant/browser/ui_controller.h
+++ b/components/autofill_assistant/browser/ui_controller.h
@@ -13,7 +13,7 @@
 #include "components/autofill_assistant/browser/chip.h"
 #include "components/autofill_assistant/browser/details.h"
 #include "components/autofill_assistant/browser/metrics.h"
-#include "components/autofill_assistant/browser/payment_information.h"
+#include "components/autofill_assistant/browser/payment_request.h"
 #include "components/autofill_assistant/browser/script.h"
 #include "components/autofill_assistant/browser/state.h"
 #include "components/autofill_assistant/browser/ui_delegate.h"
@@ -22,51 +22,46 @@
 namespace autofill_assistant {
 
 // Controller to control autofill assistant UI.
+//
+// TODO(crbug/925947): Rename this class to Observer and make treat it as a real
+// observer in the Controller.
 class UiController {
  public:
-  virtual ~UiController() = default;
+  UiController();
+  virtual ~UiController();
 
   // Called when the controller has entered a new state.
-  virtual void OnStateChanged(AutofillAssistantState new_state) = 0;
+  virtual void OnStateChanged(AutofillAssistantState new_state);
 
   // Report that the status message has changed.
-  virtual void OnStatusMessageChanged(const std::string& message) = 0;
+  virtual void OnStatusMessageChanged(const std::string& message);
 
-  // Shuts down Autofill Assistant: hide the UI and frees any associated state.
+  // Autofill Assistant is about to be shut down for this tab.
   //
-  // Warning: this indirectly deletes the caller.
-  virtual void Shutdown(Metrics::DropOutReason reason) = 0;
-
-  // Shuts down Autofill Assistant and closes Chrome.
-  virtual void Close() = 0;
+  // Pointer to UIDelegate will become invalid as soon as this method has
+  // returned.
+  virtual void WillShutdown(Metrics::DropOutReason reason);
 
   // Report that the set of chips has changed.
-  virtual void OnChipsChanged(const std::vector<Chip>& chips) = 0;
+  virtual void OnChipsChanged(const std::vector<Chip>& chips);
 
-  // Get payment information (through similar to payment request UX) to fill
-  // forms.
-  virtual void GetPaymentInformation(
-      payments::mojom::PaymentOptionsPtr payment_options,
-      base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
-      const std::vector<std::string>& supported_basic_card_networks) = 0;
+  // Gets or clears request for payment information.
+  virtual void OnPaymentRequestChanged(const PaymentRequestOptions* options);
 
   // Called when details have changed. Details will be null if they have been
   // cleared.
-  virtual void OnDetailsChanged(const Details* details) = 0;
+  virtual void OnDetailsChanged(const Details* details);
 
   // Called when the current progress has changed. Progress, is expressed as a
   // percentage.
-  virtual void OnProgressChanged(int progress) = 0;
+  virtual void OnProgressChanged(int progress);
 
   // Updates the area of the visible viewport that is accessible when the
   // overlay state is OverlayState::PARTIAL.
   //
   // |areas| is expressed in coordinates relative to the width or height of the
   // visible viewport, as a number between 0 and 1. It can be empty.
-  virtual void OnTouchableAreaChanged(const std::vector<RectF>& areas) = 0;
-
- protected:
-  UiController() = default;
+  virtual void OnTouchableAreaChanged(const std::vector<RectF>& areas);
 };
 }  // namespace autofill_assistant
 #endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_CONTROLLER_H_
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index a881872..45878ad 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -5,11 +5,13 @@
 #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_DELEGATE_H_
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_DELEGATE_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "base/optional.h"
 #include "components/autofill_assistant/browser/metrics.h"
+#include "components/autofill_assistant/browser/payment_request.h"
 #include "components/autofill_assistant/browser/rectf.h"
 #include "components/autofill_assistant/browser/state.h"
 
@@ -51,8 +53,14 @@
   // Selects a chip, from the set of chips returned by GetChips().
   virtual void SelectChip(int chip) = 0;
 
-  // Returns the drop out reason for the last state transition to STOPPED.
-  virtual Metrics::DropOutReason GetDropOutReason() const = 0;
+  // If the controller is waiting for payment request information, this
+  // field contains a non-null options describing the request.
+  virtual const PaymentRequestOptions* GetPaymentRequestOptions() const = 0;
+
+  // Sets payment information, in response to the current payment request
+  // options.
+  virtual void SetPaymentInformation(
+      std::unique_ptr<PaymentInformation> payment_information) = 0;
 
   // Adds the rectangles that correspond to the current touchable area to the
   // given vector.
@@ -63,6 +71,10 @@
   // Note that the vector is not cleared before rectangles are added.
   virtual void GetTouchableArea(std::vector<RectF>* area) const = 0;
 
+  // Reports a fatal error to Autofill Assistant, which should then stop.
+  virtual void OnFatalError(const std::string& error_message,
+                            Metrics::DropOutReason reason) = 0;
+
  protected:
   UiDelegate() = default;
 };
diff --git a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
index 959377ad..5a9c60e 100644
--- a/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
+++ b/components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler/TaskIds.java
@@ -31,6 +31,7 @@
     public static final int COMPONENT_UPDATE_JOB_ID = 2;
     public static final int DEPRECATED_EXPLORE_SITES_REFRESH_JOB_ID = 100;
     public static final int EXPLORE_SITES_REFRESH_JOB_ID = 101;
+    public static final int BACKGROUND_SYNC_ONE_SHOT_JOB_ID = 102;
 
     private TaskIds() {}
 }
diff --git a/components/browser_sync/profile_sync_components_factory_impl.cc b/components/browser_sync/profile_sync_components_factory_impl.cc
index 545eaaa2..911fc67 100644
--- a/components/browser_sync/profile_sync_components_factory_impl.cc
+++ b/components/browser_sync/profile_sync_components_factory_impl.cc
@@ -432,8 +432,8 @@
     syncer::ModelTypeConfigurer* configurer,
     DataTypeManagerObserver* observer) {
   return std::make_unique<DataTypeManagerImpl>(
-      sync_client_, initial_types, debug_info_listener, controllers,
-      encryption_handler, configurer, observer);
+      initial_types, debug_info_listener, controllers, encryption_handler,
+      configurer, observer);
 }
 
 std::unique_ptr<syncer::SyncEngine>
diff --git a/components/browser_sync/profile_sync_service_autofill_unittest.cc b/components/browser_sync/profile_sync_service_autofill_unittest.cc
index 1d047f8..29014a89 100644
--- a/components/browser_sync/profile_sync_service_autofill_unittest.cc
+++ b/components/browser_sync/profile_sync_service_autofill_unittest.cc
@@ -328,11 +328,9 @@
   DISALLOW_COPY_AND_ASSIGN(WebDataServiceFake);
 };
 
-ACTION_P2(ReturnNewDataTypeManagerWithDebugListener,
-          sync_client,
-          debug_listener) {
-  return std::make_unique<syncer::DataTypeManagerImpl>(
-      sync_client, arg0, debug_listener, arg2, arg3, arg4, arg5);
+ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) {
+  return std::make_unique<syncer::DataTypeManagerImpl>(arg0, debug_listener,
+                                                       arg2, arg3, arg4, arg5);
 }
 
 class MockPersonalDataManager : public PersonalDataManager {
@@ -451,7 +449,6 @@
     EXPECT_CALL(*profile_sync_service_bundle()->component_factory(),
                 CreateDataTypeManager(_, _, _, _, _, _))
         .WillOnce(ReturnNewDataTypeManagerWithDebugListener(
-            sync_client_,
             syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr())));
 
     EXPECT_CALL(personal_data_manager(), IsDataLoaded())
diff --git a/components/browser_sync/sync_auth_manager.cc b/components/browser_sync/sync_auth_manager.cc
index 0ceb5f6..07e28b8 100644
--- a/components/browser_sync/sync_auth_manager.cc
+++ b/components/browser_sync/sync_auth_manager.cc
@@ -246,7 +246,7 @@
 }
 
 void SyncAuthManager::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   UpdateSyncAccountIfNecessary();
 }
 
diff --git a/components/browser_sync/sync_auth_manager.h b/components/browser_sync/sync_auth_manager.h
index a860d54..925482e 100644
--- a/components/browser_sync/sync_auth_manager.h
+++ b/components/browser_sync/sync_auth_manager.h
@@ -89,7 +89,8 @@
   void Clear();
 
   // identity::IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
   void OnRefreshTokenUpdatedForAccount(
diff --git a/components/feedback/anonymizer_tool.cc b/components/feedback/anonymizer_tool.cc
index 02fc89dd..b5acfd7 100644
--- a/components/feedback/anonymizer_tool.cc
+++ b/components/feedback/anonymizer_tool.cc
@@ -473,6 +473,13 @@
   return result;
 }
 
+bool WhitelistMatchedId(re2::StringPiece matched_id) {
+  bool is_safe_chrome_resource =
+      matched_id.starts_with("chrome://resources/") &&
+      !matched_id.contains("?");
+  return is_safe_chrome_resource;
+}
+
 std::string AnonymizerTool::AnonymizeCustomPatternWithoutContext(
     const std::string& input,
     const CustomPatternWithoutContext& pattern,
@@ -488,6 +495,11 @@
   re2::StringPiece skipped;
   re2::StringPiece matched_id;
   while (FindAndConsumeAndGetSkipped(&text, *re, &skipped, &matched_id)) {
+    if (WhitelistMatchedId(matched_id)) {
+      skipped.AppendToString(&result);
+      matched_id.AppendToString(&result);
+      continue;
+    }
     std::string matched_id_as_string = matched_id.as_string();
     std::string replacement_id = (*identifier_space)[matched_id_as_string];
     if (replacement_id.empty()) {
diff --git a/components/feedback/anonymizer_tool_unittest.cc b/components/feedback/anonymizer_tool_unittest.cc
index 190dfad..c2f9a6e 100644
--- a/components/feedback/anonymizer_tool_unittest.cc
+++ b/components/feedback/anonymizer_tool_unittest.cc
@@ -269,7 +269,9 @@
       "64:ff9b::a0a:a0a\n"       // IPv4-translated 6to4 IPV6 (private class A).
       "64:ff9b::6473:5c01\n"     // IPv4-translated 6to4 IPV6 (Chrome).
       "::0101:ffff:c0a8:640a\n"  // IP address.
-      "aa:aa:aa:aa:aa:aa";       // MAC address (BSSID).
+      "aa:aa:aa:aa:aa:aa\n"      // MAC address (BSSID).
+      "chrome://resources/foo\n"        // Secure chrome resource, whitelisted.
+      "chrome://resources/f?user=bar";  // Potentially PII in parameter.
   std::string result =
       "aaaaaaaa [SSID=1]aaaaa\n"
       "aaaaaaaa<URL: 1>\n"
@@ -336,7 +338,9 @@
       "<T 10.0.0.0/8: 25>\n"
       "<T 100.115.92.1: 26>\n"
       "<IPv6: 27>\n"
-      "aa:aa:aa:00:00:01";
+      "aa:aa:aa:00:00:01\n"
+      "chrome://resources/foo\n"
+      "<URL: 2>";
   EXPECT_EQ(result, anonymizer_.Anonymize(data));
 }
 
diff --git a/components/gcm_driver/account_tracker.cc b/components/gcm_driver/account_tracker.cc
index a40ce9e..a460194 100644
--- a/components/gcm_driver/account_tracker.cc
+++ b/components/gcm_driver/account_tracker.cc
@@ -89,7 +89,7 @@
 }
 
 void AccountTracker::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   TRACE_EVENT0("identity", "AccountTracker::OnPrimaryAccountSet");
 
   std::vector<AccountInfo> accounts =
diff --git a/components/gcm_driver/account_tracker.h b/components/gcm_driver/account_tracker.h
index bdb9336..961bcd42 100644
--- a/components/gcm_driver/account_tracker.h
+++ b/components/gcm_driver/account_tracker.h
@@ -77,7 +77,8 @@
   };
 
   // identity::IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
   void OnRefreshTokenUpdatedForAccount(
diff --git a/components/gcm_driver/gcm_profile_service.cc b/components/gcm_driver/gcm_profile_service.cc
index fe19208..f43314d 100644
--- a/components/gcm_driver/gcm_profile_service.cc
+++ b/components/gcm_driver/gcm_profile_service.cc
@@ -47,7 +47,8 @@
   ~IdentityObserver() override;
 
   // identity::IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 
@@ -88,7 +89,7 @@
 }
 
 void GCMProfileService::IdentityObserver::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   // This might be called multiple times when the password changes.
   if (primary_account_info.account_id == account_id_)
     return;
diff --git a/components/ntp_snippets/breaking_news/subscription_manager_impl.cc b/components/ntp_snippets/breaking_news/subscription_manager_impl.cc
index 87bd9c27..11fbcf9 100644
--- a/components/ntp_snippets/breaking_news/subscription_manager_impl.cc
+++ b/components/ntp_snippets/breaking_news/subscription_manager_impl.cc
@@ -229,7 +229,7 @@
 }
 
 void SubscriptionManagerImpl::OnPrimaryAccountSet(
-    const AccountInfo& account_info) {
+    const CoreAccountInfo& account_info) {
   SigninStatusChanged();
 }
 
diff --git a/components/ntp_snippets/breaking_news/subscription_manager_impl.h b/components/ntp_snippets/breaking_news/subscription_manager_impl.h
index 9bf77fd7..1e769eaa 100644
--- a/components/ntp_snippets/breaking_news/subscription_manager_impl.h
+++ b/components/ntp_snippets/breaking_news/subscription_manager_impl.h
@@ -68,7 +68,7 @@
 
  private:
   // identity:IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(const AccountInfo& account_info) override;
+  void OnPrimaryAccountSet(const CoreAccountInfo& account_info) override;
   void OnPrimaryAccountCleared(const AccountInfo& account_info) override;
 
   void SigninStatusChanged();
diff --git a/components/ntp_snippets/content_suggestions_service.cc b/components/ntp_snippets/content_suggestions_service.cc
index dbfaa37..33cdbba8 100644
--- a/components/ntp_snippets/content_suggestions_service.cc
+++ b/components/ntp_snippets/content_suggestions_service.cc
@@ -522,7 +522,7 @@
 }
 // identity::IdentityManager::Observer implementation
 void ContentSuggestionsService::OnPrimaryAccountSet(
-    const AccountInfo& account_info) {
+    const CoreAccountInfo& account_info) {
   OnSignInStateChanged(/*has_signed_in=*/true);
 }
 
diff --git a/components/ntp_snippets/content_suggestions_service.h b/components/ntp_snippets/content_suggestions_service.h
index aa7b3a0e..eb1954e 100644
--- a/components/ntp_snippets/content_suggestions_service.h
+++ b/components/ntp_snippets/content_suggestions_service.h
@@ -289,7 +289,7 @@
       const ContentSuggestion::ID& suggestion_id) override;
 
   // identity::IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(const AccountInfo& account_info) override;
+  void OnPrimaryAccountSet(const CoreAccountInfo& account_info) override;
   void OnPrimaryAccountCleared(const AccountInfo& account_info) override;
 
   // history::HistoryServiceObserver implementation.
diff --git a/components/password_manager/core/browser/votes_uploader.cc b/components/password_manager/core/browser/votes_uploader.cc
index b52b9cd..334955d3 100644
--- a/components/password_manager/core/browser/votes_uploader.cc
+++ b/components/password_manager/core/browser/votes_uploader.cc
@@ -155,8 +155,8 @@
   if (pending_credentials->times_used == 0) {
     UploadPasswordVote(*pending_credentials, submitted_form, autofill::PASSWORD,
                        std::string());
-    if (has_username_correction_vote_) {
-      UploadPasswordVote(username_correction_vote_, submitted_form,
+    if (username_correction_vote_) {
+      UploadPasswordVote(*username_correction_vote_, submitted_form,
                          autofill::USERNAME,
                          FormStructure(observed).FormSignatureAsStr());
     }
@@ -448,13 +448,12 @@
 bool VotesUploader::FindUsernameInOtherPossibleUsernames(
     const PasswordForm& match,
     const base::string16& username) {
-  DCHECK(!has_username_correction_vote_);
+  DCHECK(!username_correction_vote_);
 
   for (const ValueElementPair& pair : match.other_possible_usernames) {
     if (pair.first == username) {
       username_correction_vote_ = match;
-      username_correction_vote_.username_element = pair.second;
-      has_username_correction_vote_ = true;
+      username_correction_vote_->username_element = pair.second;
       return true;
     }
   }
diff --git a/components/password_manager/core/browser/votes_uploader.h b/components/password_manager/core/browser/votes_uploader.h
index a8318d1..c02d45d 100644
--- a/components/password_manager/core/browser/votes_uploader.h
+++ b/components/password_manager/core/browser/votes_uploader.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/strings/string16.h"
+#include "base/optional.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/proto/server.pb.h"
 #include "components/autofill/core/common/password_form.h"
@@ -173,13 +174,11 @@
 
   // If the user typed username that doesn't match any saved credentials, but
   // matches an entry from |other_possible_usernames| of a saved credential,
-  // then |has_username_correction_vote_| is set to true and
-  // |username_correction_vote_| stores the credential with matched  username.
+  // |username_correction_vote_| stores the credential with matched username.
   // The matched credential is copied to |username_correction_vote_|, but
   // |username_correction_vote_.username_element| is set to the name of the
-  // field where matched username was found.
-  bool has_username_correction_vote_ = false;
-  autofill::PasswordForm username_correction_vote_;
+  // field where the matched username was found.
+  base::Optional<autofill::PasswordForm> username_correction_vote_;
 
   // Whether the password values have been shown to the user on the save prompt.
   bool has_passwords_revealed_vote_ = false;
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index 6b853dc..3ae1a62 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -268,12 +268,6 @@
     return;
   }
 
-  if (!details->total) {
-    log_.Error("Missing total");
-    OnConnectionTerminated();
-    return;
-  }
-
   spec_->UpdateWith(std::move(details));
 }
 
diff --git a/components/previews/content/previews_content_util_unittest.cc b/components/previews/content/previews_content_util_unittest.cc
index 6abf0c4..902ffa75 100644
--- a/components/previews/content/previews_content_util_unittest.cc
+++ b/components/previews/content/previews_content_util_unittest.cc
@@ -432,8 +432,7 @@
   EXPECT_EQ(content::CLIENT_LOFI_ON,
             previews::DetermineCommittedClientPreviewsState(
                 &user_data, GURL("https://www.google.com"),
-                content::CLIENT_LOFI_ON | content::NOSCRIPT_ON |
-                    content::RESOURCE_LOADING_HINTS_ON,
+                content::CLIENT_LOFI_ON | content::NOSCRIPT_ON,
                 enabled_previews_decider()));
 }
 
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc
index d87fbd0..dedd90a 100644
--- a/components/previews/content/previews_decider_impl_unittest.cc
+++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -1125,7 +1125,7 @@
       1);
 }
 
-TEST_F(PreviewsDeciderImplTest, ResourceLoadingHintsDisallowedByDefault) {
+TEST_F(PreviewsDeciderImplTest, ResourceLoadingHintsAllowedByDefault) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatures(
       {features::kPreviews, features::kOptimizationHints}, {});
@@ -1133,9 +1133,16 @@
 
   base::HistogramTester histogram_tester;
   PreviewsUserData user_data(kDefaultPageId);
-  EXPECT_FALSE(previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
-      &user_data, GURL("https://www.google.com"), false,
-      PreviewsType::RESOURCE_LOADING_HINTS));
+
+#if defined(OS_ANDROID)
+  bool expected = true;
+#else   // !defined(OS_ANDROID)
+  bool expected = false;
+#endif  // defined(OS_ANDROID)
+  EXPECT_EQ(expected,
+            previews_decider_impl()->ShouldAllowPreviewAtNavigationStart(
+                &user_data, GURL("https://www.google.com"), false,
+                PreviewsType::RESOURCE_LOADING_HINTS));
 }
 
 TEST_F(PreviewsDeciderImplTest,
diff --git a/components/previews/core/previews_features.cc b/components/previews/core/previews_features.cc
index 6c87ad1..3816111 100644
--- a/components/previews/core/previews_features.cc
+++ b/components/previews/core/previews_features.cc
@@ -74,8 +74,14 @@
     "OptimizationHintsExperiments", base::FEATURE_DISABLED_BY_DEFAULT};
 
 // Enables the application of the resource loading hints when loading resources.
-const base::Feature kResourceLoadingHints{"ResourceLoadingHints",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kResourceLoadingHints {
+  "ResourceLoadingHints",
+#if defined(OS_ANDROID)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else   // !defined(OS_ANDROID)
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif  // defined(OS_ANDROID)
+};
 
 // Enables client redirects to a server-rendered lite page preview.
 const base::Feature kLitePageServerPreviews{"LitePageServerPreviews",
diff --git a/components/sessions/core/session_service_commands.cc b/components/sessions/core/session_service_commands.cc
index 1de12aae..1a58d05 100644
--- a/components/sessions/core/session_service_commands.cc
+++ b/components/sessions/core/session_service_commands.cc
@@ -24,16 +24,24 @@
 // OBSOLETE Superseded by kCommandSetWindowBounds3.
 // static const SessionCommand::id_type kCommandSetWindowBounds = 1;
 static const SessionCommand::id_type kCommandSetTabIndexInWindow = 2;
+
+// OBSOLETE: Preserved for backward compatibility. Using
+// kCommandTabNavigationPathPruned instead
 static const SessionCommand::id_type
     kCommandTabNavigationPathPrunedFromBack = 5;
+
 static const SessionCommand::id_type kCommandUpdateTabNavigation = 6;
 static const SessionCommand::id_type kCommandSetSelectedNavigationIndex = 7;
 static const SessionCommand::id_type kCommandSetSelectedTabInIndex = 8;
 static const SessionCommand::id_type kCommandSetWindowType = 9;
 // OBSOLETE Superseded by kCommandSetWindowBounds3. Except for data migration.
 // static const SessionCommand::id_type kCommandSetWindowBounds2 = 10;
+
+// OBSOLETE: Preserved for backward compatibility. Using
+// kCommandTabNavigationPathPruned instead
 static const SessionCommand::id_type
     kCommandTabNavigationPathPrunedFromFront = 11;
+
 static const SessionCommand::id_type kCommandSetPinnedState = 12;
 static const SessionCommand::id_type kCommandSetExtensionAppID = 13;
 static const SessionCommand::id_type kCommandSetWindowBounds3 = 14;
@@ -47,6 +55,7 @@
 // OBSOLETE Superseded by kCommandSetWindowWorkspace2.
 // static const SessionCommand::id_type kCommandSetWindowWorkspace = 22;
 static const SessionCommand::id_type kCommandSetWindowWorkspace2 = 23;
+static const SessionCommand::id_type kCommandTabNavigationPathPruned = 24;
 
 namespace {
 
@@ -93,6 +102,14 @@
 
 using TabNavigationPathPrunedFromFrontPayload = IDAndIndexPayload;
 
+struct TabNavigationPathPrunedPayload {
+  SessionID::id_type id;
+  // Index starting which |count| entries were removed.
+  int32_t index;
+  // Number of entries removed.
+  int32_t count;
+};
+
 struct PinnedStatePayload {
   SessionID::id_type tab_id;
   bool pinned_state;
@@ -310,6 +327,31 @@
   tabs->clear();
 }
 
+void ProcessTabNavigationPathPrunedCommand(
+    TabNavigationPathPrunedPayload& payload,
+    SessionTab* tab) {
+  // Update the selected navigation index.
+  if (tab->current_navigation_index >= payload.index &&
+      tab->current_navigation_index < payload.index + payload.count) {
+    tab->current_navigation_index = payload.index - 1;
+  } else if (tab->current_navigation_index >= payload.index + payload.count) {
+    tab->current_navigation_index =
+        tab->current_navigation_index - payload.count;
+  }  // Else no change if selected index is before payload.index
+
+  tab->navigations.erase(
+      FindClosestNavigationWithIndex(&(tab->navigations), payload.index),
+      FindClosestNavigationWithIndex(&(tab->navigations),
+                                     payload.index + payload.count));
+
+  // And update the index of existing navigations.
+  for (auto& entry : tab->navigations) {
+    if (entry.index() < payload.index)
+      continue;
+    entry.set_index(entry.index() - payload.count);
+  }
+}
+
 // Creates tabs and windows from the commands specified in |data|. The created
 // tabs and windows are added to |tabs| and |windows| respectively, with the
 // id of the active window set in |active_window_id|. It is up to the caller
@@ -409,6 +451,7 @@
         }
         SessionTab* tab =
             GetTab(SessionID::FromSerializedValue(payload.id), tabs);
+
         tab->navigations.erase(
             FindClosestNavigationWithIndex(&(tab->navigations), payload.index),
             tab->navigations.end());
@@ -416,27 +459,34 @@
       }
 
       case kCommandTabNavigationPathPrunedFromFront: {
-        TabNavigationPathPrunedFromFrontPayload payload;
+        TabNavigationPathPrunedFromFrontPayload prune_front_payload;
+        if (!command->GetPayload(&prune_front_payload,
+                                 sizeof(prune_front_payload)) ||
+            prune_front_payload.index <= 0) {
+          DVLOG(1) << "Failed reading command " << command->id();
+          return true;
+        }
+        SessionTab* tab = GetTab(
+            SessionID::FromSerializedValue(prune_front_payload.id), tabs);
+
+        TabNavigationPathPrunedPayload payload;
+        payload.index = 0;
+        payload.count = prune_front_payload.index;
+        ProcessTabNavigationPathPrunedCommand(payload, tab);
+        break;
+      }
+
+      case kCommandTabNavigationPathPruned: {
+        TabNavigationPathPrunedPayload payload;
         if (!command->GetPayload(&payload, sizeof(payload)) ||
-            payload.index <= 0) {
+            payload.index < 0 || payload.count <= 0) {
           DVLOG(1) << "Failed reading command " << command->id();
           return true;
         }
         SessionTab* tab =
             GetTab(SessionID::FromSerializedValue(payload.id), tabs);
 
-        // Update the selected navigation index.
-        tab->current_navigation_index =
-            std::max(-1, tab->current_navigation_index - payload.index);
-
-        // And update the index of existing navigations.
-        for (auto i = tab->navigations.begin(); i != tab->navigations.end();) {
-          i->set_index(i->index() - payload.index);
-          if (i->index() < 0)
-            i = tab->navigations.erase(i);
-          else
-            ++i;
-        }
+        ProcessTabNavigationPathPrunedCommand(payload, tab);
         break;
       }
 
@@ -764,26 +814,16 @@
   return command;
 }
 
-std::unique_ptr<SessionCommand> CreateTabNavigationPathPrunedFromBackCommand(
+std::unique_ptr<SessionCommand> CreateTabNavigationPathPrunedCommand(
     const SessionID& tab_id,
+    int index,
     int count) {
-  TabNavigationPathPrunedFromBackPayload payload = { 0 };
+  TabNavigationPathPrunedPayload payload = {0};
   payload.id = tab_id.id();
-  payload.index = count;
+  payload.index = index;
+  payload.count = count;
   std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
-      kCommandTabNavigationPathPrunedFromBack, sizeof(payload));
-  memcpy(command->contents(), &payload, sizeof(payload));
-  return command;
-}
-
-std::unique_ptr<SessionCommand> CreateTabNavigationPathPrunedFromFrontCommand(
-    const SessionID& tab_id,
-    int count) {
-  TabNavigationPathPrunedFromFrontPayload payload = { 0 };
-  payload.id = tab_id.id();
-  payload.index = count;
-  std::unique_ptr<SessionCommand> command = std::make_unique<SessionCommand>(
-      kCommandTabNavigationPathPrunedFromFront, sizeof(payload));
+      kCommandTabNavigationPathPruned, sizeof(payload));
   memcpy(command->contents(), &payload, sizeof(payload));
   return command;
 }
diff --git a/components/sessions/core/session_service_commands.h b/components/sessions/core/session_service_commands.h
index 1e3a1746..0ed6fa72 100644
--- a/components/sessions/core/session_service_commands.h
+++ b/components/sessions/core/session_service_commands.h
@@ -54,11 +54,9 @@
 SESSIONS_EXPORT std::unique_ptr<SessionCommand> CreateSetActiveWindowCommand(
     const SessionID& window_id);
 SESSIONS_EXPORT std::unique_ptr<SessionCommand>
-CreateTabNavigationPathPrunedFromBackCommand(const SessionID& tab_id,
-                                             int count);
-SESSIONS_EXPORT std::unique_ptr<SessionCommand>
-CreateTabNavigationPathPrunedFromFrontCommand(const SessionID& tab_id,
-                                              int count);
+CreateTabNavigationPathPrunedCommand(const SessionID& tab_id,
+                                     int index,
+                                     int count);
 SESSIONS_EXPORT std::unique_ptr<SessionCommand>
 CreateUpdateTabNavigationCommand(
     const SessionID& tab_id,
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index ac645cd1..d1c69fe 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -78,6 +78,8 @@
     "device_id_helper.h",
     "gaia_cookie_manager_service.cc",
     "gaia_cookie_manager_service.h",
+    "oauth2_token_service_delegate_android.cc",
+    "oauth2_token_service_delegate_android.h",
     "profile_oauth2_token_service.cc",
     "profile_oauth2_token_service.h",
     "signin_client.cc",
@@ -136,8 +138,6 @@
     "mirror_account_reconcilor_delegate.h",
     "mutable_profile_oauth2_token_service_delegate.cc",
     "mutable_profile_oauth2_token_service_delegate.h",
-    "oauth2_token_service_delegate_android.cc",
-    "oauth2_token_service_delegate_android.h",
     "signin_error_controller.cc",
     "signin_error_controller.h",
     "signin_header_helper.cc",
@@ -207,10 +207,6 @@
       "dice_header_helper.h",
     ]
   }
-
-  if (is_android) {
-    deps += [ "android:jni_headers" ]
-  }
 }
 
 # This target contains test support that backs the test support for
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc
index d8684e7..64d55b2 100644
--- a/components/signin/core/browser/about_signin_internals.cc
+++ b/components/signin/core/browser/about_signin_internals.cc
@@ -435,7 +435,7 @@
 }
 
 void AboutSigninInternals::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   NotifyObservers();
 }
 
diff --git a/components/signin/core/browser/about_signin_internals.h b/components/signin/core/browser/about_signin_internals.h
index 3a1ca42..5eb722d 100644
--- a/components/signin/core/browser/about_signin_internals.h
+++ b/components/signin/core/browser/about_signin_internals.h
@@ -216,7 +216,8 @@
   void OnEndBatchOfRefreshTokenStateChanges() override;
   void OnPrimaryAccountSigninFailed(
       const GoogleServiceAuthError& error) override;
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& primary_account_info) override;
 
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc
index f0c70130..57808ec 100644
--- a/components/signin/core/browser/account_reconcilor.cc
+++ b/components/signin/core/browser/account_reconcilor.cc
@@ -247,7 +247,6 @@
 #endif  // defined(OS_IOS)
 
 void AccountReconcilor::EnableReconcile() {
-  DCHECK(delegate_->IsReconcileEnabled());
   RegisterWithAllDependencies();
 #if !defined(OS_IOS)
   // TODO(droger): Investigate why this breaks tests on iOS.
diff --git a/components/signin/core/browser/fake_account_fetcher_service.h b/components/signin/core/browser/fake_account_fetcher_service.h
index 9e1298f..3c00f92 100644
--- a/components/signin/core/browser/fake_account_fetcher_service.h
+++ b/components/signin/core/browser/fake_account_fetcher_service.h
@@ -18,13 +18,6 @@
 // to prevent AccountTrackerService from sending network requests.
 class FakeAccountFetcherService : public AccountFetcherService {
  public:
-  void FakeUserInfoFetchSuccess(const std::string& email,
-                                const std::string& gaia,
-                                const std::string& hosted_domain,
-                                const std::string& full_name,
-                                const std::string& given_name,
-                                const std::string& locale,
-                                const std::string& picture_url);
   void FakeUserInfoFetchSuccess(const std::string& account_id,
                                 const std::string& email,
                                 const std::string& gaia,
diff --git a/components/signin/core/browser/gaia_cookie_manager_service.h b/components/signin/core/browser/gaia_cookie_manager_service.h
index 0e62a0b..b623d5a 100644
--- a/components/signin/core/browser/gaia_cookie_manager_service.h
+++ b/components/signin/core/browser/gaia_cookie_manager_service.h
@@ -304,8 +304,6 @@
   // Returns a non-NULL pointer to its instance of net::BackoffEntry
   const net::BackoffEntry* GetBackoffEntry() { return &fetcher_backoff_; }
 
-  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
-
   // Ubertoken fetch completion callback. Called by unittests directly.
   void OnUbertokenFetchComplete(GoogleServiceAuthError error,
                                 const std::string& uber_token);
@@ -324,6 +322,8 @@
   FRIEND_TEST_ALL_PREFIXES(GaiaCookieManagerServiceTest,
                            MultiloginFailureInvalidGaiaCredentialsDesktop);
 
+  scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory();
+
   // Overridden from network::mojom::CookieChangeListner. If the cookie relates
   // to a GAIA APISID cookie, then we call ListAccounts and fire
   // OnGaiaAccountsInCookieUpdated.
diff --git a/components/signin/core/browser/mirror_account_reconcilor_delegate.cc b/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
index c02390d..aa8fdfa 100644
--- a/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/mirror_account_reconcilor_delegate.cc
@@ -59,17 +59,10 @@
             gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER);
   return ReorderChromeAccountsForReconcile(chrome_accounts, primary_account,
                                            gaia_accounts);
-  }
+}
 
 void MirrorAccountReconcilorDelegate::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
-  if (!IsReconcileEnabled()) {
-    // AccountReconcilor::EnableReconcile DCHECKs for
-    // |AccountReconcilorDelegate::IsReconcileEnabled|. Subclasses may have
-    // overridden |AccountReconcilorDelegate::IsReconcileEnabled|. Check that to
-    // be sure.
-    return;
-  }
+    const CoreAccountInfo& primary_account_info) {
   reconcilor()->EnableReconcile();
 }
 
diff --git a/components/signin/core/browser/mirror_account_reconcilor_delegate.h b/components/signin/core/browser/mirror_account_reconcilor_delegate.h
index 05f9f72..30860f9 100644
--- a/components/signin/core/browser/mirror_account_reconcilor_delegate.h
+++ b/components/signin/core/browser/mirror_account_reconcilor_delegate.h
@@ -47,7 +47,8 @@
       const gaia::MultiloginMode mode) const override;
 
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 
diff --git a/components/signin/core/browser/signin_error_controller.cc b/components/signin/core/browser/signin_error_controller.cc
index 6f1859e..f478635d 100644
--- a/components/signin/core/browser/signin_error_controller.cc
+++ b/components/signin/core/browser/signin_error_controller.cc
@@ -121,7 +121,7 @@
 }
 
 void SigninErrorController::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   // Ignore updates to the primary account if not in PRIMARY_ACCOUNT mode.
   if (account_mode_ != AccountMode::PRIMARY_ACCOUNT)
     return;
diff --git a/components/signin/core/browser/signin_error_controller.h b/components/signin/core/browser/signin_error_controller.h
index 334e426..f283eb0 100644
--- a/components/signin/core/browser/signin_error_controller.h
+++ b/components/signin/core/browser/signin_error_controller.h
@@ -69,7 +69,8 @@
   void OnErrorStateOfRefreshTokenUpdatedForAccount(
       const AccountInfo& account_info,
       const GoogleServiceAuthError& error) override;
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 
diff --git a/components/signin/core/browser/signin_status_metrics_provider.cc b/components/signin/core/browser/signin_status_metrics_provider.cc
index fb07e26b..4e05689 100644
--- a/components/signin/core/browser/signin_status_metrics_provider.cc
+++ b/components/signin/core/browser/signin_status_metrics_provider.cc
@@ -75,7 +75,7 @@
 }
 
 void SigninStatusMetricsProvider::OnPrimaryAccountSet(
-    const AccountInfo& account_info) {
+    const CoreAccountInfo& account_info) {
   SigninStatus recorded_signin_status = signin_status();
   if (recorded_signin_status == ALL_PROFILES_NOT_SIGNED_IN) {
     UpdateSigninStatus(MIXED_SIGNIN_STATUS);
diff --git a/components/signin/core/browser/signin_status_metrics_provider.h b/components/signin/core/browser/signin_status_metrics_provider.h
index 0bd325e..230fcde 100644
--- a/components/signin/core/browser/signin_status_metrics_provider.h
+++ b/components/signin/core/browser/signin_status_metrics_provider.h
@@ -70,7 +70,7 @@
       bool is_test);
 
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& account_info) override;
+  void OnPrimaryAccountSet(const CoreAccountInfo& account_info) override;
   void OnPrimaryAccountCleared(const AccountInfo& account_info) override;
 
   // Obtain sign-in status and add observers.
diff --git a/components/signin/ios/browser/account_consistency_service.h b/components/signin/ios/browser/account_consistency_service.h
index e7ebe23..19b4b11 100644
--- a/components/signin/ios/browser/account_consistency_service.h
+++ b/components/signin/ios/browser/account_consistency_service.h
@@ -156,7 +156,7 @@
       const GoogleServiceAuthError& error) override;
 
   // IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(const AccountInfo& account_info) override;
+  void OnPrimaryAccountSet(const CoreAccountInfo& account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_account_info) override;
 
diff --git a/components/signin/ios/browser/account_consistency_service.mm b/components/signin/ios/browser/account_consistency_service.mm
index 5bf2909..c5c058b8 100644
--- a/components/signin/ios/browser/account_consistency_service.mm
+++ b/components/signin/ios/browser/account_consistency_service.mm
@@ -492,7 +492,7 @@
 }
 
 void AccountConsistencyService::OnPrimaryAccountSet(
-    const AccountInfo& account_info) {
+    const CoreAccountInfo& account_info) {
   AddChromeConnectedCookies();
 }
 
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index ea180493..164d381 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -22,7 +22,6 @@
 #include "components/sync/driver/data_type_encryption_handler.h"
 #include "components/sync/driver/data_type_manager_observer.h"
 #include "components/sync/driver/data_type_status_table.h"
-#include "components/sync/driver/sync_client.h"
 #include "components/sync/engine/data_type_debug_info_listener.h"
 
 namespace syncer {
@@ -47,7 +46,6 @@
 DataTypeManagerImpl::AssociationTypesInfo::~AssociationTypesInfo() {}
 
 DataTypeManagerImpl::DataTypeManagerImpl(
-    SyncClient* sync_client,
     ModelTypeSet initial_types,
     const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
     const DataTypeController::TypeMap* controllers,
@@ -55,7 +53,6 @@
     ModelTypeConfigurer* configurer,
     DataTypeManagerObserver* observer)
     : downloaded_types_(initial_types),
-      sync_client_(sync_client),
       configurer_(configurer),
       controllers_(controllers),
       state_(DataTypeManager::STOPPED),
@@ -89,12 +86,6 @@
     allowed_types.Put(kv.first);
   }
 
-  // Remove types we cannot sync.
-  if (!sync_client_->HasPasswordStore())
-    allowed_types.Remove(PASSWORDS);
-  if (!sync_client_->GetHistoryService())
-    allowed_types.Remove(TYPED_URLS);
-
   ConfigureImpl(Intersection(desired_types, allowed_types), context);
 }
 
diff --git a/components/sync/driver/data_type_manager_impl.h b/components/sync/driver/data_type_manager_impl.h
index 85b4676..bc85367 100644
--- a/components/sync/driver/data_type_manager_impl.h
+++ b/components/sync/driver/data_type_manager_impl.h
@@ -27,7 +27,6 @@
 class DataTypeDebugInfoListener;
 class DataTypeEncryptionHandler;
 class DataTypeManagerObserver;
-class SyncClient;
 struct DataTypeConfigurationStats;
 
 // List of data types grouped by priority and ordered from high priority to
@@ -38,7 +37,6 @@
                             public ModelAssociationManagerDelegate {
  public:
   DataTypeManagerImpl(
-      SyncClient* sync_client,
       ModelTypeSet initial_types,
       const WeakHandle<DataTypeDebugInfoListener>& debug_info_listener,
       const DataTypeController::TypeMap* controllers,
@@ -185,7 +183,6 @@
   // Adds or removes |type| from |downloaded_types_| based on |downloaded|.
   void SetTypeDownloaded(ModelType type, bool downloaded);
 
-  SyncClient* sync_client_;
   ModelTypeConfigurer* configurer_;
 
   // Map of all data type controllers that are available for sync.
diff --git a/components/sync/driver/data_type_manager_impl_unittest.cc b/components/sync/driver/data_type_manager_impl_unittest.cc
index f71205e..2c3c7f6 100644
--- a/components/sync/driver/data_type_manager_impl_unittest.cc
+++ b/components/sync/driver/data_type_manager_impl_unittest.cc
@@ -16,7 +16,6 @@
 #include "components/sync/driver/data_type_manager_observer.h"
 #include "components/sync/driver/data_type_status_table.h"
 #include "components/sync/driver/fake_data_type_controller.h"
-#include "components/sync/driver/fake_sync_client.h"
 #include "components/sync/engine/configure_reason.h"
 #include "components/sync/engine/data_type_activation_response.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -69,11 +68,6 @@
   return status_table;
 }
 
-class TestSyncClient : public FakeSyncClient {
- public:
-  bool HasPasswordStore() override { return true; }
-};
-
 // Fake ModelTypeConfigurer implementation that simply stores away the
 // callback passed into ConfigureDataTypes.
 class FakeModelTypeConfigurer : public ModelTypeConfigurer {
@@ -258,8 +252,8 @@
  protected:
   void SetUp() override {
     dtm_ = std::make_unique<TestDataTypeManager>(
-        &sync_client_, ModelTypeSet(), WeakHandle<DataTypeDebugInfoListener>(),
-        &controllers_, &encryption_handler_, &configurer_, &observer_);
+        ModelTypeSet(), WeakHandle<DataTypeDebugInfoListener>(), &controllers_,
+        &encryption_handler_, &configurer_, &observer_);
   }
 
   void SetConfigureStartExpectation() { observer_.ExpectStart(); }
@@ -336,7 +330,6 @@
   base::test::ScopedTaskEnvironment task_environment_{
       base::test::ScopedTaskEnvironment::MainThreadType::UI};
   DataTypeController::TypeMap controllers_;
-  TestSyncClient sync_client_;
   FakeModelTypeConfigurer configurer_;
   FakeDataTypeManagerObserver observer_;
   std::unique_ptr<TestDataTypeManager> dtm_;
diff --git a/components/sync_bookmarks/bookmark_model_merger.cc b/components/sync_bookmarks/bookmark_model_merger.cc
index aa29578..18b55b3 100644
--- a/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/components/sync_bookmarks/bookmark_model_merger.cc
@@ -10,11 +10,13 @@
 #include <utility>
 
 #include "base/guid.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/bookmarks/browser/bookmark_model.h"
 #include "components/bookmarks/browser/bookmark_node.h"
 #include "components/sync/base/hash_util.h"
 #include "components/sync/base/unique_position.h"
+#include "components/sync/engine/engine_util.h"
 #include "components/sync_bookmarks/bookmark_specifics_conversions.h"
 #include "components/sync_bookmarks/synced_bookmark_tracker.h"
 
@@ -28,6 +30,10 @@
 
 static const size_t kInvalidIndex = -1;
 
+// Maximum number of bytes to allow in a title (must match sync's internal
+// limits; see write_node.cc).
+const int kTitleLimitBytes = 255;
+
 // The sync protocol identifies top-level entities by means of well-known tags,
 // (aka server defined tags) which should not be confused with titles or client
 // tags that aren't supported by bookmarks (at the time of writing). Each tag
@@ -47,6 +53,16 @@
 const char kMobileBookmarksTag[] = "synced_bookmarks";
 const char kOtherBookmarksTag[] = "other_bookmarks";
 
+// Canonicalize |title| similar to legacy client's implementation by truncating
+// up to |kTitleLimitBytes| and the appending ' ' in some cases.
+std::string CanonicalizeTitle(const std::string& title) {
+  std::string canonical_title;
+  syncer::SyncAPINameToServerName(title, &canonical_title);
+  base::TruncateUTF8ToByteSize(canonical_title, kTitleLimitBytes,
+                               &canonical_title);
+  return canonical_title;
+}
+
 // Heuristic to consider two nodes (local and remote) a match for the purpose of
 // merging. Two folders match if they have the same title, two bookmarks match
 // if they have the same title and url. A folder and a bookmark never match.
@@ -57,7 +73,14 @@
   }
   const sync_pb::BookmarkSpecifics& specifics =
       remote_node.specifics.bookmark();
-  if (local_node->GetTitle() != base::UTF8ToUTF16(specifics.title())) {
+  const std::string local_title = base::UTF16ToUTF8(local_node->GetTitle());
+  const std::string remote_title = specifics.title();
+  // Titles match if they are identical or the remote one is the canonical form
+  // of the local one. The latter is the case when a legacy client has
+  // canonicalized the same local title before committing it. Modern clients
+  // don't canonicalize titles anymore.
+  if (local_title != remote_title &&
+      CanonicalizeTitle(local_title) != remote_title) {
     return false;
   }
   if (remote_node.is_folder) {
diff --git a/components/sync_bookmarks/bookmark_model_merger_unittest.cc b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
index d0cdad6..1eedd36 100644
--- a/components/sync_bookmarks/bookmark_model_merger_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
@@ -453,4 +453,99 @@
       .Merge();
 }
 
+// This tests that canonical titles produced by legacy clients are properly
+// matched. Legacy clients append blank space to empty titles.
+TEST(BookmarkModelMergerTest,
+     ShouldMergeLocalAndRemoteNodesWhenRemoteHasLegacyCanonicalTitle) {
+  const std::string kLocalTitle = "";
+  const std::string kRemoteTitle = " ";
+  const std::string kId = "Id";
+
+  std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
+      bookmarks::TestBookmarkClient::CreateModel();
+
+  // -------- The local model --------
+  const bookmarks::BookmarkNode* bookmark_bar_node =
+      bookmark_model->bookmark_bar_node();
+  const bookmarks::BookmarkNode* folder = bookmark_model->AddFolder(
+      /*parent=*/bookmark_bar_node, /*index=*/0,
+      base::UTF8ToUTF16(kLocalTitle));
+  ASSERT_TRUE(folder);
+
+  // -------- The remote model --------
+  const std::string suffix = syncer::UniquePosition::RandomSuffix();
+  syncer::UniquePosition pos = syncer::UniquePosition::InitialPosition(suffix);
+
+  syncer::UpdateResponseDataList updates;
+  updates.push_back(CreateBookmarkBarNodeUpdateData());
+  updates.push_back(CreateUpdateResponseData(
+      /*server_id=*/kId, /*parent_id=*/kBookmarkBarId, kRemoteTitle,
+      /*url=*/std::string(),
+      /*is_folder=*/true, /*unique_position=*/pos));
+
+  SyncedBookmarkTracker tracker(std::vector<NodeMetadataPair>(),
+                                std::make_unique<sync_pb::ModelTypeState>());
+  testing::NiceMock<favicon::MockFaviconService> favicon_service;
+  BookmarkModelMerger(&updates, bookmark_model.get(), &favicon_service,
+                      &tracker)
+      .Merge();
+
+  // Both titles should have matched against each other and only node is in the
+  // model and the tracker.
+  EXPECT_THAT(bookmark_bar_node->child_count(), Eq(1));
+  EXPECT_THAT(tracker.TrackedEntitiesCountForTest(), Eq(2U));
+}
+
+// This tests that truncated titles produced by legacy clients are properly
+// matched.
+TEST(BookmarkModelMergerTest,
+     ShouldMergeLocalAndRemoteNodesWhenRemoteHasLegacyTruncatedTitle) {
+  const std::string kLocalLongTitle =
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst"
+      "uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"
+      "OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
+      "ijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB"
+      "CDEFGHIJKLMNOPQRSTUVWXYZ";
+  const std::string kRemoteTruncatedTitle =
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst"
+      "uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"
+      "OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh"
+      "ijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU";
+  const std::string kId = "Id";
+
+  std::unique_ptr<bookmarks::BookmarkModel> bookmark_model =
+      bookmarks::TestBookmarkClient::CreateModel();
+
+  // -------- The local model --------
+  const bookmarks::BookmarkNode* bookmark_bar_node =
+      bookmark_model->bookmark_bar_node();
+  const bookmarks::BookmarkNode* folder = bookmark_model->AddFolder(
+      /*parent=*/bookmark_bar_node, /*index=*/0,
+      base::UTF8ToUTF16(kLocalLongTitle));
+  ASSERT_TRUE(folder);
+
+  // -------- The remote model --------
+  const std::string suffix = syncer::UniquePosition::RandomSuffix();
+  syncer::UniquePosition pos = syncer::UniquePosition::InitialPosition(suffix);
+
+  syncer::UpdateResponseDataList updates;
+  updates.push_back(CreateBookmarkBarNodeUpdateData());
+  updates.push_back(CreateUpdateResponseData(
+      /*server_id=*/kId, /*parent_id=*/kBookmarkBarId, kRemoteTruncatedTitle,
+      /*url=*/std::string(),
+      /*is_folder=*/true, /*unique_position=*/pos));
+
+  SyncedBookmarkTracker tracker(std::vector<NodeMetadataPair>(),
+                                std::make_unique<sync_pb::ModelTypeState>());
+  testing::NiceMock<favicon::MockFaviconService> favicon_service;
+  BookmarkModelMerger(&updates, bookmark_model.get(), &favicon_service,
+                      &tracker)
+      .Merge();
+
+  // Both titles should have matched against each other and only node is in the
+  // model and the tracker.
+  EXPECT_THAT(bookmark_bar_node->child_count(), Eq(1));
+  EXPECT_THAT(tracker.TrackedEntitiesCountForTest(), Eq(2U));
+}
+
 }  // namespace sync_bookmarks
diff --git a/components/test/data/autofill/heuristics/input/153_fmm-en_inm.gob.mx.html b/components/test/data/autofill/heuristics/input/153_fmm-en_inm.gob.mx.html
new file mode 100644
index 0000000..09ff1bc
--- /dev/null
+++ b/components/test/data/autofill/heuristics/input/153_fmm-en_inm.gob.mx.html
@@ -0,0 +1,535 @@
+<html lang="en">
+<!-- Form Location: https://www.inm.gob.mx/fmme/publico/en/solicitud.html -->
+
+<head>
+  <meta charset="utf-8" />
+  <title>Instituto Nacional de Migración - Forma Migratoria Múltiple</title>
+</head>
+
+<body>
+  <form class="clearfix ns_" action="" role="form">
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Entry Information</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="internacion">Means of entry</label>
+          <select class=" form-control valid-field ns_" id="internacion">
+            <option value="0">Select</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="puntoInternacion">Point of entry</label>
+          <select class=" form-control valid-field ns_" id="puntoInternacion">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaLlegada" class="control-label">Date of arrival to Mexico</label>
+          <input id="fechaLlegada" name="fechaLlegada" type="text" class="form-control valid-field ns_" placeholder="dd/mm/yyyy o ddmmyy" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaSalida" class="control-label">Date of departure</label>
+          <input id="fechaSalida" name="fechaSalida" type="text" class="form-control valid-field ns_" placeholder="dd/mm/yyyy o ddmmyy" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div id="aerolinea">
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label " for="nombreAerolinea">Airline name</label>
+            <input class=" form-control valid-field ns_" id="nombreAerolinea" type="text" />
+          </div>
+        </div>
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="numeroVuelo">Flight number</label>
+            <input class=" form-control valid-field ns_" id="numeroVuelo" type="text" />
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Personal information</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="nombre">Name(s)</label>
+          <input class=" form-control  valid-field ns_" id="nombre" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="apellidos">Surname(s)</label>
+          <input class=" form-control  valid-field ns_" id="apellidos" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="sexo">Gender</label>
+          <select class=" form-control valid-field ns_" id="sexo">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaNacimiento" class="control-label">Date of birth</label>
+          <input id="fechaNacimiento" name="fechaNacimiento" type="text" class="form-control valid-field" placeholder="dd/mm/yyyy o ddmmyy" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="nacionalidad">Nationality (Country)</label>
+          <select class=" form-control valid-field ns_" id="nacionalidad">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisNacimiento">Country of birth</label>
+          <select class=" form-control valid-field" id="paisNacimiento">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Identification document</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="tipoDocumento">Type of document</label>
+          <select class=" form-control valid-field ns_" id="tipoDocumento">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="numeroDocumento">Document number</label>
+          <input class=" form-control valid-field ns_" id="numeroDocumento" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="confirmarNumeroDocumento">Document number (Confirmation)</label>
+          <input class=" form-control valid-field ns_" id="confirmarNumeroDocumento" type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisExpedicion">Country of issue</label>
+          <select class=" form-control valid-field ns_" id="paisExpedicion">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaExpedicion" class="control-label">Date of issue</label>
+          <input id="fechaExpedicion" name="fechaExpedicion" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/yyyy o ddmmyy" />
+          <a id="focoFuera" href="#"></a>
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="confirmarFechaExpedicion" class="control-label">Date of issue (Confirmation)</label>
+          <input id="confirmarFechaExpedicion" name="confirmarFechaExpedicion" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/yyyy o ddmmyy" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaExpiracion" class="control-label">Expiration date</label>
+          <input id="fechaExpiracion" name="fechaExpiracion" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/yyyy o ddmmyy" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="confirmarFechaExpiracion" class="control-label">Expiration date (Confirmation)</label>
+          <input id="confirmarFechaExpiracion" name="confirmarFechaExpiracion" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/yyyy o ddmmyy" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer"> Place of residence</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisResidencia">Country of residence</label>
+          <select class=" form-control valid-field ns_" id="paisResidencia">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="direccionResidencia">Address of residence</label>
+          <input class=" form-control valid-field ns_" id="direccionResidencia" type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Trip information</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label " for="motivoViaje">Reason of trip</label>
+          <select class=" form-control valid-field ns_" id="motivoViaje">
+            <option value="0">Select one</option>
+          </select> <a href="#"></a>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="especifiqueMotivo">Specify</label>
+          <select class=" form-control valid-field ns_" id="especifiqueMotivo">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="estadoDestino">State</label>
+          <select class=" form-control valid-field ns_" id="estadoDestino">
+            <option value="0">Select one</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="domicilioMexico">Address in Mexico</label>
+          <input class=" form-control valid-field ns_" id="domicilioMexico" placeholder="Hotel name, street and number"
+            type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div id="informacionTutor" style="display:none">
+
+      <div class="row">
+        <div class="col-md-8">
+          <h2 class="top-buffer">Father, mother or guardian information</h2>
+          <hr class="red" />
+        </div>
+      </div>
+
+      <div class="row">
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="nombreTutor">Name(s)</label>
+            <input class="form-control valid-field ns_" id="nombreTutor" type="text" />
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="apellidosTutor">Surname(s)</label>
+            <input class=" form-control valid-field ns_" id="apellidosTutor" type="text" />
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="sexoTutor">Gender</label>
+            <select class=" form-control valid-field ns_" id="sexoTutor">
+              <option value="0">Select one</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+      <div class="row">
+        <div class="col-md-4">
+          <div class="form-group datepicker-group">
+            <label for="fechaNacimientoTutor" class="control-label">Date of birth</label>
+            <input id="fechaNacimientoTutor" name="fechaNacimientoTutor" type="text" class="form-control valid-field ns_"
+              placeholder="dd/mm/yyyy o ddmmyy" />
+            <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="nacionalidadTutor">Nationality (Country)</label>
+            <select class=" form-control valid-field ns_" id="nacionalidadTutor">
+              <option value="0">Select one</option>
+            </select>
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="paisNacimientoTutor">Country of birth</label>
+            <select class=" form-control valid-field ns_" id="paisNacimientoTutor">
+              <option value="0">Select one</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Email</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="correoElectronico">Email</label>
+          <input class=" form-control valid-field ns_" id="correoElectronico" type="text" placeholder="johndoe@example.com" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="confirmacionCorreoElectronico">Email (Confirmation)</label>
+          <input class=" form-control valid-field ns_" id="confirmacionCorreoElectronico" placeholder="johndoe@example.com"
+            type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8 col-md-offset-4">
+        <br>
+      </div>
+    </div>
+
+    <!--captcha-->
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group" style="outline: 1pt solid lightgray">
+          <div style="width: 200px; height: 70px" id="imagenCaptcha"><img src="" /></div>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group">
+          <label for="captcha" class="control-label">Verification code:</label>
+          <input id="captcha" type="text" class="valid-field form-control ns_" />
+        </div>
+      </div>
+
+    </div>
+    <div class="row bottom-buffer">
+      <div class="col-md-8">
+        <div class="form-group">
+          <label id="noLegibleLabel" class="control-label">
+            Not readable verification code?
+            <a id="otraImagen" class="liga ns_" href="#" onclick="uid_call('inm.fmme.obtener_captacha', 'clickin')">Try
+              another one</a>
+          </label>
+
+        </div>
+      </div>
+    </div>
+
+    <!--diálogo con mensajes sobre resultados de acciones -->
+
+    <div id="avisoDialog" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+      <div class="modal-dialog">
+        <div class="modal-content">
+          <div class="modal-header">
+            <h3 class="modal-title"><span class="glyphicon glyphicon-warning-sign"></span>&nbsp;&nbsp;Notification</h3>
+          </div>
+          <div class="modal-body">
+            <p class="text-left" id="avisoDialog_texto">
+              <!-- texto generado automagicamente -->
+            </p>
+          </div>
+          <div class="modal-footer">
+            <button type="button" id="avisoDialog_cerrar" class="btn btn-default ns_">Close
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Fin Dialogos -->
+
+    <div class="bottom-buffer" id="botonesGuardar">
+      <div class="row">
+        <div class="col-md-4" style="padding-top: 10px;">
+          <div class="pull-left text-muted">* Required fields</div>
+        </div>
+        <div class="col-md-8 text-right">
+          <button type="button" id="limpiar" class="btn btn-default ns_">Delete</button>
+          <button type="button" id="procesar" class="btn btn-primary ns_">Save</button>
+        </div>
+      </div>
+    </div>
+
+    <div id="confirmacionSolicitud" style="display:none">
+      <div class="alert alert-warning" id="informacionConfirmar">
+        <strong>Important:</strong> the migration authority cannot
+        modify the information provided by the user; therefore,
+        the documents that contain inaccurate data will be refused.
+        Errors in the resolution and expedition of the migratory
+        forms that result from errors in the application, are
+        the users responsibility.
+      </div>
+      <div class="row">
+        <div class="col-md-8 col-md-offset-4">
+          <hr>
+        </div>
+      </div>
+      <div class="alert alert-info text-center">
+        <p>Is the provided information correct?</p>
+      </div>
+      <div class="bottom-buffer">
+        <div class="bottom-buffer" align="right" id="botonesConfirmar">
+          <button id="regresar" type="button" class="btn btn-default ns_">No</button>
+          <button id="confirmar" type="button" class="btn btn-primary ns_">Yes</button>
+        </div>
+      </div>
+      <div class="alert alert-info text-center top-buffer">
+        <p>To generate your request disable pop-up blocker
+          browser and check to have installed Acrobat Reader.</p>
+      </div>
+
+    </div>
+    <div id="solicitudPdf" style="display:none">
+      <div id="fmmPdf">
+      </div>
+    </div>
+  </form>
+
+  <form role="form" action="https://www.banjercito.com.mx/formaMigratoriaMultiple/condicionesGenerales.do" method="POST"
+    class="ns_">
+    <div style="display:none">
+      <div class="form-group" id="div_idioma">
+        <label class="ccontrol-label" for="idioma">Idioma: </label>
+        <input class="form-control ns_" id="idioma" name="idioma" type="text" readonly value="es">
+      </div>
+      <div class="form-group" id="div_noConsecutivoInterno">
+        <label class="control-label" for="noConsecutivoInterno">Consecutivo: </label>
+        <input class="form-control ns_" id="noConsecutivoInterno" name="noConsecutivoInterno" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNumeroPasaporte">
+        <label class="control-label" for="pagoNumeroPasaporte">Número de pasaporte: </label>
+        <input maxlength="15" class="form-control ns_" id="pagoNumeroPasaporte" name="numero_pasaporte" type="text"
+          readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNombres">
+        <label class="control-label" for="pagoNombres">Nombre(s): </label>
+        <input class="form-control ns_" id="pagoNombres" name="nombres" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoApellidos">
+        <label class="control-label" for="pagoApellidos">Apellido(s): </label>
+        <input class="form-control ns_" id="pagoApellidos" name="apellidos" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNacionalidad">
+        <label class="control-label" for="pagoNacionalidad">Nacionalidad:</label>
+        <input class="form-control ns_" id="pagoNacionalidad" name="nacionalidad" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoFechaNacimiento">
+        <label class="control-label" for="pagoFechaNacimiento">Fecha de nacimiento: </label>
+        <input maxlength="10" class="form-control ns_" id="pagoFechaNacimiento" name="fecha_nacimiento" type="text"
+          readonly>
+      </div>
+      <div class="form-group" id="div_sexo_pago">
+        <label class="control-label" for="sexo_pago">Sexo:</label>
+        <input class="form-control ns_" id="sexo_pago" type="text" readonly>
+
+      </div>
+      <div class="form-group" id="div_pagoSexo">
+        <label class="control-label" for="pagoSexo">Sexo:</label>
+        <input maxlength="1" class="form-control ns_" id="pagoSexo" name="sexo" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_monto_tramite">
+        <label class="control-label" for="monto_tramite">Monto trámite: </label>
+        <input class="form-control ns_" id="monto_tramite" name="monto_tramite" type="text" value="558.00" readonly>
+      </div>
+
+      <div class="form-group" id="div_concepto_tramite">
+        <label class="control-label" for="concepto_tramite">Concepto trámite: </label>
+        <input class="form-control ns_" id="concepto_tramite" name="concepto_tramite" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_clave_tramite">
+        <label class="control-label" for="clave_tramite">Clave trámite: </label>
+        <input class="form-control ns_" id="clave_tramite" name="clave_tramite" type="text" readonly>
+      </div>
+    </div>
+    <div class="text-right bottom-buffer" id="div_cont_pago">
+      <button id="realizaPago" type="submit" class="btn btn-primary btn-sm ns_">Send the information</button>
+    </div>
+  </form>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/components/test/data/autofill/heuristics/input/154_fmm-es_inm.gob.mx.html b/components/test/data/autofill/heuristics/input/154_fmm-es_inm.gob.mx.html
new file mode 100644
index 0000000..c7a8817
--- /dev/null
+++ b/components/test/data/autofill/heuristics/input/154_fmm-es_inm.gob.mx.html
@@ -0,0 +1,535 @@
+<html lang="es">
+<!-- Form Location: https://www.inm.gob.mx/fmme/publico/solicitud.html -->
+
+<head>
+  <meta charset="utf-8" />
+  <title>Instituto Nacional de Migración - Forma Migratoria Múltiple</title>
+</head>
+
+<body>
+  <form class="clearfix ns_" action="" role="form">
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Datos de ingreso</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="internacion">Vía de internación</label>
+          <select class=" form-control valid-field ns_" id="internacion">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="puntoInternacion">Punto de internación</label>
+          <select class=" form-control valid-field ns_" id="puntoInternacion">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaLlegada" class="control-label">Fecha de llegada a México</label>
+          <input id="fechaLlegada" name="fechaLlegada" type="text" class="form-control valid-field ns_" placeholder="dd/mm/aaaa o ddmmaa" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaSalida" class="control-label">Fecha de salida</label>
+          <input id="fechaSalida" name="fechaSalida" type="text" class="form-control valid-field" placeholder="dd/mm/aaaa o ddmmaa" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div id="aerolinea">
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label " for="nombreAerolinea">Nombre de aerolínea</label>
+            <input class=" form-control valid-field ns_" id="nombreAerolinea" type="text" />
+          </div>
+        </div>
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="numeroVuelo">Número de vuelo</label>
+            <input class=" form-control valid-field ns_" id="numeroVuelo" type="text" />
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Datos personales</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="nombre">Nombre(s)</label>
+          <input class=" form-control  valid-field ns_" id="nombre" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="apellidos">Apellido(s)</label>
+          <input class=" form-control  valid-field ns_" id="apellidos" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="sexo">Sexo</label>
+          <select class=" form-control valid-field ns_" id="sexo">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaNacimiento" class="control-label">Fecha de nacimiento</label>
+          <input id="fechaNacimiento" name="fechaNacimiento" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/aaaa o ddmmaa" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="nacionalidad">Nacionalidad (País)</label>
+          <select class=" form-control valid-field ns_" id="nacionalidad">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisNacimiento">País de nacimiento</label>
+          <select class=" form-control valid-field ns_" id="paisNacimiento">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer"> Documento de identificación</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="tipoDocumento">Tipo de documento</label>
+          <select class=" form-control valid-field ns_" id="tipoDocumento">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="numeroDocumento">Número de documento</label>
+          <input class=" form-control valid-field ns_" id="numeroDocumento" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="confirmarNumeroDocumento">Número de documento (Confirmación)</label>
+          <input class=" form-control valid-field ns_" id="confirmarNumeroDocumento" type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisExpedicion">País de expedición</label>
+          <select class=" form-control valid-field ns_" id="paisExpedicion">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaExpedicion" class="control-label">Fecha de expedición</label>
+          <input id="fechaExpedicion" name="fechaExpedicion" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/aaaa o ddmmaa" />
+          <a id="focoFuera" href="#"></a>
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="confirmarFechaExpedicion" class="control-label">Fecha de expedición (Confirmación)</label>
+          <input id="confirmarFechaExpedicion" name="confirmarFechaExpedicion" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/aaaa o ddmmaa" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaExpiracion" class="control-label">Fecha de expiración</label>
+          <input id="fechaExpiracion" name="fechaExpiracion" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/aaaa o ddmmaa" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="confirmarFechaExpiracion" class="control-label">Fecha de expiración (Confirmación)</label>
+          <input id="confirmarFechaExpiracion" name="confirmarFechaExpiracion" type="text" class="form-control valid-field ns_"
+            placeholder="dd/mm/aaaa o ddmmaa" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer"> Lugar de residencia</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisResidencia">País de residencia</label>
+          <select class=" form-control valid-field ns_" id="paisResidencia">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="direccionResidencia">Dirección de residencia</label>
+          <input class=" form-control valid-field ns_" id="direccionResidencia" type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Información de viaje</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label " for="motivoViaje">Motivo de viaje</label>
+          <select class=" form-control valid-field ns_" id="motivoViaje">
+            <option value="0">Seleccione</option>
+          </select> <a href="#"></a>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="especifiqueMotivo">Especifique motivo</label>
+          <select class=" form-control valid-field ns_" id="especifiqueMotivo">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="estadoDestino">Estado</label>
+          <select class=" form-control valid-field ns_" id="estadoDestino">
+            <option value="0">Seleccione</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="domicilioMexico">Domicilio en México</label>
+          <input class=" form-control valid-field ns_" id="domicilioMexico" placeholder="Nombre del hotel, calle y número"
+            type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div id="informacionTutor" style="display:none">
+
+      <div class="row">
+        <div class="col-md-8">
+          <h2 class="top-buffer">Informaci&oacute;n del padre, madre o tutor</h2>
+          <hr class="red" />
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="nombreTutor">Nombre(s)</label>
+            <input class="form-control valid-field ns_" id="nombreTutor" type="text" />
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="apellidosTutor">Apellido(s)</label>
+            <input class=" form-control valid-field ns_" id="apellidosTutor" type="text" />
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="sexoTutor">Sexo</label>
+            <select class=" form-control valid-field ns_" id="sexoTutor">
+              <option value="0">Seleccione</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+      <div class="row">
+        <div class="col-md-4">
+          <div class="form-group datepicker-group">
+            <label for="fechaNacimientoTutor" class="control-label">Fecha de nacimiento</label>
+            <input id="fechaNacimientoTutor" name="fechaNacimientoTutor" type="text" class="form-control valid-field ns_"
+              placeholder="dd/mm/aaaa o ddmmaa" />
+            <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="nacionalidadTutor">Nacionalidad (País)</label>
+            <select class=" form-control valid-field ns_" id="nacionalidadTutor">
+              <option value="0">Seleccione</option>
+            </select>
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="paisNacimientoTutor">País de nacimiento</label>
+            <select class=" form-control valid-field ns_" id="paisNacimientoTutor">
+              <option value="0">Seleccione</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">Correo electrónico</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="correoElectronico">Correo electrónico</label>
+          <input class=" form-control valid-field ns_" id="correoElectronico" type="text" placeholder="nombre@correo.com" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="confirmacionCorreoElectronico">Correo electrónico (Confirmación)</label>
+          <input class=" form-control valid-field ns_" id="confirmacionCorreoElectronico" placeholder="nombre@correo.com"
+            type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8 col-md-offset-4">
+        <br>
+      </div>
+    </div>
+
+    <!--captcha-->
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group" style="outline: 1pt solid lightgray">
+          <div style="width: 200px; height: 70px" id="imagenCaptcha"><img src="" /></div>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group">
+          <label for="captcha" class="control-label">Código de verificación:</label>
+          <input id="captcha" type="text" class="valid-field form-control ns_" />
+        </div>
+      </div>
+
+    </div>
+    <div class="row bottom-buffer">
+      <div class="col-md-8">
+        <div class="form-group">
+          <label id="noLegibleLabel" class="control-label">
+            ¿No es legible el código de verificación?
+            <a id="otraImagen" class="liga ns_" href="#" onclick="uid_call('inm.fmme.obtener_captacha', 'clickin')">Intenta
+              con otro</a>
+          </label>
+
+        </div>
+      </div>
+    </div>
+
+    <!--diálogo con mensajes sobre resultados de acciones -->
+
+    <div id="avisoDialog" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+      <div class="modal-dialog">
+        <div class="modal-content">
+          <div class="modal-header">
+            <h3 class="modal-title"><span class="glyphicon glyphicon-warning-sign"></span>&nbsp;&nbsp;Aviso</h3>
+          </div>
+          <div class="modal-body">
+            <p class="text-left" id="avisoDialog_texto">
+              <!-- texto generado automagicamente -->
+            </p>
+          </div>
+          <div class="modal-footer">
+            <button type="button" id="avisoDialog_cerrar" class="btn btn-default ns_">Cerrar
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Fin Dialogos -->
+
+    <div class="bottom-buffer" id="botonesGuardar">
+      <div class="row">
+        <div class="col-md-4" style="padding-top: 10px;">
+          <div class="pull-left text-muted">* Campos obligatorios</div>
+        </div>
+        <div class="col-md-8 text-right">
+          <button type="button" id="limpiar" class="btn btn-default ns_">Limpiar</button>
+          <button type="button" id="procesar" class="btn btn-primary ns_">Guardar</button>
+        </div>
+      </div>
+    </div>
+
+    <div id="confirmacionSolicitud" style="display:none">
+      <div class="alert alert-warning" id="informacionConfirmar">
+        <strong>Importante:</strong> La autoridad migratoria no puede modificar la información proporcionada por el
+        usuario, por lo que los
+        trámites que contengan datos erróneos serán negados. Los errores en la resolución y expedición de formas
+        migratorias que resulten de
+        errores en la solicitud son responsabilidad del usuario.
+      </div>
+      <div class="row">
+        <div class="col-md-8 col-md-offset-4">
+          <hr>
+        </div>
+      </div>
+      <div class="alert alert-info text-center">
+        <p>¿Es correcta la información capturada?</p>
+      </div>
+      <!--                        <hr class="red"/>-->
+      <div class="bottom-buffer">
+        <div class="bottom-buffer" align="right" id="botonesConfirmar">
+          <button id="regresar" type="button" class="btn btn-default ns_">No</button>
+          <button id="confirmar" type="button" class="btn btn-primary ns_">&nbsp;Sí&nbsp;</button>
+        </div>
+      </div>
+      <div class="alert alert-info text-center top-buffer">
+        <p>Para generar la solicitud desactiva el bloqueador de elementos
+          emergentes del explorador y verifica tener instalado
+          Acrobat Reader.</p>
+      </div>
+
+    </div>
+    <div id="solicitudPdf" style="display:none">
+      <div id="fmmPdf">
+      </div>
+    </div>
+  </form>
+
+  <form role="form" action="https://www.banjercito.com.mx/formaMigratoriaMultiple/condicionesGenerales.do" method="POST"
+    class="ns_">
+    <div style="display:none">
+      <div class="form-group" id="div_idioma">
+        <label class="ccontrol-label" for="idioma">Idioma: </label>
+        <input class="form-control ns_" id="idioma" name="idioma" type="text" readonly value="es">
+      </div>
+      <div class="form-group" id="div_noConsecutivoInterno">
+        <label class="control-label" for="noConsecutivoInterno">Consecutivo: </label>
+        <input class="form-control ns_" id="noConsecutivoInterno" name="noConsecutivoInterno" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNumeroPasaporte">
+        <label class="control-label" for="pagoNumeroPasaporte">Número de pasaporte: </label>
+        <input maxlength="15" class="form-control ns_" id="pagoNumeroPasaporte" name="numero_pasaporte" type="text"
+          readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNombres">
+        <label class="control-label" for="pagoNombres">Nombre(s): </label>
+        <input class="form-control ns_" id="pagoNombres" name="nombres" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoApellidos">
+        <label class="control-label" for="pagoApellidos">Apellido(s): </label>
+        <input class="form-control ns_" id="pagoApellidos" name="apellidos" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNacionalidad">
+        <label class="control-label" for="pagoNacionalidad">Nacionalidad:</label>
+        <input class="form-control ns_" id="pagoNacionalidad" name="nacionalidad" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoFechaNacimiento">
+        <label class="control-label" for="pagoFechaNacimiento">Fecha de nacimiento: </label>
+        <input maxlength="10" class="form-control ns_" id="pagoFechaNacimiento" name="fecha_nacimiento" type="text"
+          readonly>
+      </div>
+      <div class="form-group" id="div_sexo_pago">
+        <label class="control-label" for="sexo_pago">Sexo:</label>
+        <input class="form-control ns_" id="sexo_pago" type="text" readonly>
+
+      </div>
+      <div class="form-group" id="div_pagoSexo">
+        <label class="control-label" for="pagoSexo">Sexo:</label>
+        <input maxlength="1" class="form-control ns_" id="pagoSexo" name="sexo" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_monto_tramite">
+        <label class="control-label" for="monto_tramite">Monto trámite: </label>
+        <input class="form-control ns_" id="monto_tramite" name="monto_tramite" type="text" value="558.00" readonly>
+      </div>
+
+      <div class="form-group" id="div_concepto_tramite">
+        <label class="control-label" for="concepto_tramite">Concepto trámite: </label>
+        <input class="form-control ns_" id="concepto_tramite" name="concepto_tramite" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_clave_tramite">
+        <label class="control-label" for="clave_tramite">Clave trámite: </label>
+        <input class="form-control ns_" id="clave_tramite" name="clave_tramite" type="text" readonly>
+      </div>
+    </div>
+    <div class="text-right bottom-buffer" id="div_cont_pago">
+      <button id="realizaPago" type="submit" class="btn btn-primary btn-sm ns_">Enviar datos</button>
+    </div>
+  </form>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/components/test/data/autofill/heuristics/input/155_fmm-ja_inm.gob.mx.html b/components/test/data/autofill/heuristics/input/155_fmm-ja_inm.gob.mx.html
new file mode 100644
index 0000000..58c5b97
--- /dev/null
+++ b/components/test/data/autofill/heuristics/input/155_fmm-ja_inm.gob.mx.html
@@ -0,0 +1,531 @@
+<html lang="es">
+<!-- Form Location: https://www.inm.gob.mx/fmme/publico/ja/solicitud.html -->
+
+<head>
+  <meta charset="utf-8" />
+  <title>Instituto Nacional de Migración - Forma Migratoria Múltiple</title>
+</head>
+
+<body>
+
+  <form class="clearfix ns_" action="" role="form">
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">入国データ</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="internacion">入国手段</label>
+          <select class=" form-control valid-field ns_" id="internacion">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="puntoInternacion">入国地点</label>
+          <select class=" form-control valid-field ns_" id="puntoInternacion">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaLlegada" class="control-label">メキシコへの入国日</label>
+          <input id="fechaLlegada" name="fechaLlegada" type="text" class="form-control valid-field ns_" placeholder="直接入力可能" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaSalida" class="control-label">出国日</label>
+          <input id="fechaSalida" name="fechaSalida" type="text" class="form-control valid-field" placeholder="直接入力可能" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div id="aerolinea">
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label " for="nombreAerolinea">航空会社名</label>
+            <input class=" form-control valid-field ns_" id="nombreAerolinea" type="text" />
+          </div>
+        </div>
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="numeroVuelo">便名</label>
+            <input class=" form-control valid-field ns_" id="numeroVuelo" type="text" />
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">個人データ</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="nombre">名</label>
+          <input class=" form-control  valid-field ns_" id="nombre" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="apellidos">姓</label>
+          <input class=" form-control  valid-field ns_" id="apellidos" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="sexo">性別</label>
+          <select class=" form-control valid-field ns_" id="sexo">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaNacimiento" class="control-label">生年月日</label>
+          <input id="fechaNacimiento" name="fechaNacimiento" type="text" class="form-control valid-field ns_"
+            placeholder="直接入力可能" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="nacionalidad">国籍(国名)</label>
+          <select class=" form-control valid-field ns_" id="nacionalidad">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisNacimiento">出生国</label>
+          <select class=" form-control valid-field ns_" id="paisNacimiento">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">身分証明用の書類</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="tipoDocumento">書類の種類</label>
+          <select class=" form-control valid-field ns_" id="tipoDocumento">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="numeroDocumento">書類番号</label>
+          <input class=" form-control valid-field ns_" id="numeroDocumento" type="text" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="confirmarNumeroDocumento">書類番号(再入力)</label>
+          <input class=" form-control valid-field ns_" id="confirmarNumeroDocumento" type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisExpedicion">書類の発行国</label>
+          <select class=" form-control valid-field ns_" id="paisExpedicion">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaExpedicion" class="control-label">発行日</label>
+          <input id="fechaExpedicion" name="fechaExpedicion" type="text" class="form-control valid-field ns_"
+            placeholder="直接入力可能" />
+          <a id="focoFuera" href="#"></a>
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="confirmarFechaExpedicion" class="control-label">発行日(再入力)</label>
+          <input id="confirmarFechaExpedicion" name="confirmarFechaExpedicion" type="text" class="form-control valid-field ns_"
+            placeholder="直接入力可能" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="fechaExpiracion" class="control-label">有効期限日</label>
+          <input id="fechaExpiracion" name="fechaExpiracion" type="text" class="form-control valid-field ns_"
+            placeholder="直接入力可能" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group datepicker-group">
+          <label for="confirmarFechaExpiracion" class="control-label">有効期限日(再入力)</label>
+          <input id="confirmarFechaExpiracion" name="confirmarFechaExpiracion" type="text" class="form-control valid-field ns_"
+            placeholder="直接入力可能" />
+          <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer"> 居住地</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="paisResidencia">居住国</label>
+          <select class=" form-control valid-field ns_" id="paisResidencia">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="direccionResidencia">現住所</label>
+          <input class=" form-control valid-field ns_" id="direccionResidencia" type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">旅行の情報</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label " for="motivoViaje">旅行目的</label>
+          <select class=" form-control valid-field ns_" id="motivoViaje">
+            <option value="0">選択</option>
+          </select> <a href="#"></a>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="especifiqueMotivo">具体的な目的</label>
+          <select class=" form-control valid-field ns_" id="especifiqueMotivo">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="estadoDestino">州名</label>
+          <select class=" form-control valid-field ns_" id="estadoDestino">
+            <option value="0">選択</option>
+          </select>
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="domicilioMexico">メキシコ国内の住所</label>
+          <input class=" form-control valid-field ns_" id="domicilioMexico" placeholder="ホテル名、通りと番地" type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div id="informacionTutor" style="display:none">
+
+      <div class="row">
+        <div class="col-md-8">
+          <h2 class="top-buffer">父親、母親あるいは後見人のデータ</h2>
+          <hr class="red" />
+        </div>
+      </div>
+      <div class="row">
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="nombreTutor">名</label>
+            <input class="form-control valid-field ns_" id="nombreTutor" type="text" />
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="apellidosTutor">姓</label>
+            <input class=" form-control valid-field ns_" id="apellidosTutor" type="text" />
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="sexoTutor">性別</label>
+            <select class=" form-control valid-field ns_" id="sexoTutor">
+              <option value="0">選択</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+      <div class="row">
+        <div class="col-md-4">
+          <div class="form-group datepicker-group">
+            <label for="fechaNacimientoTutor" class="control-label">生年月日</label>
+            <input id="fechaNacimientoTutor" name="fechaNacimientoTutor" type="text" class="form-control valid-field ns_"
+              placeholder="直接入力可能" />
+            <span class="glyphicon glyphicon-calendar" aria-hidden="true"></span>
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="nacionalidadTutor">国籍(国名)</label>
+            <select class=" form-control valid-field ns_" id="nacionalidadTutor">
+              <option value="0">選択</option>
+            </select>
+          </div>
+        </div>
+
+        <div class="col-md-4">
+          <div class="form-group">
+            <label class="control-label" for="paisNacimientoTutor">出生国</label>
+            <select class=" form-control valid-field ns_" id="paisNacimientoTutor">
+              <option value="0">選択</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+    </div>
+
+    <div class="row">
+      <div class="col-md-8">
+        <h2 class="top-buffer">電子メール</h2>
+        <hr class="red" />
+      </div>
+    </div>
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="correoElectronico">メールアドレス</label>
+          <input class=" form-control valid-field ns_" id="correoElectronico" type="text" placeholder="nombre@correo.com" />
+        </div>
+      </div>
+
+      <div class="col-md-4">
+        <div class="form-group">
+          <label class="control-label" for="confirmacionCorreoElectronico">メールアドレス(再入力)</label>
+          <input class=" form-control valid-field ns_" id="confirmacionCorreoElectronico" placeholder="nombre@correo.com"
+            type="text" />
+        </div>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="col-md-8 col-md-offset-4">
+        <br>
+      </div>
+    </div>
+
+    <!--captcha-->
+    <div class="row">
+      <div class="col-md-4">
+        <div class="form-group" style="outline: 1pt solid lightgray">
+          <div style="width: 200px; height: 70px" id="imagenCaptcha"><img src="" /></div>
+        </div>
+      </div>
+      <div class="col-md-4">
+        <div class="form-group">
+          <label for="captcha" class="control-label">認証コード</label>
+          <input id="captcha" type="text" class="valid-field form-control ns_" />
+        </div>
+      </div>
+
+    </div>
+    <div class="row bottom-buffer">
+      <div class="col-md-8">
+        <div class="form-group">
+          <label id="noLegibleLabel" class="control-label">
+            認証コードが判読できませんか。
+            <a id="otraImagen" class="liga ns_" href="#" onclick="uid_call('inm.fmme.obtener_captacha', 'clickin')">別のコードを使用してください。</a>
+          </label>
+
+        </div>
+      </div>
+    </div>
+
+    <!--diálogo con mensajes sobre resultados de acciones -->
+
+    <div id="avisoDialog" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+      <div class="modal-dialog">
+        <div class="modal-content">
+          <div class="modal-header">
+            <h3 class="modal-title"><span class="glyphicon glyphicon-warning-sign"></span>&nbsp;&nbsp;メッセージ</h3>
+          </div>
+          <div class="modal-body">
+            <p class="text-left" id="avisoDialog_texto">
+              <!-- texto generado automagicamente -->
+            </p>
+          </div>
+          <div class="modal-footer">
+            <button type="button" id="avisoDialog_cerrar" class="btn btn-default ns_">閉じる
+            </button>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Fin Dialogos -->
+
+    <div class="bottom-buffer" id="botonesGuardar">
+      <div class="row">
+        <div class="col-md-4" style="padding-top: 10px;">
+          <div class="pull-left text-muted">* 入力必須の欄</div>
+        </div>
+        <div class="col-md-8 text-right">
+          <button type="button" id="limpiar" class="btn btn-default ns_">消去</button>
+          <button type="button" id="procesar" class="btn btn-primary ns_">保存</button>
+        </div>
+      </div>
+    </div>
+
+    <div id="confirmacionSolicitud" style="display:none">
+      <div class="alert alert-warning" id="informacionConfirmar">
+        重要事項:出入国管理当局では、ユーザーが提出したデータの変更ができないことから、記入ミスのある申請書は、却下されます。
+        申請書に入力した内容の誤りによる出入国カードに関する決定や発行ミスは、ユーザーの責任となります。
+      </div>
+      <div class="row">
+        <div class="col-md-8 col-md-offset-4">
+          <hr>
+        </div>
+      </div>
+      <div class="alert alert-info text-center">
+        <p>入力した情報は正しいですか。</p>
+      </div>
+      <!--                        <hr class="red"/>-->
+      <div class="bottom-buffer">
+        <div class="bottom-buffer" align="right" id="botonesConfirmar">
+          <button id="regresar" type="button" class="btn btn-default ns_">いいえ</button>
+          <button id="confirmar" type="button" class="btn btn-primary ns_">&nbsp;はい&nbsp;</button>
+        </div>
+      </div>
+      <div class="alert alert-info text-center top-buffer">
+        <p>申請書の作成にあたり、ウエブブラウザのポップアッブロック機能をオフにし、
+          Acrobat Readerがインストールされていることを確認してください。</p>
+      </div>
+
+    </div>
+    <div id="solicitudPdf" style="display:none">
+      <div id="fmmPdf">
+      </div>
+    </div>
+  </form>
+
+  <form role="form" action="https://www.banjercito.com.mx/formaMigratoriaMultiple/condicionesGenerales.do" method="POST"
+    class="ns_">
+    <div style="display:none">
+      <div class="form-group" id="div_idioma">
+        <label class="ccontrol-label" for="idioma">Idioma: </label>
+        <input class="form-control ns_" id="idioma" name="idioma" type="text" readonly value="es">
+      </div>
+      <div class="form-group" id="div_noConsecutivoInterno">
+        <label class="control-label" for="noConsecutivoInterno">Consecutivo: </label>
+        <input class="form-control ns_" id="noConsecutivoInterno" name="noConsecutivoInterno" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNumeroPasaporte">
+        <label class="control-label" for="pagoNumeroPasaporte">Número de pasaporte: </label>
+        <input maxlength="15" class="form-control ns_" id="pagoNumeroPasaporte" name="numero_pasaporte" type="text"
+          readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNombres">
+        <label class="control-label" for="pagoNombres">Nombre(s): </label>
+        <input class="form-control ns_" id="pagoNombres" name="nombres" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoApellidos">
+        <label class="control-label" for="pagoApellidos">Apellido(s): </label>
+        <input class="form-control ns_" id="pagoApellidos" name="apellidos" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoNacionalidad">
+        <label class="control-label" for="pagoNacionalidad">Nacionalidad:</label>
+        <input class="form-control ns_" id="pagoNacionalidad" name="nacionalidad" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_pagoFechaNacimiento">
+        <label class="control-label" for="pagoFechaNacimiento">Fecha de nacimiento: </label>
+        <input maxlength="10" class="form-control ns_" id="pagoFechaNacimiento" name="fecha_nacimiento" type="text"
+          readonly>
+      </div>
+      <div class="form-group" id="div_sexo_pago">
+        <label class="control-label" for="sexo_pago">Sexo:</label>
+        <input class="form-control ns_" id="sexo_pago" type="text" readonly>
+
+      </div>
+      <div class="form-group" id="div_pagoSexo">
+        <label class="control-label" for="pagoSexo">Sexo:</label>
+        <input maxlength="1" class="form-control ns_" id="pagoSexo" name="sexo" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_monto_tramite">
+        <label class="control-label" for="monto_tramite">Monto trámite: </label>
+        <input class="form-control ns_" id="monto_tramite" name="monto_tramite" type="text" value="558.00" readonly>
+      </div>
+
+      <div class="form-group" id="div_concepto_tramite">
+        <label class="control-label" for="concepto_tramite">Concepto trámite: </label>
+        <input class="form-control ns_" id="concepto_tramite" name="concepto_tramite" type="text" readonly>
+      </div>
+
+      <div class="form-group" id="div_clave_tramite">
+        <label class="control-label" for="clave_tramite">Clave trámite: </label>
+        <input class="form-control ns_" id="clave_tramite" name="clave_tramite" type="text" readonly>
+      </div>
+    </div>
+    <div class="text-right bottom-buffer" id="div_cont_pago">
+      <button id="realizaPago" type="submit" class="btn btn-primary btn-sm ns_">Enviar datos</button>
+    </div>
+  </form>
+
+</body>
+
+</html>
\ No newline at end of file
diff --git a/components/test/data/autofill/heuristics/output/153_fmm-en_inm.gob.mx.out b/components/test/data/autofill/heuristics/output/153_fmm-en_inm.gob.mx.out
new file mode 100644
index 0000000..eb7dd63
--- /dev/null
+++ b/components/test/data/autofill/heuristics/output/153_fmm-en_inm.gob.mx.out
@@ -0,0 +1,47 @@
+UNKNOWN_TYPE | internacion | Means of entry | 0 | internacion_1-default
+UNKNOWN_TYPE | puntoInternacion | Point of entry | 0 | internacion_1-default
+UNKNOWN_TYPE | fechaLlegada | Date of arrival to Mexico |  | internacion_1-default
+UNKNOWN_TYPE | fechaSalida | Date of departure |  | internacion_1-default
+UNKNOWN_TYPE | nombreAerolinea | Airline name |  | internacion_1-default
+UNKNOWN_TYPE | numeroVuelo | Flight number |  | internacion_1-default
+NAME_FIRST | nombre | Name(s) |  | internacion_1-default
+NAME_LAST | apellidos | Surname(s) |  | internacion_1-default
+UNKNOWN_TYPE | sexo | Gender | 0 | internacion_1-default
+UNKNOWN_TYPE | fechaNacimiento | Date of birth |  | internacion_1-default
+ADDRESS_HOME_COUNTRY | nacionalidad | Nationality (Country) | 0 | internacion_1-default
+ADDRESS_HOME_COUNTRY | paisNacimiento | Country of birth | 0 | internacion_1-default
+UNKNOWN_TYPE | tipoDocumento | Type of document | 0 | internacion_1-default
+UNKNOWN_TYPE | numeroDocumento | Document number |  | internacion_1-default
+UNKNOWN_TYPE | confirmarNumeroDocumento | Document number (Confirmation) |  | internacion_1-default
+ADDRESS_HOME_COUNTRY | paisExpedicion | Country of issue | 0 | paisExpedicion_1-default
+UNKNOWN_TYPE | fechaExpedicion | Date of issue |  | paisExpedicion_1-default
+UNKNOWN_TYPE | confirmarFechaExpedicion | Date of issue (Confirmation) |  | paisExpedicion_1-default
+UNKNOWN_TYPE | fechaExpiracion | Expiration date |  | paisExpedicion_1-default
+UNKNOWN_TYPE | confirmarFechaExpiracion | Expiration date (Confirmation) |  | paisExpedicion_1-default
+ADDRESS_HOME_COUNTRY | paisResidencia | Country of residence | 0 | paisResidencia_1-default
+ADDRESS_HOME_LINE1 | direccionResidencia | Address of residence |  | paisResidencia_1-default
+UNKNOWN_TYPE | motivoViaje | Reason of trip | 0 | paisResidencia_1-default
+UNKNOWN_TYPE | especifiqueMotivo | Specify | 0 | paisResidencia_1-default
+ADDRESS_HOME_STATE | estadoDestino | State | 0 | paisResidencia_1-default
+ADDRESS_HOME_LINE1 | domicilioMexico | Address in Mexico |  | domicilioMexico_1-default
+NAME_FIRST | nombreTutor | Name(s) |  | domicilioMexico_1-default
+NAME_LAST | apellidosTutor | Surname(s) |  | domicilioMexico_1-default
+UNKNOWN_TYPE | sexoTutor | Gender | 0 | domicilioMexico_1-default
+UNKNOWN_TYPE | fechaNacimientoTutor | Date of birth |  | domicilioMexico_1-default
+ADDRESS_HOME_COUNTRY | nacionalidadTutor | Nationality (Country) | 0 | domicilioMexico_1-default
+ADDRESS_HOME_COUNTRY | paisNacimientoTutor | Country of birth | 0 | domicilioMexico_1-default
+EMAIL_ADDRESS | correoElectronico | Email |  | domicilioMexico_1-default
+EMAIL_ADDRESS | confirmacionCorreoElectronico | Email (Confirmation) |  | domicilioMexico_1-default
+UNKNOWN_TYPE | captcha | Verification code: |  | domicilioMexico_1-default
+UNKNOWN_TYPE | idioma | Idioma: | es | idioma_1-default
+UNKNOWN_TYPE | noConsecutivoInterno | Consecutivo: |  | idioma_1-default
+UNKNOWN_TYPE | numero_pasaporte | Número de pasaporte: |  | idioma_1-default
+NAME_FIRST | nombres | Nombre(s): |  | idioma_1-default
+NAME_LAST | apellidos | Apellido(s): |  | idioma_1-default
+UNKNOWN_TYPE | nacionalidad | Nacionalidad: |  | idioma_1-default
+UNKNOWN_TYPE | fecha_nacimiento | Fecha de nacimiento: |  | credit-card-cc
+UNKNOWN_TYPE | sexo_pago | Sexo: |  | idioma_1-default
+UNKNOWN_TYPE | sexo | Sexo: |  | idioma_1-default
+UNKNOWN_TYPE | monto_tramite | Monto trámite: | 558.00 | idioma_1-default
+UNKNOWN_TYPE | concepto_tramite | Concepto trámite: |  | idioma_1-default
+UNKNOWN_TYPE | clave_tramite | Clave trámite: |  | idioma_1-default
diff --git a/components/test/data/autofill/heuristics/output/154_fmm-es_inm.gob.mx.out b/components/test/data/autofill/heuristics/output/154_fmm-es_inm.gob.mx.out
new file mode 100644
index 0000000..1d18dd8
--- /dev/null
+++ b/components/test/data/autofill/heuristics/output/154_fmm-es_inm.gob.mx.out
@@ -0,0 +1,47 @@
+UNKNOWN_TYPE | internacion | Vía de internación | 0 | internacion_1-default
+UNKNOWN_TYPE | puntoInternacion | Punto de internación | 0 | internacion_1-default
+UNKNOWN_TYPE | fechaLlegada | Fecha de llegada a México |  | internacion_1-default
+UNKNOWN_TYPE | fechaSalida | Fecha de salida |  | internacion_1-default
+UNKNOWN_TYPE | nombreAerolinea | Nombre de aerolínea |  | internacion_1-default
+UNKNOWN_TYPE | numeroVuelo | Número de vuelo |  | internacion_1-default
+NAME_FIRST | nombre | Nombre(s) |  | internacion_1-default
+NAME_LAST | apellidos | Apellido(s) |  | internacion_1-default
+UNKNOWN_TYPE | sexo | Sexo | 0 | internacion_1-default
+UNKNOWN_TYPE | fechaNacimiento | Fecha de nacimiento |  | internacion_1-default
+ADDRESS_HOME_COUNTRY | nacionalidad | Nacionalidad (País) | 0 | internacion_1-default
+ADDRESS_HOME_COUNTRY | paisNacimiento | País de nacimiento | 0 | internacion_1-default
+UNKNOWN_TYPE | tipoDocumento | Tipo de documento | 0 | internacion_1-default
+UNKNOWN_TYPE | numeroDocumento | Número de documento |  | internacion_1-default
+UNKNOWN_TYPE | confirmarNumeroDocumento | Número de documento (Confirmación) |  | internacion_1-default
+ADDRESS_HOME_COUNTRY | paisExpedicion | País de expedición | 0 | paisExpedicion_1-default
+UNKNOWN_TYPE | fechaExpedicion | Fecha de expedición |  | paisExpedicion_1-default
+UNKNOWN_TYPE | confirmarFechaExpedicion | Fecha de expedición (Confirmación) |  | paisExpedicion_1-default
+UNKNOWN_TYPE | fechaExpiracion | Fecha de expiración |  | paisExpedicion_1-default
+UNKNOWN_TYPE | confirmarFechaExpiracion | Fecha de expiración (Confirmación) |  | paisExpedicion_1-default
+ADDRESS_HOME_COUNTRY | paisResidencia | País de residencia | 0 | paisResidencia_1-default
+ADDRESS_HOME_LINE1 | direccionResidencia | Dirección de residencia |  | paisResidencia_1-default
+UNKNOWN_TYPE | motivoViaje | Motivo de viaje | 0 | paisResidencia_1-default
+UNKNOWN_TYPE | especifiqueMotivo | Especifique motivo | 0 | paisResidencia_1-default
+ADDRESS_HOME_STATE | estadoDestino | Estado | 0 | paisResidencia_1-default
+UNKNOWN_TYPE | domicilioMexico | Domicilio en México |  | paisResidencia_1-default
+NAME_FIRST | nombreTutor | Nombre(s) |  | paisResidencia_1-default
+NAME_LAST | apellidosTutor | Apellido(s) |  | paisResidencia_1-default
+UNKNOWN_TYPE | sexoTutor | Sexo | 0 | paisResidencia_1-default
+UNKNOWN_TYPE | fechaNacimientoTutor | Fecha de nacimiento |  | paisResidencia_1-default
+ADDRESS_HOME_COUNTRY | nacionalidadTutor | Nacionalidad (País) | 0 | nacionalidadTutor_1-default
+ADDRESS_HOME_COUNTRY | paisNacimientoTutor | País de nacimiento | 0 | nacionalidadTutor_1-default
+EMAIL_ADDRESS | correoElectronico | Correo electrónico |  | paisResidencia_1-default
+EMAIL_ADDRESS | confirmacionCorreoElectronico | Correo electrónico (Confirmación) |  | paisResidencia_1-default
+UNKNOWN_TYPE | captcha | Código de verificación: |  | paisResidencia_1-default
+UNKNOWN_TYPE | idioma | Idioma: | es | idioma_1-default
+UNKNOWN_TYPE | noConsecutivoInterno | Consecutivo: |  | idioma_1-default
+UNKNOWN_TYPE | numero_pasaporte | Número de pasaporte: |  | idioma_1-default
+NAME_FIRST | nombres | Nombre(s): |  | idioma_1-default
+NAME_LAST | apellidos | Apellido(s): |  | idioma_1-default
+UNKNOWN_TYPE | nacionalidad | Nacionalidad: |  | idioma_1-default
+UNKNOWN_TYPE | fecha_nacimiento | Fecha de nacimiento: |  | credit-card-cc
+UNKNOWN_TYPE | sexo_pago | Sexo: |  | idioma_1-default
+UNKNOWN_TYPE | sexo | Sexo: |  | idioma_1-default
+UNKNOWN_TYPE | monto_tramite | Monto trámite: | 558.00 | idioma_1-default
+UNKNOWN_TYPE | concepto_tramite | Concepto trámite: |  | idioma_1-default
+UNKNOWN_TYPE | clave_tramite | Clave trámite: |  | idioma_1-default
diff --git a/components/test/data/autofill/heuristics/output/155_fmm-ja_inm.gob.mx.out b/components/test/data/autofill/heuristics/output/155_fmm-ja_inm.gob.mx.out
new file mode 100644
index 0000000..2f7c19e
--- /dev/null
+++ b/components/test/data/autofill/heuristics/output/155_fmm-ja_inm.gob.mx.out
@@ -0,0 +1,47 @@
+UNKNOWN_TYPE | internacion | 入国手段 | 0 | internacion_1-default
+UNKNOWN_TYPE | puntoInternacion | 入国地点 | 0 | internacion_1-default
+UNKNOWN_TYPE | fechaLlegada | メキシコへの入国日 |  | internacion_1-default
+UNKNOWN_TYPE | fechaSalida | 出国日 |  | internacion_1-default
+UNKNOWN_TYPE | nombreAerolinea | 航空会社名 |  | internacion_1-default
+UNKNOWN_TYPE | numeroVuelo | 便名 |  | internacion_1-default
+NAME_FIRST | nombre | 名 |  | internacion_1-default
+NAME_LAST | apellidos | 姓 |  | internacion_1-default
+UNKNOWN_TYPE | sexo | 性別 | 0 | internacion_1-default
+UNKNOWN_TYPE | fechaNacimiento | 生年月日 |  | internacion_1-default
+ADDRESS_HOME_COUNTRY | nacionalidad | 国籍(国名) | 0 | internacion_1-default
+ADDRESS_HOME_COUNTRY | paisNacimiento | 出生国 | 0 | internacion_1-default
+UNKNOWN_TYPE | tipoDocumento | 書類の種類 | 0 | internacion_1-default
+UNKNOWN_TYPE | numeroDocumento | 書類番号 |  | internacion_1-default
+UNKNOWN_TYPE | confirmarNumeroDocumento | 書類番号(再入力) |  | internacion_1-default
+ADDRESS_HOME_COUNTRY | paisExpedicion | 書類の発行国 | 0 | paisExpedicion_1-default
+UNKNOWN_TYPE | fechaExpedicion | 発行日 |  | paisExpedicion_1-default
+UNKNOWN_TYPE | confirmarFechaExpedicion | 発行日(再入力) |  | paisExpedicion_1-default
+UNKNOWN_TYPE | fechaExpiracion | 有効期限日 |  | paisExpedicion_1-default
+UNKNOWN_TYPE | confirmarFechaExpiracion | 有効期限日(再入力) |  | paisExpedicion_1-default
+ADDRESS_HOME_COUNTRY | paisResidencia | 居住国 | 0 | paisResidencia_1-default
+ADDRESS_HOME_LINE1 | direccionResidencia | 現住所 |  | paisResidencia_1-default
+UNKNOWN_TYPE | motivoViaje | 旅行目的 | 0 | paisResidencia_1-default
+UNKNOWN_TYPE | especifiqueMotivo | 具体的な目的 | 0 | paisResidencia_1-default
+ADDRESS_HOME_STATE | estadoDestino | 州名 | 0 | paisResidencia_1-default
+ADDRESS_HOME_LINE1 | domicilioMexico | メキシコ国内の住所 |  | domicilioMexico_1-default
+NAME_FIRST | nombreTutor | 名 |  | domicilioMexico_1-default
+NAME_LAST | apellidosTutor | 姓 |  | domicilioMexico_1-default
+UNKNOWN_TYPE | sexoTutor | 性別 | 0 | domicilioMexico_1-default
+UNKNOWN_TYPE | fechaNacimientoTutor | 生年月日 |  | domicilioMexico_1-default
+ADDRESS_HOME_COUNTRY | nacionalidadTutor | 国籍(国名) | 0 | domicilioMexico_1-default
+ADDRESS_HOME_COUNTRY | paisNacimientoTutor | 出生国 | 0 | domicilioMexico_1-default
+EMAIL_ADDRESS | correoElectronico | メールアドレス |  | domicilioMexico_1-default
+EMAIL_ADDRESS | confirmacionCorreoElectronico | メールアドレス(再入力) |  | domicilioMexico_1-default
+UNKNOWN_TYPE | captcha | 認証コード |  | domicilioMexico_1-default
+UNKNOWN_TYPE | idioma | Idioma: | es | idioma_1-default
+UNKNOWN_TYPE | noConsecutivoInterno | Consecutivo: |  | idioma_1-default
+UNKNOWN_TYPE | numero_pasaporte | Número de pasaporte: |  | idioma_1-default
+NAME_FIRST | nombres | Nombre(s): |  | idioma_1-default
+NAME_LAST | apellidos | Apellido(s): |  | idioma_1-default
+UNKNOWN_TYPE | nacionalidad | Nacionalidad: |  | idioma_1-default
+UNKNOWN_TYPE | fecha_nacimiento | Fecha de nacimiento: |  | credit-card-cc
+UNKNOWN_TYPE | sexo_pago | Sexo: |  | idioma_1-default
+UNKNOWN_TYPE | sexo | Sexo: |  | idioma_1-default
+UNKNOWN_TYPE | monto_tramite | Monto trámite: | 558.00 | idioma_1-default
+UNKNOWN_TYPE | concepto_tramite | Concepto trámite: |  | idioma_1-default
+UNKNOWN_TYPE | clave_tramite | Clave trámite: |  | idioma_1-default
diff --git a/components/test/data/payments/payment_request_update_with_test.html b/components/test/data/payments/payment_request_update_with_test.html
index 9dcc6c2..522e0450 100644
--- a/components/test/data/payments/payment_request_update_with_test.html
+++ b/components/test/data/payments/payment_request_update_with_test.html
@@ -12,8 +12,9 @@
 <link rel="stylesheet" type="text/css" href="style.css">
 </head>
 <body>
-<button onclick="updateWithNoShippingOptions()" id="updateWithNoShippingOptions">updateWithNoShippingOptions</button>
-<button onclick="updateWithShippingOptions()" id="updateWithShippingOptions">updateWithShippingOptions</button>
+<button class="small" onclick="updateWithEmpty()" id="updateWithEmpty">updateWithEmpty</button>
+<button class="small" onclick="updateWithTotal()" id="updateWithTotal">updateWithTotal</button>
+<button class="small" onclick="updateWithShippingOptions()" id="updateWithShippingOptions">updateWithShippingOptions</button>
 <pre id="result"></pre>
 <script src="util.js"></script>
 <script src="update_with.js"></script>
diff --git a/components/test/data/payments/style.css b/components/test/data/payments/style.css
index efe83cb1..9126316b 100644
--- a/components/test/data/payments/style.css
+++ b/components/test/data/payments/style.css
@@ -10,6 +10,12 @@
   width: 100%;
 }
 
+button.small {
+  font-size: 2em;
+  height: 3em;
+  width: 100%;
+}
+
 pre {
   font-size: 2em;
 }
diff --git a/components/test/data/payments/update_with.js b/components/test/data/payments/update_with.js
index 87db7e5..264d6bd 100644
--- a/components/test/data/payments/update_with.js
+++ b/components/test/data/payments/update_with.js
@@ -47,9 +47,24 @@
 }
 
 /**
- * Calls updateWith() with no shipping options
+ * Calls updateWith() with {}
  */
-function updateWithNoShippingOptions() {  // eslint-disable-line no-unused-vars
+function updateWithEmpty() {  // eslint-disable-line no-unused-vars
+  var pr = buildPaymentRequest();
+  var updatedDetails = {};
+  pr.addEventListener('shippingaddresschange', function(e) {
+    e.updateWith(updatedDetails);
+  });
+  pr.addEventListener('shippingoptionchange', function(e) {
+    e.updateWith(updatedDetails);
+  });
+  showPaymentRequest(pr);
+}
+
+/**
+ * Calls updateWith() with total
+ */
+function updateWithTotal() {  // eslint-disable-line no-unused-vars
   var pr = buildPaymentRequest();
   var updatedDetails = {
     total: {label: 'Updated total', amount: {currency: 'USD', value: '10.00'}},
@@ -69,7 +84,6 @@
 function updateWithShippingOptions() {  // eslint-disable-line no-unused-vars
   var pr = buildPaymentRequest();
   var updatedDetails = {
-    total: {label: 'Updated total', amount: {currency: 'USD', value: '10.00'}},
     shippingOptions: [{
       selected: true,
       id: 'updatedShipping',
diff --git a/components/variations/net/DEPS b/components/variations/net/DEPS
index 432ab454..4b6ac37 100644
--- a/components/variations/net/DEPS
+++ b/components/variations/net/DEPS
@@ -3,4 +3,5 @@
   "+components/metrics",
   "+net",
   "+services/network/public/cpp",
+  "+services/network/public/mojom",
 ]
diff --git a/components/variations/net/variations_http_headers.cc b/components/variations/net/variations_http_headers.cc
index c06fd47..0a0ca46 100644
--- a/components/variations/net/variations_http_headers.cc
+++ b/components/variations/net/variations_http_headers.cc
@@ -112,9 +112,10 @@
       return false;
 
     if (resource_request_) {
-      // Set the variations header to client_data_header rather than headers to
-      // be exempted from CORS checks.
-      resource_request_->client_data_header = variations_header_;
+      // Set the variations header to cors_exempt_headers rather than headers
+      // to be exempted from CORS checks.
+      resource_request_->cors_exempt_headers.SetHeaderIfMissing(
+          kClientDataHeader, variations_header_);
     } else if (url_request_) {
       url_request_->SetExtraRequestHeaderByName(kClientDataHeader,
                                                 variations_header_, false);
@@ -229,11 +230,16 @@
 }
 
 bool HasVariationsHeader(const network::ResourceRequest& request) {
-  return !request.client_data_header.empty();
+  return request.cors_exempt_headers.HasHeader(kClientDataHeader);
 }
 
 bool ShouldAppendVariationsHeaderForTesting(const GURL& url) {
   return ShouldAppendVariationsHeader(url);
 }
 
+void UpdateCorsExemptHeaderForVariations(
+    network::mojom::NetworkContextParams* params) {
+  params->cors_exempt_header_list.push_back(kClientDataHeader);
+}
+
 }  // namespace variations
diff --git a/components/variations/net/variations_http_headers.h b/components/variations/net/variations_http_headers.h
index a25f45d..a18c6ed 100644
--- a/components/variations/net/variations_http_headers.h
+++ b/components/variations/net/variations_http_headers.h
@@ -10,6 +10,8 @@
 #include <string>
 #include <vector>
 
+#include "services/network/public/mojom/network_context.mojom.h"
+
 namespace net {
 struct NetworkTrafficAnnotationTag;
 struct RedirectInfo;
@@ -112,6 +114,11 @@
 // Calls the internal ShouldAppendVariationsHeader() for testing.
 bool ShouldAppendVariationsHeaderForTesting(const GURL& url);
 
+// Updates |cors_exempt_header_list| field of the given |param| to register the
+// variation headers.
+void UpdateCorsExemptHeaderForVariations(
+    network::mojom::NetworkContextParams* params);
+
 }  // namespace variations
 
 #endif  // COMPONENTS_VARIATIONS_NET_VARIATIONS_HTTP_HEADERS_H_
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc
index 8dc7d40..d66a94ce 100644
--- a/components/viz/service/display/software_renderer.cc
+++ b/components/viz/service/display/software_renderer.cc
@@ -46,14 +46,16 @@
       : image_animation_map_(image_animation_map) {}
   ~AnimatedImagesProvider() override = default;
 
-  ScopedDecodedDrawImage GetDecodedDrawImage(
+  ImageProvider::ScopedResult GetRasterContent(
       const cc::DrawImage& draw_image) override {
+    // TODO(xidachen): Ensure this function works for paint worklet generated
+    // images.
     const auto& paint_image = draw_image.paint_image();
     auto it = image_animation_map_->find(paint_image.stable_id());
     size_t frame_index = it == image_animation_map_->end()
                              ? cc::PaintImage::kDefaultFrameIndex
                              : it->second;
-    return ScopedDecodedDrawImage(cc::DecodedDrawImage(
+    return ScopedResult(cc::DecodedDrawImage(
         paint_image.GetSkImageForFrame(
             frame_index, cc::PaintImage::kDefaultGeneratorClientId),
         SkSize::Make(0, 0), SkSize::Make(1.f, 1.f), draw_image.filter_quality(),
diff --git a/content/browser/file_url_loader_factory.cc b/content/browser/file_url_loader_factory.cc
index 2923ee04..c5c9117b 100644
--- a/content/browser/file_url_loader_factory.cc
+++ b/content/browser/file_url_loader_factory.cc
@@ -113,8 +113,9 @@
     const GURL url) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService));
-  return !shared_cors_origin_access_list->GetOriginAccessList().IsAllowed(
-      origin, url);
+  return shared_cors_origin_access_list->GetOriginAccessList().CheckAccessState(
+             origin, url) !=
+         network::cors::OriginAccessList::AccessState::kAllowed;
 }
 
 class FileURLDirectoryLoader
@@ -772,8 +773,9 @@
       // Only internal call sites, such as ExtensionDownloader, is permitted.
       DCHECK(shared_cors_origin_access_list_);
       cors_flag =
-          !shared_cors_origin_access_list_->GetOriginAccessList().IsAllowed(
-              *request.request_initiator, request.url);
+          shared_cors_origin_access_list_->GetOriginAccessList()
+              .CheckAccessState(*request.request_initiator, request.url) !=
+          network::cors::OriginAccessList::AccessState::kAllowed;
     } else {
       // TODO(toyoshim): Remove this thread-hop code once the NetworkService is
       // fully enabled, and if other IO thread users do not need cors enabled
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index b3265ed6..5714b54 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -95,10 +95,10 @@
 // current entries are [google, digg, yahoo], with the current entry google,
 // and the user types in cnet, then digg and yahoo are pruned.
 void NotifyPrunedEntries(NavigationControllerImpl* nav_controller,
-                         bool from_front,
+                         int index,
                          int count) {
   PrunedDetails details;
-  details.from_front = from_front;
+  details.index = index;
   details.count = count;
   nav_controller->delegate()->NotifyNavigationListPruned(details);
 }
@@ -2404,8 +2404,11 @@
       entries_.pop_back();
       current_size--;
     }
-    if (num_pruned > 0)  // Only notify if we did prune something.
-      NotifyPrunedEntries(this, false, num_pruned);
+    if (num_pruned > 0) {  // Only notify if we did prune something.
+      NotifyPrunedEntries(this,
+                          last_committed_entry_index_ + 1 /* start index */,
+                          num_pruned /* count */);
+    }
   }
 
   PruneOldestEntryIfFull();
@@ -2421,7 +2424,7 @@
   DCHECK_EQ(max_entry_count(), entries_.size());
   DCHECK_GT(last_committed_entry_index_, 0);
   RemoveEntryAtIndex(0);
-  NotifyPrunedEntries(this, true, 1);
+  NotifyPrunedEntries(this, 0 /* start index */, 1 /* count */);
   // TODO(crbug.com/907167): Consider removing the earliest skippable entry
   // instead of the first entry.
 }
diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc
index 4413e75..20145db 100644
--- a/content/browser/frame_host/navigation_controller_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc
@@ -2693,7 +2693,7 @@
 
   // We should have got a pruned navigation.
   EXPECT_EQ(1U, navigation_list_pruned_counter_);
-  EXPECT_TRUE(last_navigation_entry_pruned_details_.from_front);
+  EXPECT_EQ(0, last_navigation_entry_pruned_details_.index);
   EXPECT_EQ(1, last_navigation_entry_pruned_details_.count);
 
   // We expect http://www.a.com/0 to be gone.
@@ -4215,7 +4215,7 @@
 
   // We should have received a pruned notification.
   EXPECT_EQ(1U, navigation_list_pruned_counter_);
-  EXPECT_TRUE(last_navigation_entry_pruned_details_.from_front);
+  EXPECT_EQ(0, last_navigation_entry_pruned_details_.index);
   EXPECT_EQ(1, last_navigation_entry_pruned_details_.count);
 
   // other_controller should now contain only 3 urls: url2, url3 and url4.
diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc
index b39aa13..a6d373e19 100644
--- a/content/browser/gpu/gpu_process_host.cc
+++ b/content/browser/gpu/gpu_process_host.cc
@@ -252,6 +252,7 @@
     switches::kUseCmdDecoder,
     switches::kForceVideoOverlays,
 #if defined(OS_ANDROID)
+    switches::kEnableReachedCodeProfiler,
     switches::kOrderfileMemoryOptimization,
 #endif
     switches::kWebglAntialiasingMode,
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc
index a704b83..2e568ca 100644
--- a/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -873,18 +873,10 @@
   new_request->SetReferrer(network::ComputeReferrer(request_data.referrer));
   new_request->set_referrer_policy(request_data.referrer_policy);
 
-  new_request->SetExtraRequestHeaders(headers);
-  // X-Requested-With and X-Client-Data header must be set here to avoid
-  // breaking CORS checks. They are non-empty when the values are given by the
-  // UA code, therefore they should be ignored by CORS checks.
-  if (!request_data.requested_with_header.empty()) {
-    new_request->SetExtraRequestHeaderByName(
-        "X-Requested-With", request_data.requested_with_header, true);
-  }
-  if (!request_data.client_data_header.empty()) {
-    new_request->SetExtraRequestHeaderByName(
-        "X-Client-Data", request_data.client_data_header, true);
-  }
+  // Internal headers must be set here to avoid being blocked by CORS checks.
+  net::HttpRequestHeaders merged_headers = headers;
+  merged_headers.MergeFrom(request_data.cors_exempt_headers);
+  new_request->SetExtraRequestHeaders(merged_headers);
 
   std::unique_ptr<network::ScopedThrottlingToken> throttling_token =
       network::ScopedThrottlingToken::MaybeCreate(
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc
index 6f05b47..05ffffa 100644
--- a/content/browser/network_service_restart_browsertest.cc
+++ b/content/browser/network_service_restart_browsertest.cc
@@ -20,6 +20,7 @@
 #include "content/browser/url_loader_factory_getter.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/cors_exempt_headers.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 9045c813..54eb543 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -3084,6 +3084,7 @@
     switches::kDisallowNonExactResourceReuse,
 #if defined(OS_ANDROID)
     switches::kDisableMediaSessionAPI,
+    switches::kEnableReachedCodeProfiler,
     switches::kOrderfileMemoryOptimization,
     switches::kRendererWaitForJavaDebugger,
 #endif
diff --git a/content/browser/tracing/tracing_controller_impl.cc b/content/browser/tracing/tracing_controller_impl.cc
index beed93a..558c2c5 100644
--- a/content/browser/tracing/tracing_controller_impl.cc
+++ b/content/browser/tracing/tracing_controller_impl.cc
@@ -50,6 +50,7 @@
 #endif
 
 #if defined(OS_WIN)
+#include "base/win/registry.h"
 #include "base/win/windows_version.h"
 #endif
 
@@ -109,6 +110,32 @@
   return std::string();
 }
 
+#if defined(OS_WIN)
+// The following code detect whether the current session is a remote session.
+// See:
+// https://docs.microsoft.com/en-us/windows/desktop/TermServ/detecting-the-terminal-services-environment
+bool IsCurrentSessionRemote() {
+  static const wchar_t kRdpSettingsKeyName[] =
+      L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server";
+  static const wchar_t kGlassSessionIdValueName[] = L"GlassSessionId";
+
+  if (::GetSystemMetrics(SM_REMOTESESSION))
+    return true;
+
+  DWORD glass_session_id = 0;
+  DWORD current_session_id = 0;
+  base::win::RegKey key(HKEY_LOCAL_MACHINE, kRdpSettingsKeyName, KEY_READ);
+  if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &current_session_id) ||
+      !key.Valid() ||
+      key.ReadValueDW(kGlassSessionIdValueName, &glass_session_id) !=
+          ERROR_SUCCESS) {
+    return false;
+  }
+
+  return current_session_id != glass_session_id;
+}
+#endif
+
 }  // namespace
 
 TracingController* TracingController::GetInstance() {
@@ -219,7 +246,11 @@
       metadata_dict->SetString("os-wow64", "disabled");
     }
   }
+
+  metadata_dict->SetString("os-session",
+                           IsCurrentSessionRemote() ? "remote" : "local");
 #endif
+
   metadata_dict->SetString("os-arch",
                            base::SysInfo::OperatingSystemArchitecture());
 
diff --git a/content/browser/utility_process_host.cc b/content/browser/utility_process_host.cc
index 5e1c913..25c05c1 100644
--- a/content/browser/utility_process_host.cc
+++ b/content/browser/utility_process_host.cc
@@ -376,6 +376,7 @@
       switches::kV,
       switches::kVModule,
 #if defined(OS_ANDROID)
+      switches::kEnableReachedCodeProfiler,
       switches::kOrderfileMemoryOptimization,
 #endif
       // These flags are used by the audio service:
diff --git a/content/common/content_constants_internal.cc b/content/common/content_constants_internal.cc
index be2463a..d6e2a4f 100644
--- a/content/common/content_constants_internal.cc
+++ b/content/common/content_constants_internal.cc
@@ -37,4 +37,6 @@
 const char kMachBootstrapName[] = "rohitfork";
 #endif
 
+const char kCorsExemptRequestedWithHeaderName[] = "X-Requested-With";
+
 } // namespace content
diff --git a/content/common/content_constants_internal.h b/content/common/content_constants_internal.h
index 9c0767b..b520383 100644
--- a/content/common/content_constants_internal.h
+++ b/content/common/content_constants_internal.h
@@ -45,6 +45,11 @@
 CONTENT_EXPORT extern const char kMachBootstrapName[];
 #endif
 
+// Defines a HTTP header name that is set internally, and some code places
+// in content need to know the name to manage the header stored in
+// network::ResourceRequest::cors_exempt_headers.
+extern const char kCorsExemptRequestedWithHeaderName[];
+
 } // namespace content
 
 #endif  // CONTENT_COMMON_CONTENT_CONSTANTS_INTERNAL_H_
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 09f7ea9..dfde858 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -105,6 +105,8 @@
     "content_browser_client.cc",
     "content_browser_client.h",
     "cookie_store_factory.h",
+    "cors_exempt_headers.cc",
+    "cors_exempt_headers.h",
     "delegate_to_browser_gpu_service_accelerator_factory.h",
     "desktop_capture.cc",
     "desktop_capture.h",
diff --git a/content/public/browser/DEPS b/content/public/browser/DEPS
index 49fd1ed..805504c5 100644
--- a/content/public/browser/DEPS
+++ b/content/public/browser/DEPS
@@ -18,6 +18,7 @@
 specific_include_rules = {
   ".*\.cc": [
     "+content/browser",
+    "+content/common/content_constants_internal.h",
     "-content/browser/loader",
 
     # TODO: content/browser/loader is being separated out of content, and this
diff --git a/content/public/browser/cors_exempt_headers.cc b/content/public/browser/cors_exempt_headers.cc
new file mode 100644
index 0000000..c82e8d71
--- /dev/null
+++ b/content/public/browser/cors_exempt_headers.cc
@@ -0,0 +1,18 @@
+// Copyright 2019 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.
+
+#include "content/public/browser/cors_exempt_headers.h"
+
+#include "content/common/content_constants_internal.h"
+
+namespace content {
+
+void UpdateCorsExemptHeader(network::mojom::NetworkContextParams* params) {
+  // Note: This mechanism will be deprecated in the near future. You can find
+  // a recommended alternative approach on URLRequest::cors_exempt_headers at
+  // services/network/public/mojom/url_loader.mojom.
+  params->cors_exempt_header_list.push_back(kCorsExemptRequestedWithHeaderName);
+}
+
+}  // namespace content
diff --git a/content/public/browser/cors_exempt_headers.h b/content/public/browser/cors_exempt_headers.h
new file mode 100644
index 0000000..c9b5cf1
--- /dev/null
+++ b/content/public/browser/cors_exempt_headers.h
@@ -0,0 +1,21 @@
+// Copyright 2019 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.
+
+#ifndef CONTENT_PUBLIC_BROWSER_CORS_EXEMPT_HEADERS_H_
+#define CONTENT_PUBLIC_BROWSER_CORS_EXEMPT_HEADERS_H_
+
+#include "content/common/content_export.h"
+#include "services/network/public/mojom/network_context.mojom.h"
+
+namespace content {
+
+// Updates |cors_exempt_header_list| field of the given |param| to register
+// headers that are used in content for special purpose and should not be
+// blocked by CORS checks.
+CONTENT_EXPORT void UpdateCorsExemptHeader(
+    network::mojom::NetworkContextParams* params);
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_CORS_EXEMPT_HEADERS_H_
diff --git a/content/public/browser/navigation_details.h b/content/public/browser/navigation_details.h
index 0386d5a..7155040 100644
--- a/content/public/browser/navigation_details.h
+++ b/content/public/browser/navigation_details.h
@@ -76,9 +76,8 @@
 
 // Details sent for NOTIFY_NAV_LIST_PRUNED.
 struct PrunedDetails {
-  // If true, count items were removed from the front of the list, otherwise
-  // count items were removed from the back of the list.
-  bool from_front;
+  // Index starting which |count| entries were removed.
+  int index;
 
   // Number of items removed.
   int count;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 9f93e3f..bc88ea5c 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -423,6 +423,12 @@
 #endif
 };
 
+// Signed Exchange Reporting for distributors
+// https://www.chromestatus.com/features/5687904902840320
+const base::Feature kSignedExchangeReportingForDistributors{
+    "SignedExchangeReportingForDistributors",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Origin-Signed HTTP Exchanges (for WebPackage Loading)
 // https://www.chromestatus.com/features/5745285984681984
 const base::Feature kSignedHTTPExchange{"SignedHTTPExchange",
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index dd36944..dc30463 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -101,6 +101,8 @@
 CONTENT_EXPORT extern const base::Feature kServiceWorkerLongRunningMessage;
 CONTENT_EXPORT extern const base::Feature kServiceWorkerPaymentApps;
 CONTENT_EXPORT extern const base::Feature kSharedArrayBuffer;
+CONTENT_EXPORT extern const base::Feature
+    kSignedExchangeReportingForDistributors;
 CONTENT_EXPORT extern const base::Feature kSignedHTTPExchange;
 CONTENT_EXPORT extern const base::Feature kSignedHTTPExchangeAcceptHeader;
 CONTENT_EXPORT extern const char
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 2ecaf78..d53f9d1 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -5,6 +5,7 @@
 #include "content/public/test/browser_test_base.h"
 
 #include <stddef.h>
+#include <iostream>
 
 #include "base/base_switches.h"
 #include "base/bind.h"
@@ -91,7 +92,12 @@
     message += strsignal(signal);
     message += ". Backtrace:\n";
     logging::RawLog(logging::LOG_ERROR, message.c_str());
-    base::debug::StackTrace().Print();
+    auto stack_trace = base::debug::StackTrace();
+    stack_trace.OutputToStream(&std::cerr);
+#if defined(OS_ANDROID)
+    // Also output the trace to logcat on Android.
+    stack_trace.Print();
+#endif
   }
   _exit(128 + signal);
 }
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index f7cbc5c..ef22a32 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -26,6 +26,7 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "content/child/child_thread_impl.h"
+#include "content/common/content_constants_internal.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/navigation_policy.h"
@@ -725,10 +726,13 @@
     resource_request->headers.SetHeaderIfMissing(network::kAcceptHeader,
                                                  network::kDefaultAcceptHeader);
   }
-  resource_request->requested_with_header =
-      WebString(request.GetRequestedWithHeader()).Utf8();
-  resource_request->client_data_header =
-      WebString(request.GetClientDataHeader()).Utf8();
+  // Set X-Requested-With header to cors_exempt_headers rather than headers to
+  // be exempted from CORS checks.
+  if (!request.GetRequestedWithHeader().IsEmpty()) {
+    resource_request->cors_exempt_headers.SetHeader(
+        kCorsExemptRequestedWithHeaderName,
+        WebString(request.GetRequestedWithHeader()).Utf8());
+  }
 
   if (resource_request->resource_type == RESOURCE_TYPE_PREFETCH ||
       resource_request->resource_type == RESOURCE_TYPE_FAVICON) {
diff --git a/content/renderer/media/stream/processed_local_audio_source.cc b/content/renderer/media/stream/processed_local_audio_source.cc
index 13efe28..8996cce 100644
--- a/content/renderer/media/stream/processed_local_audio_source.cc
+++ b/content/renderer/media/stream/processed_local_audio_source.cc
@@ -42,6 +42,50 @@
   return false;
 #endif
 }
+
+void LogAudioProcesingProperties(const AudioProcessingProperties& properties) {
+  auto aec_to_string =
+      [](AudioProcessingProperties::EchoCancellationType type) {
+        using AEC = AudioProcessingProperties::EchoCancellationType;
+        switch (type) {
+          case AEC::kEchoCancellationDisabled:
+            return "disabled";
+          case AEC::kEchoCancellationAec2:
+            return "aec2";
+          case AEC::kEchoCancellationAec3:
+            return "aec3";
+          case AEC::kEchoCancellationSystem:
+            return "system";
+        }
+      };
+  auto bool_to_string = [](bool value) { return value ? "true" : "false"; };
+  auto str = base::StringPrintf(
+      "AudioProcessingProperties: "
+      "aec=%s, "
+      "disable_hw_ns=%s, "
+      "goog_audio_mirroring=%s, "
+      "goog_auto_gain_control=%s, "
+      "goog_experimental_echo_cancellation=%s, "
+      "goog_typing_noise_detection=%s, "
+      "goog_noise_suppression=%s, "
+      "goog_experimental_noise_suppression=%s, "
+      "goog_highpass_filter=%s, "
+      "goog_experimental_agc=%s, "
+      "hybrid_agc=%s",
+      aec_to_string(properties.echo_cancellation_type),
+      bool_to_string(properties.disable_hw_noise_suppression),
+      bool_to_string(properties.goog_audio_mirroring),
+      bool_to_string(properties.goog_auto_gain_control),
+      bool_to_string(properties.goog_experimental_echo_cancellation),
+      bool_to_string(properties.goog_typing_noise_detection),
+      bool_to_string(properties.goog_noise_suppression),
+      bool_to_string(properties.goog_experimental_noise_suppression),
+      bool_to_string(properties.goog_highpass_filter),
+      bool_to_string(properties.goog_experimental_auto_gain_control),
+      bool_to_string(base::FeatureList::IsEnabled(features::kWebRtcHybridAgc)));
+
+  WebRtcLogMessage(str);
+}
 }  // namespace
 
 ProcessedLocalAudioSource::ProcessedLocalAudioSource(
@@ -108,6 +152,8 @@
   WebRtcLogMessage(str);
   DVLOG(1) << str;
 
+  LogAudioProcesingProperties(audio_processing_properties_);
+
   blink::MediaStreamDevice modified_device(device());
   bool device_is_modified = false;
 
@@ -230,7 +276,12 @@
       source_params.processing->settings.automatic_gain_control =
           media::AutomaticGainControlType::kHybridExperimental;
     }
+    WebRtcLogMessage(base::StringPrintf(
+        "Using APM in audio process; settings: %s",
+        source_params.processing->settings.ToString().c_str()));
+
   } else {
+    WebRtcLogMessage("Using APM in renderer process.");
     audio_processor_ = new rtc::RefCountedObject<MediaStreamAudioProcessor>(
         audio_processing_properties_, rtc_audio_device);
     params.set_frames_per_buffer(GetBufferSize(device().input.sample_rate()));
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index 553efc8..02dd519 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -1937,13 +1937,6 @@
   return GetPreferredPrintOutputFormat(&format, params);
 }
 
-bool PepperPluginInstanceImpl::IsPrintScalingDisabled() {
-  DCHECK(plugin_print_interface_);
-  if (!plugin_print_interface_)
-    return false;
-  return plugin_print_interface_->IsScalingDisabled(pp_instance()) == PP_TRUE;
-}
-
 int PepperPluginInstanceImpl::PrintBegin(const WebPrintParams& print_params) {
   // Keep a reference on the stack. See NOTE above.
   scoped_refptr<PepperPluginInstanceImpl> ref(this);
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h
index 275e3c1..cb17be7d 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.h
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.h
@@ -271,7 +271,6 @@
   void StopFind();
 
   bool SupportsPrintInterface();
-  bool IsPrintScalingDisabled();
   int PrintBegin(const blink::WebPrintParams& print_params);
   void PrintPage(int page_number, cc::PaintCanvas* canvas);
   void PrintEnd();
diff --git a/content/renderer/pepper/pepper_webplugin_impl.cc b/content/renderer/pepper/pepper_webplugin_impl.cc
index 183c17f..8ce7bdc 100644
--- a/content/renderer/pepper/pepper_webplugin_impl.cc
+++ b/content/renderer/pepper/pepper_webplugin_impl.cc
@@ -424,14 +424,6 @@
   return instance_->SupportsPrintInterface();
 }
 
-bool PepperWebPluginImpl::IsPrintScalingDisabled() {
-  // Re-entrancy may cause JS to try to execute script on the plugin before it
-  // is fully initialized. See: crbug.com/715747.
-  if (!instance_)
-    return false;
-  return instance_->IsPrintScalingDisabled();
-}
-
 int PepperWebPluginImpl::PrintBegin(const WebPrintParams& print_params) {
   // Re-entrancy may cause JS to try to execute script on the plugin before it
   // is fully initialized. See: crbug.com/715747.
diff --git a/content/renderer/pepper/pepper_webplugin_impl.h b/content/renderer/pepper/pepper_webplugin_impl.h
index f0fc4a3..5ee76e1 100644
--- a/content/renderer/pepper/pepper_webplugin_impl.h
+++ b/content/renderer/pepper/pepper_webplugin_impl.h
@@ -78,7 +78,6 @@
   void SelectFindResult(bool forward, int identifier) override;
   void StopFind() override;
   bool SupportsPaginatedPrint() override;
-  bool IsPrintScalingDisabled() override;
 
   int PrintBegin(const blink::WebPrintParams& print_params) override;
   void PrintPage(int page_number, cc::PaintCanvas* canvas) override;
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index 78ca1e6..2df24c1 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -18,6 +18,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "content/public/browser/client_certificate_delegate.h"
+#include "content/public/browser/cors_exempt_headers.h"
 #include "content/public/browser/login_delegate.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/page_navigator.h"
@@ -538,6 +539,7 @@
   network::mojom::NetworkContextPtr network_context;
   network::mojom::NetworkContextParamsPtr context_params =
       network::mojom::NetworkContextParams::New();
+  UpdateCorsExemptHeader(context_params.get());
   context_params->user_agent = GetUserAgent();
   context_params->accept_language = "en-us,en";
   context_params->enable_data_url_support = true;
diff --git a/content/test/url_loader_interceptor_test.cc b/content/test/url_loader_interceptor_test.cc
index 61004e24..bde75f1 100644
--- a/content/test/url_loader_interceptor_test.cc
+++ b/content/test/url_loader_interceptor_test.cc
@@ -143,12 +143,9 @@
   }
 
   // network::mojom::TrustedURLLoaderHeaderClient:
-  void OnBeforeSendHeaders(int32_t request_id,
-                           const net::HttpRequestHeaders& headers,
-                           OnBeforeSendHeadersCallback callback) override {}
-  void OnHeadersReceived(int32_t request_id,
-                         const std::string& headers,
-                         OnHeadersReceivedCallback callback) override {}
+  void OnLoaderCreated(
+      int32_t request_id,
+      network::mojom::TrustedHeaderClientRequest request) override {}
 
   mojo::BindingSet<network::mojom::TrustedURLLoaderHeaderClient> bindings_;
 };
diff --git a/device/fido/ctap_make_credential_request.h b/device/fido/ctap_make_credential_request.h
index 12b72d5..8050054 100644
--- a/device/fido/ctap_make_credential_request.h
+++ b/device/fido/ctap_make_credential_request.h
@@ -81,7 +81,6 @@
   const base::Optional<std::vector<uint8_t>>& pin_auth() const {
     return pin_auth_;
   }
-  const base::Optional<uint8_t>& pin_protocol() const { return pin_protocol_; }
 
   void set_is_u2f_only(bool is_u2f_only) { is_u2f_only_ = is_u2f_only; }
   bool is_u2f_only() { return is_u2f_only_; }
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h
index 85cfa30..02cc5b4 100644
--- a/device/fido/fido_constants.h
+++ b/device/fido/fido_constants.h
@@ -105,7 +105,7 @@
   kCtap2ErrUserActionPending = 0x23,
   kCtap2ErrOperationPending = 0x24,
   kCtap2ErrNoOperations = 0x25,
-  kCtap2ErrUnsupportedAlgorithm = 0x26,
+  kCtap2ErrUnsupportedAlgorithms = 0x26,
   kCtap2ErrOperationDenied = 0x27,
   kCtap2ErrKeyStoreFull = 0x28,
   kCtap2ErrNotBusy = 0x29,
@@ -156,7 +156,7 @@
           CtapDeviceResponseCode::kCtap2ErrUserActionPending,
           CtapDeviceResponseCode::kCtap2ErrOperationPending,
           CtapDeviceResponseCode::kCtap2ErrNoOperations,
-          CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm,
+          CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithms,
           CtapDeviceResponseCode::kCtap2ErrOperationDenied,
           CtapDeviceResponseCode::kCtap2ErrKeyStoreFull,
           CtapDeviceResponseCode::kCtap2ErrNotBusy,
diff --git a/device/fido/mac/make_credential_operation.mm b/device/fido/mac/make_credential_operation.mm
index be3d8e5..8c81fc5 100644
--- a/device/fido/mac/make_credential_operation.mm
+++ b/device/fido/mac/make_credential_operation.mm
@@ -64,7 +64,7 @@
   if (!std::any_of(key_params.begin(), key_params.end(), is_es256)) {
     DVLOG(1) << "No supported algorithm found.";
     std::move(callback())
-        .Run(CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm,
+        .Run(CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithms,
              base::nullopt);
     return;
   }
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index c3371c6b..d1fe3d50 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -50,87 +50,28 @@
                                        data.value_or(std::vector<uint8_t>{}))));
 }
 
-// CheckUserVerification implements the first, common steps of
-// makeCredential and getAssertion from the CTAP2 spec.
-CtapDeviceResponseCode CheckUserVerification(
-    bool is_make_credential,
-    const AuthenticatorSupportedOptions& options,
-    const base::Optional<std::vector<uint8_t>>& pin_auth,
-    const base::Optional<uint8_t>& pin_protocol,
-    UserVerificationRequirement user_verification,
-    base::RepeatingCallback<void(void)> simulate_press_callback,
-    bool* out_user_verified) {
-  // The following quotes are from the CTAP2 spec:
+bool AreMakeCredentialOptionsValid(const AuthenticatorSupportedOptions& options,
+                                   const CtapMakeCredentialRequest& request) {
+  if (request.resident_key_required() && !options.supports_resident_key)
+    return false;
 
-  // 1. "If authenticator supports clientPin and platform sends a zero length
-  // pinAuth, wait for user touch and then return either CTAP2_ERR_PIN_NOT_SET
-  // if pin is not set or CTAP2_ERR_PIN_INVALID if pin has been set."
-  const bool supports_pin =
-      options.client_pin_availability !=
-      AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported;
-  if (supports_pin && pin_auth && pin_auth->empty()) {
-    if (simulate_press_callback) {
-      simulate_press_callback.Run();
-    }
-    switch (options.client_pin_availability) {
-      case AuthenticatorSupportedOptions::ClientPinAvailability::
-          kSupportedAndPinSet:
-        return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
-      case AuthenticatorSupportedOptions::ClientPinAvailability::
-          kSupportedButPinNotSet:
-        return CtapDeviceResponseCode::kCtap2ErrPinNotSet;
-      case AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported:
-        NOTREACHED();
-    }
-  }
+  return request.user_verification() !=
+             UserVerificationRequirement::kRequired ||
+         options.user_verification_availability ==
+             AuthenticatorSupportedOptions::UserVerificationAvailability::
+                 kSupportedAndConfigured;
+}
 
-  // 2. "If authenticator supports clientPin and pinAuth parameter is present
-  // and the pinProtocol is not supported, return CTAP2_ERR_PIN_AUTH_INVALID
-  // error."
-  if (supports_pin && pin_auth &&
-      (!pin_protocol || *pin_protocol != 1)) {
-    return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
-  }
+bool AreGetAssertionOptionsValid(const AuthenticatorSupportedOptions& options,
+                                 const CtapGetAssertionRequest& request) {
+  if (request.user_presence_required() && !options.user_presence_required)
+    return false;
 
-  // 3. "If authenticator is not protected by some form of user verification and
-  // platform has set "uv" or pinAuth to get the user verification, return
-  // CTAP2_ERR_INVALID_OPTION."
-  const bool can_do_uv =
-      options.user_verification_availability ==
-          AuthenticatorSupportedOptions::UserVerificationAvailability::
-              kSupportedAndConfigured ||
-      options.client_pin_availability ==
-          AuthenticatorSupportedOptions::ClientPinAvailability::
-              kSupportedAndPinSet;
-  if (!can_do_uv &&
-      (user_verification == UserVerificationRequirement::kRequired ||
-       pin_auth)) {
-    return CtapDeviceResponseCode::kCtap2ErrInvalidOption;
-  }
-
-  // Step 4.
-  bool uv = false;
-  if (can_do_uv) {
-    if (user_verification == UserVerificationRequirement::kRequired) {
-      // Internal UV is assumed to always succeed.
-      if (simulate_press_callback) {
-        simulate_press_callback.Run();
-      }
-      uv = true;
-    }
-
-    if (pin_auth) {
-      DCHECK(pin_protocol && *pin_protocol == 1);
-      // The pin_auth argument is assumed to be correct.
-      uv = true;
-    }
-
-    if (is_make_credential && !uv) {
-      return CtapDeviceResponseCode::kCtap2ErrPinRequired;
-    }
-  }
-
-  return CtapDeviceResponseCode::kSuccess;
+  return request.user_verification() !=
+             UserVerificationRequirement::kRequired ||
+         options.user_verification_availability ==
+             AuthenticatorSupportedOptions::UserVerificationAvailability::
+                 kSupportedAndConfigured;
 }
 
 // Checks that whether the received MakeCredential request includes EA256
@@ -333,43 +274,30 @@
   CtapMakeCredentialRequest request = std::get<0>(*request_and_hash);
   CtapMakeCredentialRequest::ClientDataHash client_data_hash =
       std::get<1>(*request_and_hash);
-  const AuthenticatorSupportedOptions& options = device_info_.options();
 
-  bool user_verified;
-  const CtapDeviceResponseCode uv_error = CheckUserVerification(
-      true /* is makeCredential */, options, request.pin_auth(),
-      request.pin_protocol(), request.user_verification(),
-      mutable_state()->simulate_press_callback, &user_verified);
-  if (uv_error != CtapDeviceResponseCode::kSuccess) {
-    return uv_error;
+  if (!AreMakeCredentialOptionsValid(device_info_.options(), request) ||
+      !AreMakeCredentialParamsValid(request)) {
+    DLOG(ERROR) << "Virtual CTAP2 device does not support options required by "
+                   "the request.";
+    return CtapDeviceResponseCode::kCtap2ErrOther;
   }
 
-  // 6. Check for already registered credentials.
+  // Client pin is not supported.
+  if (request.pin_auth()) {
+    DLOG(ERROR) << "Virtual CTAP2 device does not support client pin.";
+    return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
+  }
+
+  // Check for already registered credentials.
   const auto rp_id_hash =
       fido_parsing_utils::CreateSHA256Hash(request.rp().rp_id());
   if (request.exclude_list()) {
     for (const auto& excluded_credential : *request.exclude_list()) {
-      if (FindRegistrationData(excluded_credential.id(), rp_id_hash)) {
-        if (mutable_state()->simulate_press_callback) {
-          mutable_state()->simulate_press_callback.Run();
-        }
+      if (FindRegistrationData(excluded_credential.id(), rp_id_hash))
         return CtapDeviceResponseCode::kCtap2ErrCredentialExcluded;
-      }
     }
   }
 
-  // Step 7.
-  if (!AreMakeCredentialParamsValid(request)) {
-    DLOG(ERROR) << "Virtual CTAP2 device does not support options required by "
-                   "the request.";
-    return CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm;
-  }
-
-  // Step 8.
-  if (request.resident_key_required() && !options.supports_resident_key) {
-    return CtapDeviceResponseCode::kCtap2ErrUnsupportedOption;
-  }
-
   // Create key to register.
   // Note: Non-deterministic, you need to mock this out if you rely on
   // deterministic behavior.
@@ -403,7 +331,7 @@
   }
 
   auto authenticator_data = ConstructAuthenticatorData(
-      rp_id_hash, user_verified, 01ul, std::move(attested_credential_data),
+      rp_id_hash, 01ul, std::move(attested_credential_data),
       std::move(extensions));
   auto sign_buffer =
       ConstructSignatureBuffer(authenticator_data, client_data_hash);
@@ -445,16 +373,6 @@
   CtapGetAssertionRequest request = std::get<0>(*request_and_hash);
   CtapGetAssertionRequest::ClientDataHash client_data_hash =
       std::get<1>(*request_and_hash);
-  const AuthenticatorSupportedOptions& options = device_info_.options();
-
-  bool user_verified;
-  const CtapDeviceResponseCode uv_error = CheckUserVerification(
-      false /* not makeCredential */, options, request.pin_auth(),
-      request.pin_protocol(), request.user_verification(),
-      mutable_state()->simulate_press_callback, &user_verified);
-  if (uv_error != CtapDeviceResponseCode::kSuccess) {
-    return uv_error;
-  }
 
   // Resident keys are not supported.
   if (!request.allow_list() || request.allow_list()->empty()) {
@@ -463,6 +381,17 @@
     return CtapDeviceResponseCode::kCtap2ErrNoCredentials;
   }
 
+  // Client pin option is not supported.
+  if (request.pin_auth()) {
+    DLOG(ERROR) << "Virtual CTAP2 device does not support client pin.";
+    return CtapDeviceResponseCode::kCtap2ErrOther;
+  }
+
+  if (!AreGetAssertionOptionsValid(device_info_.options(), request)) {
+    DLOG(ERROR) << "Unsupported options required from the request.";
+    return CtapDeviceResponseCode::kCtap2ErrOther;
+  }
+
   const auto rp_id_hash = fido_parsing_utils::CreateSHA256Hash(request.rp_id());
 
   RegistrationData* found_data = nullptr;
@@ -480,8 +409,7 @@
 
   found_data->counter++;
   auto authenticator_data = ConstructAuthenticatorData(
-      rp_id_hash, user_verified, found_data->counter, base::nullopt,
-      base::nullopt);
+      rp_id_hash, found_data->counter, base::nullopt, base::nullopt);
   auto signature_buffer =
       ConstructSignatureBuffer(authenticator_data, client_data_hash);
 
@@ -504,16 +432,14 @@
 
 AuthenticatorData VirtualCtap2Device::ConstructAuthenticatorData(
     base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
-    bool user_verified,
     uint32_t current_signature_count,
     base::Optional<AttestedCredentialData> attested_credential_data,
     base::Optional<cbor::Value> extensions) {
   uint8_t flag =
       base::strict_cast<uint8_t>(AuthenticatorData::Flag::kTestOfUserPresence);
-  if (user_verified) {
-    flag |= base::strict_cast<uint8_t>(
-        AuthenticatorData::Flag::kTestOfUserVerification);
-  }
+  std::array<uint8_t, kSignCounterLength> signature_counter;
+
+  // Constructing AuthenticatorData for registration operation.
   if (attested_credential_data)
     flag |= base::strict_cast<uint8_t>(AuthenticatorData::Flag::kAttestation);
   if (extensions) {
@@ -521,7 +447,6 @@
         AuthenticatorData::Flag::kExtensionDataIncluded);
   }
 
-  std::array<uint8_t, kSignCounterLength> signature_counter;
   signature_counter[0] = (current_signature_count >> 24) & 0xff;
   signature_counter[1] = (current_signature_count >> 16) & 0xff;
   signature_counter[2] = (current_signature_count >> 8) & 0xff;
diff --git a/device/fido/virtual_ctap2_device.h b/device/fido/virtual_ctap2_device.h
index 9925fb5..fab7d8d 100644
--- a/device/fido/virtual_ctap2_device.h
+++ b/device/fido/virtual_ctap2_device.h
@@ -53,7 +53,6 @@
 
   AuthenticatorData ConstructAuthenticatorData(
       base::span<const uint8_t, kRpIdHashLength> rp_id_hash,
-      bool user_verified,
       uint32_t current_signature_count,
       base::Optional<AttestedCredentialData> attested_credential_data,
       base::Optional<cbor::Value> extensions);
diff --git a/device/fido/win/type_conversions.cc b/device/fido/win/type_conversions.cc
index 299ed74..20b94a76 100644
--- a/device/fido/win/type_conversions.cc
+++ b/device/fido/win/type_conversions.cc
@@ -216,7 +216,7 @@
           {L"ConstraintError",
            CtapDeviceResponseCode::kCtap2ErrUnsupportedOption},
           {L"NotSupportedError",
-           CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithm},
+           CtapDeviceResponseCode::kCtap2ErrUnsupportedAlgorithms},
           {L"NotAllowedError",
            CtapDeviceResponseCode::kCtap2ErrOperationDenied},
           {L"UnknownError", CtapDeviceResponseCode::kCtap2ErrOther},
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index f9e7896..f8258eef 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -145,6 +145,14 @@
     keys::kOnHeadersReceivedEvent,
 };
 
+// List of all webRequest events that support extraHeaders in the extraInfoSpec.
+const char* const kWebRequestExtraHeadersEventNames[] = {
+    keys::kOnBeforeSendHeadersEvent, keys::kOnSendHeadersEvent,
+    keys::kOnHeadersReceivedEvent,   keys::kOnAuthRequiredEvent,
+    keys::kOnResponseStartedEvent,   keys::kOnBeforeRedirectEvent,
+    keys::kOnCompletedEvent,
+};
+
 // User data key for WebRequestAPI::ProxySet.
 const void* const kWebRequestProxySetUserDataKey =
     &kWebRequestProxySetUserDataKey;
@@ -989,7 +997,8 @@
   }
   request_time_tracker_->LogRequestStartTime(
       request->id, base::TimeTicks::Now(), has_listener,
-      HasExtraHeadersListener(browser_context, extension_info_map, request));
+      HasExtraHeadersListenerForRequest(browser_context, extension_info_map,
+                                        request));
 
   const bool is_incognito_context = IsIncognitoBrowserContext(browser_context);
 
@@ -1731,18 +1740,12 @@
   callbacks_for_page_load_.push_back(callback);
 }
 
-bool ExtensionWebRequestEventRouter::HasExtraHeadersListener(
+bool ExtensionWebRequestEventRouter::HasExtraHeadersListenerForRequest(
     void* browser_context,
     const extensions::InfoMap* extension_info_map,
     const WebRequestInfo* request) {
-  static const char* kEventNames[] = {
-      keys::kOnBeforeSendHeadersEvent, keys::kOnSendHeadersEvent,
-      keys::kOnHeadersReceivedEvent,   keys::kOnAuthRequiredEvent,
-      keys::kOnResponseStartedEvent,   keys::kOnBeforeRedirectEvent,
-      keys::kOnCompletedEvent,
-  };
   int extra_info_spec = 0;
-  for (const char* name : kEventNames) {
+  for (const char* name : kWebRequestExtraHeadersEventNames) {
     GetMatchingListeners(browser_context, extension_info_map, name, request,
                          &extra_info_spec);
     if (extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS)
@@ -1751,6 +1754,30 @@
   return false;
 }
 
+bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListener(
+    void* browser_context) {
+  if (HasAnyExtraHeadersListenerImpl(browser_context))
+    return true;
+
+  void* cross_browser_context = GetCrossBrowserContext(browser_context);
+  if (cross_browser_context)
+    return HasAnyExtraHeadersListenerImpl(cross_browser_context);
+
+  return false;
+}
+
+bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListenerImpl(
+    void* browser_context) {
+  for (const char* name : kWebRequestExtraHeadersEventNames) {
+    const Listeners& listeners = listeners_[browser_context][name];
+    for (const auto& listener : listeners) {
+      if (listener->extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS)
+        return true;
+    }
+  }
+  return false;
+}
+
 bool ExtensionWebRequestEventRouter::IsPageLoad(
     const WebRequestInfo& request) const {
   return request.type == content::RESOURCE_TYPE_MAIN_FRAME;
diff --git a/extensions/browser/api/web_request/web_request_api.h b/extensions/browser/api/web_request/web_request_api.h
index 26f6563..1e525d5 100644
--- a/extensions/browser/api/web_request/web_request_api.h
+++ b/extensions/browser/api/web_request/web_request_api.h
@@ -480,9 +480,14 @@
 
   // Whether there is a listener matching the request that has
   // ExtraInfoSpec::EXTRA_HEADERS set.
-  bool HasExtraHeadersListener(void* browser_context,
-                               const extensions::InfoMap* extension_info_map,
-                               const WebRequestInfo* request);
+  bool HasExtraHeadersListenerForRequest(
+      void* browser_context,
+      const extensions::InfoMap* extension_info_map,
+      const WebRequestInfo* request);
+
+  // Whether there are any listeners for this context that have
+  // ExtraInfoSpec::EXTRA_HEADERS set.
+  bool HasAnyExtraHeadersListener(void* browser_context);
 
  private:
   friend class WebRequestAPI;
@@ -699,6 +704,9 @@
   // Returns true if |request| was already signaled to some event handlers.
   bool WasSignaled(const WebRequestInfo& request) const;
 
+  // Helper for |HasAnyExtraHeadersListener()|.
+  bool HasAnyExtraHeadersListenerImpl(void* browser_context);
+
   // Get the number of listeners - for testing only.
   size_t GetListenerCountForTesting(void* browser_context,
                                     const std::string& event_name);
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index 75ad640..8ee19dee 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -42,6 +42,11 @@
       proxied_loader_binding_(this, std::move(loader_request)),
       target_client_(std::move(client)),
       proxied_client_binding_(this),
+      has_any_extra_headers_listeners_(
+          network_service_request_id_ != 0 &&
+          ExtensionWebRequestEventRouter::GetInstance()
+              ->HasAnyExtraHeadersListener(factory_->browser_context_)),
+      header_client_binding_(this),
       weak_factory_(this) {
   // If there is a client error, clean up the request.
   target_client_.set_connection_error_handler(base::BindOnce(
@@ -78,17 +83,18 @@
       routing_id_, factory_->resource_context_, request_,
       !(options_ & network::mojom::kURLLoadOptionSynchronous));
 
-  uses_header_client_ =
-      factory_->header_client_binding_ && request_.url.SchemeIsHTTPOrHTTPS() &&
-      network_service_request_id_ != 0 &&
-      ExtensionWebRequestEventRouter::GetInstance()->HasExtraHeadersListener(
-          factory_->browser_context_, factory_->info_map_, &info_.value());
+  current_request_uses_header_client_ =
+      factory_->url_loader_header_client_binding_ &&
+      request_.url.SchemeIsHTTPOrHTTPS() && network_service_request_id_ != 0 &&
+      ExtensionWebRequestEventRouter::GetInstance()
+          ->HasExtraHeadersListenerForRequest(
+              factory_->browser_context_, factory_->info_map_, &info_.value());
 
   // If the header client will be used, we start the request immediately, and
   // OnBeforeSendHeaders and OnSendHeaders will be handled there. Otherwise,
   // send these events before the request starts.
   base::RepeatingCallback<void(int)> continuation;
-  if (uses_header_client_) {
+  if (current_request_uses_header_client_) {
     continuation = base::BindRepeating(
         &InProgressRequest::ContinueToStartRequest, weak_factory_.GetWeakPtr());
   } else {
@@ -121,6 +127,11 @@
     // We pause the binding here to prevent further client message processing.
     if (proxied_client_binding_.is_bound())
       proxied_client_binding_.PauseIncomingMethodCallProcessing();
+
+    // Pause the header client, since we want to wait until OnBeforeRequest has
+    // finished before processing any future events.
+    if (header_client_binding_)
+      header_client_binding_.PauseIncomingMethodCallProcessing();
     return;
   }
   DCHECK_EQ(net::OK, result);
@@ -175,7 +186,7 @@
     const network::ResourceResponseHead& head) {
   current_response_ = head;
   on_receive_response_received_ = true;
-  if (uses_header_client_) {
+  if (current_request_uses_header_client_) {
     ContinueToResponseStarted(net::OK);
   } else {
     HandleResponseOrRedirectHeaders(
@@ -195,7 +206,7 @@
   }
 
   current_response_ = head;
-  if (uses_header_client_) {
+  if (current_request_uses_header_client_) {
     ContinueToBeforeRedirect(redirect_info, net::OK);
   } else {
     HandleResponseOrRedirectHeaders(
@@ -266,10 +277,19 @@
       base::RetainedRef(auth_info), base::Passed(&callback))));
 }
 
+void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnLoaderCreated(
+    network::mojom::TrustedHeaderClientRequest request) {
+  header_client_binding_.Bind(std::move(request));
+}
+
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnBeforeSendHeaders(
     const net::HttpRequestHeaders& headers,
     OnBeforeSendHeadersCallback callback) {
-  DCHECK(uses_header_client_);
+  if (!current_request_uses_header_client_) {
+    std::move(callback).Run(net::OK, base::nullopt);
+    return;
+  }
+
   request_.headers = headers;
   on_before_send_headers_callback_ = std::move(callback);
   ContinueToBeforeSendHeaders(net::OK);
@@ -278,7 +298,11 @@
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnHeadersReceived(
     const std::string& headers,
     OnHeadersReceivedCallback callback) {
-  DCHECK(uses_header_client_);
+  if (!current_request_uses_header_client_) {
+    std::move(callback).Run(net::OK, base::nullopt, GURL());
+    return;
+  }
+
   on_headers_received_callback_ = std::move(callback);
   current_response_.headers =
       base::MakeRefCounted<net::HttpResponseHeaders>(headers);
@@ -299,6 +323,7 @@
   // the load with it gives unexpected results. See
   // https://crbug.com/882661#c72.
   proxied_client_binding_.Close();
+  header_client_binding_.Close();
   target_loader_.reset();
 
   constexpr int kInternalRedirectStatusCode = 307;
@@ -344,7 +369,7 @@
     return;
   }
 
-  if (!uses_header_client_ && !redirect_url_.is_empty()) {
+  if (!current_request_uses_header_client_ && !redirect_url_.is_empty()) {
     HandleBeforeRequestRedirect();
     return;
   }
@@ -394,7 +419,7 @@
     return;
   }
 
-  if (uses_header_client_ && !redirect_url_.is_empty()) {
+  if (current_request_uses_header_client_ && !redirect_url_.is_empty()) {
     HandleBeforeRequestRedirect();
     return;
   }
@@ -402,13 +427,18 @@
   if (proxied_client_binding_.is_bound())
     proxied_client_binding_.ResumeIncomingMethodCallProcessing();
 
+  if (header_client_binding_)
+    header_client_binding_.ResumeIncomingMethodCallProcessing();
+
   if (!target_loader_.is_bound() && factory_->target_factory_.is_bound()) {
     // No extensions have cancelled us up to this point, so it's now OK to
     // initiate the real network request.
     network::mojom::URLLoaderClientPtr proxied_client;
     proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client));
     uint32_t options = options_;
-    if (uses_header_client_)
+    // Even if this request does not use the header client, future redirects
+    // might, so we need to set the option on the loader.
+    if (has_any_extra_headers_listeners_)
       options |= network::mojom::kURLLoadOptionUseHeaderClient;
     factory_->target_factory_->CreateLoaderAndStart(
         mojo::MakeRequest(&target_loader_), info_->routing_id,
@@ -428,7 +458,7 @@
     return;
   }
 
-  if (uses_header_client_) {
+  if (current_request_uses_header_client_) {
     DCHECK(on_before_send_headers_callback_);
     std::move(on_before_send_headers_callback_)
         .Run(error_code, request_.headers);
@@ -446,7 +476,7 @@
         request_.headers);
   }
 
-  if (!uses_header_client_)
+  if (!current_request_uses_header_client_)
     ContinueToStartRequest(net::OK);
 }
 
@@ -542,7 +572,7 @@
     return;
   }
 
-  DCHECK(!uses_header_client_ || !override_headers_);
+  DCHECK(!current_request_uses_header_client_ || !override_headers_);
   if (override_headers_)
     current_response_.headers = override_headers_;
 
@@ -567,6 +597,7 @@
     // These will get re-bound if a new request is initiated by
     // |FollowRedirect()|.
     proxied_client_binding_.Close();
+    header_client_binding_.Close();
     target_loader_.reset();
     on_receive_response_received_ = false;
 
@@ -691,7 +722,7 @@
       request_id_generator_(std::move(request_id_generator)),
       navigation_ui_data_(std::move(navigation_ui_data)),
       info_map_(info_map),
-      header_client_binding_(this),
+      url_loader_header_client_binding_(this),
       proxies_(proxies),
       weak_factory_(this) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
@@ -705,7 +736,7 @@
       base::Unretained(this)));
 
   if (header_client_request)
-    header_client_binding_.Bind(std::move(header_client_request));
+    url_loader_header_client_binding_.Bind(std::move(header_client_request));
 }
 
 void WebRequestProxyingURLLoaderFactory::StartProxying(
@@ -774,34 +805,16 @@
   proxy_bindings_.AddBinding(this, std::move(loader_request));
 }
 
-void WebRequestProxyingURLLoaderFactory::OnBeforeSendHeaders(
+void WebRequestProxyingURLLoaderFactory::OnLoaderCreated(
     int32_t request_id,
-    const net::HttpRequestHeaders& headers,
-    OnBeforeSendHeadersCallback callback) {
+    network::mojom::TrustedHeaderClientRequest request) {
   auto it = network_request_id_to_web_request_id_.find(request_id);
-  if (it == network_request_id_to_web_request_id_.end()) {
-    std::move(callback).Run(net::OK, base::nullopt);
+  if (it == network_request_id_to_web_request_id_.end())
     return;
-  }
 
   auto request_it = requests_.find(it->second);
   DCHECK(request_it != requests_.end());
-  request_it->second->OnBeforeSendHeaders(headers, std::move(callback));
-}
-
-void WebRequestProxyingURLLoaderFactory::OnHeadersReceived(
-    int32_t request_id,
-    const std::string& headers,
-    OnHeadersReceivedCallback callback) {
-  auto it = network_request_id_to_web_request_id_.find(request_id);
-  if (it == network_request_id_to_web_request_id_.end()) {
-    std::move(callback).Run(net::OK, base::nullopt, GURL());
-    return;
-  }
-
-  auto request_it = requests_.find(it->second);
-  DCHECK(request_it != requests_.end());
-  request_it->second->OnHeadersReceived(headers, std::move(callback));
+  request_it->second->OnLoaderCreated(std::move(request));
 }
 
 void WebRequestProxyingURLLoaderFactory::HandleAuthRequest(
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
index 1ca71ad..b0ac89d 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -48,7 +48,8 @@
       public network::mojom::TrustedURLLoaderHeaderClient {
  public:
   class InProgressRequest : public network::mojom::URLLoader,
-                            public network::mojom::URLLoaderClient {
+                            public network::mojom::URLLoaderClient,
+                            public network::mojom::TrustedHeaderClient {
    public:
     InProgressRequest(
         WebRequestProxyingURLLoaderFactory* factory,
@@ -92,10 +93,13 @@
         scoped_refptr<net::HttpResponseHeaders> response_headers,
         WebRequestAPI::AuthRequestCallback callback);
 
+    void OnLoaderCreated(network::mojom::TrustedHeaderClientRequest request);
+
+    // network::mojom::TrustedHeaderClient:
     void OnBeforeSendHeaders(const net::HttpRequestHeaders& headers,
-                             OnBeforeSendHeadersCallback callback);
+                             OnBeforeSendHeadersCallback callback) override;
     void OnHeadersReceived(const std::string& headers,
-                           OnHeadersReceivedCallback callback);
+                           OnHeadersReceivedCallback callback) override;
 
    private:
     void ContinueToBeforeSendHeaders(int error_code);
@@ -153,14 +157,17 @@
 
     bool request_completed_ = false;
 
-    // If |uses_header_client_| is set to true, the request will be sent with
-    // the network::mojom::kURLLoadOptionUseHeaderClient option, and we expect
-    // events to come through the network::mojom::TrustedURLLoaderHeaderClient
-    // binding on the factory. This is only set to true if there is a listener
-    // that needs to view or modify headers set in the network process.
-    bool uses_header_client_ = false;
+    // If |has_any_extra_headers_listeners_| is set to true, the request will be
+    // sent with the network::mojom::kURLLoadOptionUseHeaderClient option, and
+    // we expect events to come through the
+    // network::mojom::TrustedURLLoaderHeaderClient binding on the factory. This
+    // is only set to true if there is a listener that needs to view or modify
+    // headers set in the network process.
+    bool has_any_extra_headers_listeners_ = false;
+    bool current_request_uses_header_client_ = false;
     OnBeforeSendHeadersCallback on_before_send_headers_callback_;
     OnHeadersReceivedCallback on_headers_received_callback_;
+    mojo::Binding<network::mojom::TrustedHeaderClient> header_client_binding_;
 
     base::WeakPtrFactory<InProgressRequest> weak_factory_;
 
@@ -205,12 +212,9 @@
   void Clone(network::mojom::URLLoaderFactoryRequest loader_request) override;
 
   // network::mojom::TrustedURLLoaderHeaderClient:
-  void OnBeforeSendHeaders(int32_t request_id,
-                           const net::HttpRequestHeaders& headers,
-                           OnBeforeSendHeadersCallback callback) override;
-  void OnHeadersReceived(int32_t request_id,
-                         const std::string& headers,
-                         OnHeadersReceivedCallback callback) override;
+  void OnLoaderCreated(
+      int32_t request_id,
+      network::mojom::TrustedHeaderClientRequest request) override;
 
   // WebRequestAPI::Proxy:
   void HandleAuthRequest(
@@ -234,7 +238,7 @@
   mojo::BindingSet<network::mojom::URLLoaderFactory> proxy_bindings_;
   network::mojom::URLLoaderFactoryPtr target_factory_;
   mojo::Binding<network::mojom::TrustedURLLoaderHeaderClient>
-      header_client_binding_;
+      url_loader_header_client_binding_;
   // Owns |this|.
   WebRequestAPI::ProxySet* const proxies_;
 
diff --git a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
index ebf9665..a00b054 100644
--- a/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
+++ b/extensions/browser/guest_view/extensions_guest_view_message_filter.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/guid.h"
-#include "base/no_destructor.h"
 #include "base/stl_util.h"
 #include "base/task/post_task.h"
 #include "base/time/time.h"
diff --git a/extensions/browser/guest_view/extensions_guest_view_message_filter.h b/extensions/browser/guest_view/extensions_guest_view_message_filter.h
index 836e42f..6ae1665 100644
--- a/extensions/browser/guest_view/extensions_guest_view_message_filter.h
+++ b/extensions/browser/guest_view/extensions_guest_view_message_filter.h
@@ -44,10 +44,8 @@
                                    content::BrowserContext* context);
 
  private:
-  class FrameNavigationHelper;
   friend class content::BrowserThread;
   friend class base::DeleteHelper<ExtensionsGuestViewMessageFilter>;
-  friend class ExtensionsGuestViewMessageFilter::FrameNavigationHelper;
 
   ~ExtensionsGuestViewMessageFilter() override;
 
diff --git a/google_apis/google_api_keys.cc b/google_apis/google_api_keys.cc
index d05b9ef..643926f 100644
--- a/google_apis/google_api_keys.cc
+++ b/google_apis/google_api_keys.cc
@@ -73,6 +73,10 @@
 #define GOOGLE_API_KEY_PHYSICAL_WEB_TEST DUMMY_API_TOKEN
 #endif
 
+#if !defined(GOOGLE_API_KEY_REMOTING_FTL)
+#define GOOGLE_API_KEY_REMOTING_FTL DUMMY_API_TOKEN
+#endif
+
 // These are used as shortcuts for developers and users providing
 // OAuth credentials via preprocessor defines or environment
 // variables.  If set, they will be used to replace any of the client
@@ -111,6 +115,11 @@
     api_key_non_stable_ = api_key_;
 #endif
 
+    api_key_remoting_ftl_ =
+        CalculateKeyValue(GOOGLE_API_KEY_REMOTING_FTL,
+                          STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_REMOTING_FTL),
+                          NULL, std::string(), environment.get(), command_line);
+
     std::string default_client_id =
         CalculateKeyValue(GOOGLE_DEFAULT_CLIENT_ID,
                           STRINGIZE_NO_EXPANSION(GOOGLE_DEFAULT_CLIENT_ID),
@@ -198,6 +207,7 @@
   void set_api_key(const std::string& api_key) { api_key_ = api_key; }
 #endif
   std::string api_key_non_stable() const { return api_key_non_stable_; }
+  std::string api_key_remoting_ftl() const { return api_key_remoting_ftl_; }
 
   std::string GetClientID(OAuth2Client client) const {
     DCHECK_LT(client, CLIENT_NUM_ITEMS);
@@ -293,6 +303,7 @@
 
   std::string api_key_;
   std::string api_key_non_stable_;
+  std::string api_key_remoting_ftl_;
   std::string client_ids_[CLIENT_NUM_ITEMS];
   std::string client_secrets_[CLIENT_NUM_ITEMS];
 };
@@ -312,6 +323,10 @@
   return g_api_key_cache.Get().api_key_non_stable();
 }
 
+std::string GetFtlAPIKey() {
+  return g_api_key_cache.Get().api_key_remoting_ftl();
+}
+
 #if defined(OS_IOS)
 void SetAPIKey(const std::string& api_key) {
   g_api_key_cache.Get().set_api_key(api_key);
diff --git a/google_apis/google_api_keys.h b/google_apis/google_api_keys.h
index d4979fe9..6f13643d 100644
--- a/google_apis/google_api_keys.h
+++ b/google_apis/google_api_keys.h
@@ -73,6 +73,10 @@
 // Non-stable channels may have a different Google API key.
 std::string GetNonStableAPIKey();
 
+// Retrieves the Chrome Remote Desktop FTL API key to be used during the
+// signaling process.
+std::string GetRemotingFtlAPIKey();
+
 #if defined(OS_IOS)
 // Sets the API key. This should be called as early as possible before this
 // API key is even accessed.
diff --git a/gpu/command_buffer/client/raster_implementation_gles_unittest.cc b/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
index 2a6964a0..324fc46 100644
--- a/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
+++ b/gpu/command_buffer/client/raster_implementation_gles_unittest.cc
@@ -233,9 +233,8 @@
 class ImageProviderStub : public cc::ImageProvider {
  public:
   ~ImageProviderStub() override {}
-  ScopedDecodedDrawImage GetDecodedDrawImage(
-      const cc::DrawImage& draw_image) override {
-    return ScopedDecodedDrawImage();
+  ScopedResult GetRasterContent(const cc::DrawImage& draw_image) override {
+    return ScopedResult();
   }
 };
 
diff --git a/ios/chrome/browser/autofill/BUILD.gn b/ios/chrome/browser/autofill/BUILD.gn
index 7b6bbd9..480b838 100644
--- a/ios/chrome/browser/autofill/BUILD.gn
+++ b/ios/chrome/browser/autofill/BUILD.gn
@@ -326,6 +326,9 @@
     "//components/test/data/autofill/heuristics/input/149_checkout_qvc.com_non_hidden.html",
     "//components/test/data/autofill/heuristics/input/150_checkout_venus.com_search_field.html",
     "//components/test/data/autofill/heuristics/input/151_ticketmaster.com.html",
+    "//components/test/data/autofill/heuristics/input/153_fmm-en_inm.gob.mx.html",
+    "//components/test/data/autofill/heuristics/input/154_fmm-es_inm.gob.mx.html",
+    "//components/test/data/autofill/heuristics/input/155_fmm-ja_inm.gob.mx.html",
     "//components/test/data/autofill/heuristics/output/000_i18n_de.out",
     "//components/test/data/autofill/heuristics/output/001_i18n_de2.out",
     "//components/test/data/autofill/heuristics/output/002_i18n_en.out",
@@ -478,6 +481,9 @@
     "//components/test/data/autofill/heuristics/output/149_checkout_qvc.com_non_hidden.out",
     "//components/test/data/autofill/heuristics/output/150_checkout_venus.com_search_field.out",
     "//components/test/data/autofill/heuristics/output/151_ticketmaster.com.out",
+    "//components/test/data/autofill/heuristics/output/153_fmm-en_inm.gob.mx.out",
+    "//components/test/data/autofill/heuristics/output/154_fmm-es_inm.gob.mx.out",
+    "//components/test/data/autofill/heuristics/output/155_fmm-ja_inm.gob.mx.out",
   ]
   outputs = [
     "{{bundle_resources_dir}}/" +
diff --git a/ios/chrome/browser/signin/authentication_service_unittest.mm b/ios/chrome/browser/signin/authentication_service_unittest.mm
index 68403bf..43c7f10 100644
--- a/ios/chrome/browser/signin/authentication_service_unittest.mm
+++ b/ios/chrome/browser/signin/authentication_service_unittest.mm
@@ -123,8 +123,12 @@
                               base::BindRepeating(&BuildMockSyncSetupService));
     builder.SetPrefService(CreatePrefService());
 
+    // This test requires usage of the production iOS TokenService delegate,
+    // as the production AuthenticationService class assumes the presence of
+    // this delegate.
     browser_state_ = IdentityTestEnvironmentChromeBrowserStateAdaptor::
-        CreateChromeBrowserStateForIdentityTestEnvironment(builder);
+        CreateChromeBrowserStateForIdentityTestEnvironment(
+            builder, /*use_ios_token_service_delegate=*/true);
 
     identity_test_environment_adaptor_ =
         std::make_unique<IdentityTestEnvironmentChromeBrowserStateAdaptor>(
diff --git a/ios/chrome/browser/signin/identity_manager_factory.cc b/ios/chrome/browser/signin/identity_manager_factory.cc
index 8351b2ee..ce9696c 100644
--- a/ios/chrome/browser/signin/identity_manager_factory.cc
+++ b/ios/chrome/browser/signin/identity_manager_factory.cc
@@ -15,6 +15,7 @@
 #include "ios/chrome/browser/signin/identity_manager_factory_observer.h"
 #include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator_impl.h"
 #include "services/identity/public/cpp/accounts_mutator.h"
 #include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/primary_account_mutator_impl.h"
@@ -41,7 +42,10 @@
                 ios::AccountTrackerServiceFactory::GetForBrowserState(
                     browser_state),
                 ios::SigninManagerFactory::GetForBrowserState(browser_state)),
-            nullptr) {}
+            nullptr,
+            std::make_unique<identity::AccountsCookieMutatorImpl>(
+                ios::GaiaCookieManagerServiceFactory::GetForBrowserState(
+                    browser_state))) {}
 };
 
 IdentityManagerFactory::IdentityManagerFactory()
diff --git a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
index 56e53da0..0848537 100644
--- a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
+++ b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
@@ -4,7 +4,6 @@
 
 #include "ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h"
 
-#include <memory>
 #include <utility>
 
 #include "base/bind.h"
@@ -42,6 +41,14 @@
     web::BrowserState* context) {
   ios::ChromeBrowserState* browser_state =
       ios::ChromeBrowserState::FromBrowserState(context);
+  return std::make_unique<FakeProfileOAuth2TokenService>(
+      browser_state->GetPrefs());
+}
+
+std::unique_ptr<KeyedService> BuildFakeOAuth2TokenServiceWithIOSDelegate(
+    web::BrowserState* context) {
+  ios::ChromeBrowserState* browser_state =
+      ios::ChromeBrowserState::FromBrowserState(context);
   std::unique_ptr<OAuth2TokenServiceDelegate> delegate =
       std::make_unique<ProfileOAuth2TokenServiceIOSDelegate>(
           SigninClientFactory::GetForBrowserState(browser_state),
@@ -64,11 +71,14 @@
   return account_fetcher_service;
 }
 
-TestChromeBrowserState::TestingFactories GetIdentityTestEnvironmentFactories() {
+TestChromeBrowserState::TestingFactories GetIdentityTestEnvironmentFactories(
+    bool use_ios_token_service_delegate) {
   return {{ios::AccountFetcherServiceFactory::GetInstance(),
            base::BindRepeating(&BuildFakeAccountFetcherService)},
           {ProfileOAuth2TokenServiceFactory::GetInstance(),
-           base::BindRepeating(&BuildFakeOAuth2TokenService)},
+           base::BindRepeating(use_ios_token_service_delegate
+                                   ? &BuildFakeOAuth2TokenServiceWithIOSDelegate
+                                   : &BuildFakeOAuth2TokenService)},
           {ios::SigninManagerFactory::GetInstance(),
            base::BindRepeating(&BuildFakeSigninManager)}};
 }
@@ -101,8 +111,10 @@
 std::unique_ptr<TestChromeBrowserState>
 IdentityTestEnvironmentChromeBrowserStateAdaptor::
     CreateChromeBrowserStateForIdentityTestEnvironment(
-        TestChromeBrowserState::Builder& builder) {
-  for (auto& identity_factory : GetIdentityTestEnvironmentFactories()) {
+        TestChromeBrowserState::Builder& builder,
+        bool use_ios_token_service_delegate) {
+  for (auto& identity_factory :
+       GetIdentityTestEnvironmentFactories(use_ios_token_service_delegate)) {
     builder.AddTestingFactory(identity_factory.first, identity_factory.second);
   }
 
@@ -113,7 +125,8 @@
 void IdentityTestEnvironmentChromeBrowserStateAdaptor::
     SetIdentityTestEnvironmentFactoriesOnBrowserContext(
         TestChromeBrowserState* browser_state) {
-  for (const auto& factory_pair : GetIdentityTestEnvironmentFactories()) {
+  for (const auto& factory_pair : GetIdentityTestEnvironmentFactories(
+           /*use_ios_token_service_delegate=*/false)) {
     factory_pair.first->SetTestingFactory(browser_state, factory_pair.second);
   }
 }
@@ -123,7 +136,8 @@
     AppendIdentityTestEnvironmentFactories(
         TestChromeBrowserState::TestingFactories* factories_to_append_to) {
   TestChromeBrowserState::TestingFactories identity_factories =
-      GetIdentityTestEnvironmentFactories();
+      GetIdentityTestEnvironmentFactories(
+          /*use_ios_token_service_delegate=*/false);
   factories_to_append_to->insert(factories_to_append_to->end(),
                                  identity_factories.begin(),
                                  identity_factories.end());
diff --git a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h
index 0a7d5d2..e8446457 100644
--- a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h
+++ b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h
@@ -5,6 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_SIGNIN_IDENTITY_TEST_ENVIRONMENT_CHROME_BROWSER_STATE_ADAPTOR_H_
 #define IOS_CHROME_BROWSER_SIGNIN_IDENTITY_TEST_ENVIRONMENT_CHROME_BROWSER_STATE_ADAPTOR_H_
 
+#include <memory>
+
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
 
@@ -33,12 +35,15 @@
   CreateChromeBrowserStateForIdentityTestEnvironment(
       const TestChromeBrowserState::TestingFactories& input_factories);
 
-  // Creates and returns a TestChromeBrowserState that has been configured with
-  // the given |builder|.
+  // Creates and returns a TestChromeBrowserState that has been configured
+  // with the given |builder|. Optionally, use a
+  // ProfileOAuth2TokenServiceIOSDelegate to build the
+  // FakeProfileOAuth2TokenService.
   // See the above variant for comments on common parameters.
   static std::unique_ptr<TestChromeBrowserState>
   CreateChromeBrowserStateForIdentityTestEnvironment(
-      TestChromeBrowserState::Builder& builder);
+      TestChromeBrowserState::Builder& builder,
+      bool use_ios_token_service_delegate = false);
 
   // Sets the testing factories that identity::IdentityTestEnvironment
   // requires explicitly on a Profile that is passed to it.
diff --git a/ios/chrome/browser/signin/signin_browser_state_info_updater.h b/ios/chrome/browser/signin/signin_browser_state_info_updater.h
index c05690a..af34bed 100644
--- a/ios/chrome/browser/signin/signin_browser_state_info_updater.h
+++ b/ios/chrome/browser/signin/signin_browser_state_info_updater.h
@@ -37,7 +37,8 @@
   void OnErrorChanged() override;
 
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 
diff --git a/ios/chrome/browser/signin/signin_browser_state_info_updater.mm b/ios/chrome/browser/signin/signin_browser_state_info_updater.mm
index efca8f2..8149987c 100644
--- a/ios/chrome/browser/signin/signin_browser_state_info_updater.mm
+++ b/ios/chrome/browser/signin/signin_browser_state_info_updater.mm
@@ -80,7 +80,7 @@
 }
 
 void SigninBrowserStateInfoUpdater::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   UpdateBrowserStateInfo();
 }
 
diff --git a/ios/chrome/browser/signin/signin_browser_state_info_updater_unittest.mm b/ios/chrome/browser/signin/signin_browser_state_info_updater_unittest.mm
index 5d358335..80ffb48e 100644
--- a/ios/chrome/browser/signin/signin_browser_state_info_updater_unittest.mm
+++ b/ios/chrome/browser/signin/signin_browser_state_info_updater_unittest.mm
@@ -9,46 +9,30 @@
 #endif
 
 #include <memory>
+#include <string>
 
-#include "base/bind.h"
 #include "base/files/file_path.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
-#include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
-#include "components/signin/core/browser/signin_manager.h"
-#include "components/signin/core/browser/test_signin_client.h"
-#include "components/signin/ios/browser/profile_oauth2_token_service_ios_delegate.h"
-#include "components/sync_preferences/pref_service_syncable.h"
+#include "components/signin/core/browser/account_info.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/browser_state_info_cache.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state_manager.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state_manager.h"
-#include "ios/chrome/browser/signin/account_tracker_service_factory.h"
-#include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "ios/chrome/browser/signin/signin_client_factory.h"
-#include "ios/chrome/browser/signin/signin_manager_factory.h"
+#include "ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.h"
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
 namespace {
-// Returns a TestSigninClient.
-std::unique_ptr<KeyedService> BuildTestSigninClient(web::BrowserState* state) {
-  return std::make_unique<TestSigninClient>(
-      ios::ChromeBrowserState::FromBrowserState(state)->GetPrefs());
-}
 
-// Builds a fake token service.
-std::unique_ptr<KeyedService> BuildTestTokenService(web::BrowserState* state) {
-  ios::ChromeBrowserState* browser_state =
-      ios::ChromeBrowserState::FromBrowserState(state);
-  return std::make_unique<FakeProfileOAuth2TokenService>(
-      browser_state->GetPrefs());
-}
+const char kEmail[] = "example@email.com";
+
 }  // namespace
 
 class SigninBrowserStateInfoUpdaterTest : public PlatformTest {
@@ -69,24 +53,30 @@
     // Create the browser state.
     TestChromeBrowserState::Builder test_cbs_builder;
     test_cbs_builder.SetPath(temp_directory_.GetPath());
-    test_cbs_builder.AddTestingFactory(
-        SigninClientFactory::GetInstance(),
-        base::BindRepeating(&BuildTestSigninClient));
-    test_cbs_builder.AddTestingFactory(
-        ProfileOAuth2TokenServiceFactory::GetInstance(),
-        base::BindRepeating(&BuildTestTokenService));
-    chrome_browser_state_ = test_cbs_builder.Build();
+    chrome_browser_state_ = IdentityTestEnvironmentChromeBrowserStateAdaptor::
+        CreateChromeBrowserStateForIdentityTestEnvironment(test_cbs_builder);
+
+    identity_test_environment_chrome_browser_state_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentChromeBrowserStateAdaptor>(
+            chrome_browser_state_.get());
 
     cache_index_ = browser_state_info_->GetIndexOfBrowserStateWithPath(
         chrome_browser_state_->GetStatePath());
   }
 
+  identity::IdentityTestEnvironment* identity_test_env() {
+    return identity_test_environment_chrome_browser_state_adaptor_
+        ->identity_test_env();
+  }
+
   web::TestWebThreadBundle thread_bundle_;
   base::ScopedTempDir temp_directory_;
   std::unique_ptr<IOSChromeScopedTestingChromeBrowserStateManager>
       scoped_browser_state_manager_;
   size_t cache_index_ = 0u;
   std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
+  std::unique_ptr<IdentityTestEnvironmentChromeBrowserStateAdaptor>
+      identity_test_environment_chrome_browser_state_adaptor_;
 
   // Weak, owned by scoped_browser_state_manager_.
   BrowserStateInfoCache* browser_state_info_ = nullptr;
@@ -98,26 +88,20 @@
       browser_state_info_->BrowserStateIsAuthenticatedAtIndex(cache_index_));
 
   // Signin.
-  AccountTrackerService* account_tracker =
-      ios::AccountTrackerServiceFactory::GetForBrowserState(
-          chrome_browser_state_.get());
-  SigninManager* signin_manager = ios::SigninManagerFactory::GetForBrowserState(
-      chrome_browser_state_.get());
-  std::string account_id =
-      account_tracker->SeedAccountInfo("gaia", "example@email.com");
-  signin_manager->OnExternalSigninCompleted("example@email.com");
+  AccountInfo account_info =
+      identity_test_env()->MakePrimaryAccountAvailable(kEmail);
+
   EXPECT_TRUE(
       browser_state_info_->BrowserStateIsAuthenticatedAtIndex(cache_index_));
-  EXPECT_EQ("gaia",
+  EXPECT_EQ(account_info.gaia,
             browser_state_info_->GetGAIAIdOfBrowserStateAtIndex(cache_index_));
   EXPECT_EQ(
-      "example@email.com",
+      kEmail,
       base::UTF16ToUTF8(
           browser_state_info_->GetUserNameOfBrowserStateAtIndex(cache_index_)));
 
   // Signout.
-  signin_manager->SignOut(signin_metrics::SIGNOUT_TEST,
-                          signin_metrics::SignoutDelete::IGNORE_METRIC);
+  identity_test_env()->ClearPrimaryAccount();
   EXPECT_FALSE(
       browser_state_info_->BrowserStateIsAuthenticatedAtIndex(cache_index_));
 }
@@ -128,19 +112,8 @@
       browser_state_info_->BrowserStateIsAuthenticatedAtIndex(cache_index_));
 
   // Signin.
-  AccountTrackerService* account_tracker =
-      ios::AccountTrackerServiceFactory::GetForBrowserState(
-          chrome_browser_state_.get());
-  SigninManager* signin_manager = ios::SigninManagerFactory::GetForBrowserState(
-      chrome_browser_state_.get());
-  FakeProfileOAuth2TokenService* token_service =
-      static_cast<FakeProfileOAuth2TokenService*>(
-          ProfileOAuth2TokenServiceFactory::GetForBrowserState(
-              chrome_browser_state_.get()));
-  std::string account_id =
-      account_tracker->SeedAccountInfo("gaia", "example@email.com");
-  token_service->UpdateCredentials(account_id, "token");
-  signin_manager->OnExternalSigninCompleted("example@email.com");
+  AccountInfo account_info =
+      identity_test_env()->MakePrimaryAccountAvailable(kEmail);
 
   EXPECT_TRUE(
       browser_state_info_->BrowserStateIsAuthenticatedAtIndex(cache_index_));
@@ -148,15 +121,15 @@
       browser_state_info_->BrowserStateIsAuthErrorAtIndex(cache_index_));
 
   // Set auth error.
-  token_service->UpdateAuthErrorForTesting(
-      account_id,
+  identity_test_env()->UpdatePersistentErrorOfRefreshTokenForAccount(
+      account_info.account_id,
       GoogleServiceAuthError(GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
   EXPECT_TRUE(
       browser_state_info_->BrowserStateIsAuthErrorAtIndex(cache_index_));
 
   // Remove auth error.
-  token_service->UpdateAuthErrorForTesting(
-      account_id, GoogleServiceAuthError::AuthErrorNone());
+  identity_test_env()->UpdatePersistentErrorOfRefreshTokenForAccount(
+      account_info.account_id, GoogleServiceAuthError::AuthErrorNone());
   EXPECT_FALSE(
       browser_state_info_->BrowserStateIsAuthErrorAtIndex(cache_index_));
 }
diff --git a/ios/chrome/browser/translate/OWNERS b/ios/chrome/browser/translate/OWNERS
index 51f79e89..8f9a794 100644
--- a/ios/chrome/browser/translate/OWNERS
+++ b/ios/chrome/browser/translate/OWNERS
@@ -1,5 +1,5 @@
 file://components/translate/OWNERS
-droger@chromium.org
+mahmadi@chromium.org
 
 # TEAM: ios-directory-owners@chromium.org
 # OS: iOS
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
index 79bc9e38..0873e3f 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
@@ -102,7 +102,7 @@
 #pragma mark - IdentityManagerObserverBridgeDelegate
 
 // Called when a user signs into Google services such as sync.
-- (void)onPrimaryAccountSet:(const AccountInfo&)primaryAccountInfo {
+- (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo {
   if (!self.signinPromoViewMediator.isSigninInProgress)
     self.shouldShowSigninPromo = NO;
 }
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index 40c5a6d..960e915 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -361,10 +361,13 @@
                                           : variations::InIncognito::kNo,
       &resource_request);
   NSMutableDictionary* result = [NSMutableDictionary dictionary];
-  if (!resource_request.client_data_header.empty()) {
-    NSString* name = base::SysUTF8ToNSString("X-Client-Data");
-    NSString* value =
-        base::SysUTF8ToNSString(resource_request.client_data_header);
+  // The variations header appears in cors_exempt_headers rather than in
+  // headers.
+  net::HttpRequestHeaders::Iterator header_iterator(
+      resource_request.cors_exempt_headers);
+  while (header_iterator.GetNext()) {
+    NSString* name = base::SysUTF8ToNSString(header_iterator.name());
+    NSString* value = base::SysUTF8ToNSString(header_iterator.value());
     result[name] = value;
   }
   return [result copy];
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
index 671d156..7649b6d 100644
--- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
+++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_mediator.mm
@@ -620,7 +620,7 @@
 }
 #pragma mark - IdentityManagerObserverBridgeDelegate
 
-- (void)onPrimaryAccountSet:(const AccountInfo&)primaryAccountInfo {
+- (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo {
   [self updateSyncSection:YES];
   [self updateIdentitySectionAndNotifyConsumer];
 }
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
index 7730dad..e97f9a2 100644
--- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -153,7 +153,8 @@
   ~IdentityObserverBridge() override {}
 
   // IdentityManager::Observer implementation:
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
 
@@ -175,7 +176,7 @@
 }
 
 void IdentityObserverBridge::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   [owner_ onSignInStateChanged];
 }
 
diff --git a/ios/web_view/internal/signin/web_view_identity_manager_factory.mm b/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
index ec5592d..39fc486 100644
--- a/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
+++ b/ios/web_view/internal/signin/web_view_identity_manager_factory.mm
@@ -13,6 +13,7 @@
 #include "ios/web_view/internal/signin/web_view_oauth2_token_service_factory.h"
 #include "ios/web_view/internal/signin/web_view_signin_manager_factory.h"
 #include "ios/web_view/internal/web_view_browser_state.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator_impl.h"
 #include "services/identity/public/cpp/accounts_mutator.h"
 #include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/primary_account_mutator_impl.h"
@@ -45,7 +46,10 @@
                 WebViewAccountTrackerServiceFactory::GetForBrowserState(
                     browser_state),
                 WebViewSigninManagerFactory::GetForBrowserState(browser_state)),
-            nullptr) {}
+            nullptr,
+            std::make_unique<identity::AccountsCookieMutatorImpl>(
+                WebViewGaiaCookieManagerServiceFactory::GetForBrowserState(
+                    browser_state))) {}
 };
 
 WebViewIdentityManagerFactory::WebViewIdentityManagerFactory()
diff --git a/media/learning/common/labelled_example.cc b/media/learning/common/labelled_example.cc
index 76d0850..43e834f 100644
--- a/media/learning/common/labelled_example.cc
+++ b/media/learning/common/labelled_example.cc
@@ -59,7 +59,8 @@
 LabelledExample& LabelledExample::operator=(const LabelledExample& rhs) =
     default;
 
-LabelledExample& LabelledExample::operator=(LabelledExample&& rhs) = default;
+LabelledExample& LabelledExample::operator=(LabelledExample&& rhs) noexcept =
+    default;
 
 TrainingData::TrainingData() = default;
 
diff --git a/media/learning/common/labelled_example.h b/media/learning/common/labelled_example.h
index 4f43c54..365abc3c 100644
--- a/media/learning/common/labelled_example.h
+++ b/media/learning/common/labelled_example.h
@@ -40,7 +40,7 @@
   bool operator<(const LabelledExample& rhs) const;
 
   LabelledExample& operator=(const LabelledExample& rhs);
-  LabelledExample& operator=(LabelledExample&& rhs);
+  LabelledExample& operator=(LabelledExample&& rhs) noexcept;
 
   // Observed feature values.
   // Note that to interpret these values, you probably need to have the
diff --git a/media/learning/common/value.cc b/media/learning/common/value.cc
index 9c9395c..12ea399 100644
--- a/media/learning/common/value.cc
+++ b/media/learning/common/value.cc
@@ -23,6 +23,12 @@
 
 Value::Value(const Value& other) : value_(other.value_) {}
 
+Value::Value(Value&& rhs) noexcept = default;
+
+Value& Value::operator=(const Value& rhs) = default;
+
+Value& Value::operator=(Value&& rhs) noexcept = default;
+
 bool Value::operator==(const Value& rhs) const {
   return value_ == rhs.value_;
 }
diff --git a/media/learning/common/value.h b/media/learning/common/value.h
index 0e64da9..62f4953 100644
--- a/media/learning/common/value.h
+++ b/media/learning/common/value.h
@@ -38,6 +38,10 @@
   explicit Value(const std::string& x);
 
   Value(const Value& other);
+  Value(Value&&) noexcept;
+
+  Value& operator=(const Value&);
+  Value& operator=(Value&&) noexcept;
 
   bool operator==(const Value& rhs) const;
   bool operator!=(const Value& rhs) const;
diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc
index 6f1f0589..ad94984 100644
--- a/net/quic/quic_connection_logger.cc
+++ b/net/quic/quic_connection_logger.cc
@@ -598,15 +598,18 @@
 
 void QuicConnectionLogger::OnPacketHeader(
     const quic::QuicPacketHeader& header) {
+  if (!first_received_packet_number_.IsInitialized()) {
+    first_received_packet_number_ = header.packet_number;
+  } else if (header.packet_number < first_received_packet_number_) {
+    // Ignore packets with packet numbers less than
+    // first_received_packet_number_.
+    return;
+  }
   ++num_packets_received_;
-  if (!largest_received_packet_number_.IsInitialized() ||
-      largest_received_packet_number_ < header.packet_number) {
-    // TODO(fayang): Fix this as this check assume the first received packet
-    // is 1.
-    uint64_t delta = header.packet_number.ToUint64();
-    if (largest_received_packet_number_.IsInitialized()) {
-      delta = header.packet_number - largest_received_packet_number_;
-    }
+  if (!largest_received_packet_number_.IsInitialized()) {
+    largest_received_packet_number_ = header.packet_number;
+  } else if (largest_received_packet_number_ < header.packet_number) {
+    uint64_t delta = header.packet_number - largest_received_packet_number_;
     if (delta > 1) {
       // There is a gap between the largest packet previously received and
       // the current packet.  This indicates either loss, or out-of-order
@@ -617,9 +620,9 @@
     }
     largest_received_packet_number_ = header.packet_number;
   }
-  // TODO(fayang): Fix this as this check assume the first received packet is 1.
-  if (header.packet_number < quic::QuicPacketNumber(received_packets_.size())) {
-    received_packets_[static_cast<size_t>(header.packet_number.ToUint64())] =
+  if (header.packet_number - first_received_packet_number_ <
+      received_packets_.size()) {
+    received_packets_[header.packet_number - first_received_packet_number_] =
         true;
   }
   if (last_received_packet_number_.IsInitialized() &&
@@ -632,13 +635,7 @@
         static_cast<base::HistogramBase::Sample>(last_received_packet_number_ -
                                                  header.packet_number));
   } else if (no_packet_received_after_ping_) {
-    if (!last_received_packet_number_.IsInitialized()) {
-      // TODO(fayang): Fix this as this check assume the first received packet
-      // is 1.
-      UMA_HISTOGRAM_COUNTS_1M("Net.QuicSession.PacketGapReceivedNearPing",
-                              static_cast<base::HistogramBase::Sample>(
-                                  header.packet_number.ToUint64()));
-    } else {
+    if (last_received_packet_number_.IsInitialized()) {
       UMA_HISTOGRAM_COUNTS_1M(
           "Net.QuicSession.PacketGapReceivedNearPing",
           static_cast<base::HistogramBase::Sample>(
@@ -661,12 +658,11 @@
 
 void QuicConnectionLogger::OnAckFrame(const quic::QuicAckFrame& frame) {
   const size_t kApproximateLargestSoloAckBytes = 100;
-  // TODO(fayang): Fix this as this check assume the first received packet is 1.
-  if (last_received_packet_number_ <
-          quic::QuicPacketNumber(received_acks_.size()) &&
+  if (last_received_packet_number_ - first_received_packet_number_ <
+          received_acks_.size() &&
       last_received_packet_size_ < kApproximateLargestSoloAckBytes) {
-    received_acks_[static_cast<size_t>(
-        last_received_packet_number_.ToUint64())] = true;
+    received_acks_[last_received_packet_number_ -
+                   first_received_packet_number_] = true;
   }
 
   if (!net_log_is_capturing_)
@@ -845,14 +841,12 @@
 }
 
 float QuicConnectionLogger::ReceivedPacketLossRate() const {
-  // TODO(fayang): Fix this as this check assume the first received packet is 1.
-  if (!largest_received_packet_number_.IsInitialized() ||
-      largest_received_packet_number_ <=
-          quic::QuicPacketNumber(num_packets_received_))
+  if (!largest_received_packet_number_.IsInitialized())
     return 0.0f;
-  float num_received =
-      largest_received_packet_number_.ToUint64() - num_packets_received_;
-  return num_received / largest_received_packet_number_.ToUint64();
+  float num_packets =
+      largest_received_packet_number_ - first_received_packet_number_ + 1;
+  float num_missing = num_packets - num_packets_received_;
+  return num_missing / num_packets;
 }
 
 void QuicConnectionLogger::OnRttChanged(quic::QuicTime::Delta rtt) const {
@@ -875,9 +869,8 @@
   // histogram.  (e.g., if we only got 5 packets, but lost 1, we'd otherwise
   // record a 20% loss in this histogram!). We may still get some strange data
   // (1 loss in 22 is still high :-/).
-  // TODO(fayang): Fix this as this check assume the first received packet is 1.
   if (!largest_received_packet_number_.IsInitialized() ||
-      largest_received_packet_number_ <= quic::QuicPacketNumber(21))
+      largest_received_packet_number_ - first_received_packet_number_ < 22)
     return;
 
   string prefix("Net.QuicSession.PacketLossRate_");
diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h
index e320098..085f4d91 100644
--- a/net/quic/quic_connection_logger.h
+++ b/net/quic/quic_connection_logger.h
@@ -118,6 +118,11 @@
   bool no_packet_received_after_ping_;
   // The size of the previously received packet.
   size_t previous_received_packet_size_;
+  // The first received packet number. Used as the left edge of
+  // received_packets_ and received_acks_. In the case where packets are
+  // received out of order, packets with numbers smaller than
+  // first_received_packet_number_ will not be logged.
+  quic::QuicPacketNumber first_received_packet_number_;
   // The largest packet number received.  In the case where a packet is
   // received late (out of order), this value will not be updated.
   quic::QuicPacketNumber largest_received_packet_number_;
@@ -152,13 +157,13 @@
   // Count of the number of BLOCKED frames sent.
   int num_blocked_frames_sent_;
   // Vector of inital packets status' indexed by packet numbers, where
-  // false means never received.  Zero is not a valid packet number, so
-  // that offset is never used, and we'll track 150 packets.
-  std::bitset<151> received_packets_;
+  // false means never received. We track 150 packets starting from
+  // first_received_packet_number_.
+  std::bitset<150> received_packets_;
   // Vector to indicate which of the initial 150 received packets turned out to
   // contain solo ACK frames.  An element is true iff an ACK frame was in the
   // corresponding packet, and there was very little else.
-  std::bitset<151> received_acks_;
+  std::bitset<150> received_acks_;
   // The available type of connection (WiFi, 3G, etc.) when connection was first
   // used.
   const char* const connection_description_;
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc
index 363b87aa..8d2d634 100644
--- a/net/third_party/quic/core/quic_connection.cc
+++ b/net/third_party/quic/core/quic_connection.cc
@@ -1464,10 +1464,9 @@
   if (should_last_packet_instigate_acks_ && !ack_queued_) {
     ++num_retransmittable_packets_received_since_last_ack_sent_;
     if (ack_mode_ != TCP_ACKING &&
-        // TODO(fayang): Fix this as this check assumes the first received
-        // packet is 1.
-        last_header_.packet_number >
-            QuicPacketNumber(min_received_before_ack_decimation_)) {
+        last_header_.packet_number >=
+            received_packet_manager_.PeerFirstSendingPacketNumber() +
+                min_received_before_ack_decimation_) {
       // Ack up to 10 packets at once unless ack decimation is unlimited.
       if (!unlimited_ack_decimation_ &&
           num_retransmittable_packets_received_since_last_ack_sent_ >=
@@ -1949,16 +1948,17 @@
     // Count those that would have been accepted if FLAGS..random_ipn
     // were true -- to detect/diagnose potential issues prior to
     // enabling the flag.
-    if ((header.packet_number > QuicPacketNumber(1)) &&
-        (header.packet_number <= MaxRandomInitialPacketNumber())) {
+    if (header.packet_number >
+            received_packet_manager_.PeerFirstSendingPacketNumber() &&
+        header.packet_number <= MaxRandomInitialPacketNumber()) {
       QUIC_CODE_COUNT_N(had_possibly_random_ipn, 2, 2);
     }
     bool out_of_bound =
         last_header_.packet_number.IsInitialized()
             ? !Near(header.packet_number, last_header_.packet_number)
-            // TODO(fayang): Fix this as this check assume the first received
-            // packet is 1.
-            : header.packet_number > QuicPacketNumber(kMaxPacketGap);
+            : header.packet_number >=
+                  (received_packet_manager_.PeerFirstSendingPacketNumber() +
+                   kMaxPacketGap);
     if (out_of_bound) {
       QUIC_DLOG(INFO) << ENDPOINT << "Packet " << header.packet_number
                       << " out of bounds.  Discarding";
diff --git a/net/third_party/quic/core/quic_connection_test.cc b/net/third_party/quic/core/quic_connection_test.cc
index f232587..ee9e0d9f 100644
--- a/net/third_party/quic/core/quic_connection_test.cc
+++ b/net/third_party/quic/core/quic_connection_test.cc
@@ -2194,20 +2194,38 @@
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
 
   ProcessPacket(3);
-  // Should ack immediately since we have missing packets.
-  EXPECT_EQ(1u, writer_->packets_write_attempts());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    // Should not cause an ack.
+    EXPECT_EQ(0u, writer_->packets_write_attempts());
+  } else {
+    // Should ack immediately since we have missing packets.
+    EXPECT_EQ(1u, writer_->packets_write_attempts());
+  }
 
   ProcessPacket(2);
-  // Should ack immediately since we have missing packets.
-  EXPECT_EQ(2u, writer_->packets_write_attempts());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    // Should ack immediately, since this fills the last hole.
+    EXPECT_EQ(1u, writer_->packets_write_attempts());
+  } else {
+    // Should ack immediately since we have missing packets.
+    EXPECT_EQ(2u, writer_->packets_write_attempts());
+  }
 
   ProcessPacket(1);
   // Should ack immediately, since this fills the last hole.
-  EXPECT_EQ(3u, writer_->packets_write_attempts());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(2u, writer_->packets_write_attempts());
+  } else {
+    EXPECT_EQ(3u, writer_->packets_write_attempts());
+  }
 
   ProcessPacket(4);
   // Should not cause an ack.
-  EXPECT_EQ(3u, writer_->packets_write_attempts());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(2u, writer_->packets_write_attempts());
+  } else {
+    EXPECT_EQ(3u, writer_->packets_write_attempts());
+  }
 }
 
 TEST_P(QuicConnectionTest, OutOfOrderAckReceiptCausesNoAck) {
@@ -5812,23 +5830,46 @@
 TEST_P(QuicConnectionTest, NoAckOnOldNacks) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   // Drop one packet, triggering a sequence of acks.
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  } else {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+  }
   ProcessPacket(2);
   size_t frames_per_ack = GetParam().no_stop_waiting ? 1 : 2;
-  EXPECT_EQ(frames_per_ack, writer_->frame_count());
-  EXPECT_FALSE(writer_->ack_frames().empty());
-  writer_->Reset();
+  if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(frames_per_ack, writer_->frame_count());
+    EXPECT_FALSE(writer_->ack_frames().empty());
+    writer_->Reset();
+  }
+
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
   ProcessPacket(3);
   EXPECT_EQ(frames_per_ack, writer_->frame_count());
   EXPECT_FALSE(writer_->ack_frames().empty());
   writer_->Reset();
+
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
+  } else {
+    EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
+  }
   ProcessPacket(4);
-  EXPECT_EQ(frames_per_ack, writer_->frame_count());
-  EXPECT_FALSE(writer_->ack_frames().empty());
-  writer_->Reset();
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(0u, writer_->frame_count());
+  } else {
+    EXPECT_EQ(frames_per_ack, writer_->frame_count());
+    EXPECT_FALSE(writer_->ack_frames().empty());
+    writer_->Reset();
+  }
+
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1);
   ProcessPacket(5);
   EXPECT_EQ(frames_per_ack, writer_->frame_count());
   EXPECT_FALSE(writer_->ack_frames().empty());
   writer_->Reset();
+
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0);
   // Now only set the timer on the 6th packet, instead of sending another ack.
   ProcessPacket(6);
   EXPECT_EQ(0u, writer_->frame_count());
diff --git a/net/third_party/quic/core/quic_received_packet_manager.cc b/net/third_party/quic/core/quic_received_packet_manager.cc
index aad7aa9..8bf9a65 100644
--- a/net/third_party/quic/core/quic_received_packet_manager.cc
+++ b/net/third_party/quic/core/quic_received_packet_manager.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quic/core/quic_received_packet_manager.h"
 
+#include <algorithm>
 #include <limits>
 #include <utility>
 
@@ -75,6 +76,13 @@
           std::make_pair(packet_number, receipt_time));
     }
   }
+
+  if (least_received_packet_number_.IsInitialized()) {
+    least_received_packet_number_ =
+        std::min(least_received_packet_number_, packet_number);
+  } else {
+    least_received_packet_number_ = packet_number;
+  }
 }
 
 bool QuicReceivedPacketManager::IsMissing(QuicPacketNumber packet_number) {
@@ -150,12 +158,14 @@
   if (ack_frame_.packets.NumIntervals() > 1) {
     return true;
   }
-  // TODO(fayang): Fix this as this check assumes first sent packet by peer
-  // is 1.
-  return ack_frame_.packets.Min() >
-         (peer_least_packet_awaiting_ack_.IsInitialized()
-              ? peer_least_packet_awaiting_ack_
-              : QuicPacketNumber(1));
+  if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    return ack_frame_.packets.Min() >
+           (peer_least_packet_awaiting_ack_.IsInitialized()
+                ? peer_least_packet_awaiting_ack_
+                : QuicPacketNumber(1));
+  }
+  return peer_least_packet_awaiting_ack_.IsInitialized() &&
+         ack_frame_.packets.Min() > peer_least_packet_awaiting_ack_;
 }
 
 bool QuicReceivedPacketManager::HasNewMissingPackets() const {
@@ -171,4 +181,16 @@
   return LargestAcked(ack_frame_);
 }
 
+QuicPacketNumber QuicReceivedPacketManager::PeerFirstSendingPacketNumber()
+    const {
+  if (!GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    return QuicPacketNumber(1);
+  }
+  if (!least_received_packet_number_.IsInitialized()) {
+    QUIC_BUG << "No packets have been received yet";
+    return QuicPacketNumber(1);
+  }
+  return least_received_packet_number_;
+}
+
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_received_packet_manager.h b/net/third_party/quic/core/quic_received_packet_manager.h
index 21fe690f..f438702 100644
--- a/net/third_party/quic/core/quic_received_packet_manager.h
+++ b/net/third_party/quic/core/quic_received_packet_manager.h
@@ -65,6 +65,13 @@
 
   QuicPacketNumber GetLargestObserved() const;
 
+  // Returns peer first sending packet number to our best knowledge. If
+  // GetQuicRestartFlag(quic_enable_accept_random_ipn) is false, returns 1.
+  // Otherwise considers least_received_packet_number_ as peer first sending
+  // packet number. Please note, this function should only be called when at
+  // least one packet has been received.
+  QuicPacketNumber PeerFirstSendingPacketNumber() const;
+
   // For logging purposes.
   const QuicAckFrame& ack_frame() const { return ack_frame_; }
 
@@ -101,6 +108,9 @@
   // If true, save timestamps in the ack_frame_.
   bool save_timestamps_;
 
+  // Least packet number received from peer.
+  QuicPacketNumber least_received_packet_number_;
+
   QuicConnectionStats* stats_;
 };
 
diff --git a/net/third_party/quic/core/quic_received_packet_manager_test.cc b/net/third_party/quic/core/quic_received_packet_manager_test.cc
index fdb8960..e68f864 100644
--- a/net/third_party/quic/core/quic_received_packet_manager_test.cc
+++ b/net/third_party/quic/core/quic_received_packet_manager_test.cc
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "net/third_party/quic/core/quic_connection_stats.h"
+#include "net/third_party/quic/platform/api/quic_expect_bug.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 
 namespace quic {
@@ -159,6 +160,44 @@
   EXPECT_EQ(2u, received_manager_.ack_frame().received_packet_times.size());
 }
 
+TEST_P(QuicReceivedPacketManagerTest, HasMissingPackets) {
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_QUIC_BUG(received_manager_.PeerFirstSendingPacketNumber(),
+                    "No packets have been received yet");
+  } else {
+    EXPECT_EQ(QuicPacketNumber(1),
+              received_manager_.PeerFirstSendingPacketNumber());
+  }
+  RecordPacketReceipt(4, QuicTime::Zero());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_EQ(QuicPacketNumber(4),
+              received_manager_.PeerFirstSendingPacketNumber());
+    EXPECT_FALSE(received_manager_.HasMissingPackets());
+  } else {
+    EXPECT_TRUE(received_manager_.HasMissingPackets());
+    EXPECT_EQ(QuicPacketNumber(1),
+              received_manager_.PeerFirstSendingPacketNumber());
+  }
+  RecordPacketReceipt(3, QuicTime::Zero());
+  if (GetQuicRestartFlag(quic_enable_accept_random_ipn)) {
+    EXPECT_FALSE(received_manager_.HasMissingPackets());
+    EXPECT_EQ(QuicPacketNumber(3),
+              received_manager_.PeerFirstSendingPacketNumber());
+  } else {
+    EXPECT_TRUE(received_manager_.HasMissingPackets());
+    EXPECT_EQ(QuicPacketNumber(1),
+              received_manager_.PeerFirstSendingPacketNumber());
+  }
+  RecordPacketReceipt(1, QuicTime::Zero());
+  EXPECT_EQ(QuicPacketNumber(1),
+            received_manager_.PeerFirstSendingPacketNumber());
+  EXPECT_TRUE(received_manager_.HasMissingPackets());
+  RecordPacketReceipt(2, QuicTime::Zero());
+  EXPECT_EQ(QuicPacketNumber(1),
+            received_manager_.PeerFirstSendingPacketNumber());
+  EXPECT_FALSE(received_manager_.HasMissingPackets());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/net/third_party/quic/tools/quic_client_test.cc b/net/third_party/quic/tools/quic_client_test.cc
index 8717fd7f..b146ca4 100644
--- a/net/third_party/quic/tools/quic_client_test.cc
+++ b/net/third_party/quic/tools/quic_client_test.cc
@@ -5,20 +5,17 @@
 #include "net/third_party/quic/tools/quic_client.h"
 
 #include <dirent.h>
-#include <stdio.h>
+#include <sys/types.h>
 
 #include <memory>
 
-#include "base/files/file_enumerator.h"
-#include "base/files/file_util.h"
+#include "net/third_party/quic/platform/api/quic_epoll.h"
+#include "net/third_party/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 #include "net/third_party/quic/platform/api/quic_test_loopback.h"
 #include "net/third_party/quic/platform/api/quic_text_utils.h"
 #include "net/third_party/quic/test_tools/crypto_test_utils.h"
 #include "net/third_party/quic/test_tools/quic_client_peer.h"
-#include "net/third_party/quic/test_tools/quic_test_utils.h"
-#include "net/tools/epoll_server/epoll_server.h"
-#include "testing/gtest/include/gtest/gtest.h"
 
 namespace quic {
 namespace test {
@@ -26,24 +23,31 @@
 
 const char* kPathToFds = "/proc/self/fd";
 
+QuicString ReadLink(const QuicString& path) {
+  QuicString result(PATH_MAX, '\0');
+  ssize_t result_size = readlink(path.c_str(), &result[0], result.size());
+  CHECK(result_size > 0 && static_cast<size_t>(result_size) < result.size());
+  result.resize(result_size);
+  return result;
+}
+
 // Counts the number of open sockets for the current process.
 size_t NumOpenSocketFDs() {
-  base::FileEnumerator fd_entries(
-      base::FilePath(kPathToFds), false,
-      base::FileEnumerator::FILES | base::FileEnumerator::SHOW_SYM_LINKS);
-
   size_t socket_count = 0;
-  for (base::FilePath entry = fd_entries.Next(); !entry.empty();
-       entry = fd_entries.Next()) {
-    base::FilePath fd_path;
-    if (!base::ReadSymbolicLink(entry, &fd_path)) {
+  dirent* file;
+  std::unique_ptr<DIR, int (*)(DIR*)> fd_directory(opendir(kPathToFds),
+                                                   closedir);
+  while ((file = readdir(fd_directory.get())) != nullptr) {
+    QuicStringPiece name(file->d_name);
+    if (name == "." || name == "..") {
       continue;
     }
-    if (QuicTextUtils::StartsWith(fd_path.value(), "socket:")) {
+
+    QuicString fd_path = ReadLink(QuicStrCat(kPathToFds, "/", name));
+    if (QuicTextUtils::StartsWith(fd_path, "socket:")) {
       socket_count++;
     }
   }
-
   return socket_count;
 }
 
@@ -66,10 +70,6 @@
   // Make sure that the QuicClient doesn't leak socket FDs. Doing so could cause
   // port exhaustion in long running processes which repeatedly create clients.
 
-  // Create a ProofVerifier before counting the number of open FDs to work
-  // around some memory corruption detector weirdness.
-  crypto_test_utils::ProofVerifierForTesting().reset();
-
   // Record initial number of FDs, after creation of EpollServer.
   QuicEpollServer eps;
   size_t number_of_open_fds = NumOpenSocketFDs();
@@ -90,10 +90,6 @@
 }
 
 TEST_F(QuicClientTest, CreateAndCleanUpUDPSockets) {
-  // Create a ProofVerifier before counting the number of open FDs to work
-  // around some memory corruption detector weirdness.
-  crypto_test_utils::ProofVerifierForTesting().reset();
-
   QuicEpollServer eps;
   size_t number_of_open_fds = NumOpenSocketFDs();
 
diff --git a/sandbox/linux/services/thread_helpers.cc b/sandbox/linux/services/thread_helpers.cc
index 20752c8d..d27cfe6 100644
--- a/sandbox/linux/services/thread_helpers.cc
+++ b/sandbox/linux/services/thread_helpers.cc
@@ -128,7 +128,7 @@
 
   const base::PlatformThreadId thread_id = thread->GetThreadId();
   const std::string thread_id_dir_str =
-      "self/task/" + base::IntToString(thread_id) + "/";
+      "self/task/" + base::NumberToString(thread_id) + "/";
 
   if (action == ThreadAction::Stop) {
     // The target thread should exist in /proc.
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
index a5f96bef..2e5ded8 100644
--- a/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
+++ b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc
@@ -23,7 +23,7 @@
 
   // Set-up a fake environment as if we went through the setuid sandbox.
   EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides,
-              base::IntToString(kSUIDSandboxApiNumber)));
+                          base::NumberToString(kSUIDSandboxApiNumber)));
   EXPECT_TRUE(env->SetVar(kSandboxDescriptorEnvironmentVarName, "1"));
   EXPECT_TRUE(env->SetVar(kSandboxPIDNSEnvironmentVarName, "1"));
   EXPECT_TRUE(env->UnSetVar(kSandboxNETNSEnvironmentVarName));
@@ -36,7 +36,7 @@
 
   // Forge an incorrect API version and check.
   EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides,
-              base::IntToString(kSUIDSandboxApiNumber + 1)));
+                          base::NumberToString(kSUIDSandboxApiNumber + 1)));
   EXPECT_FALSE(sandbox_client->IsSuidSandboxUpToDate());
   // We didn't go through the actual sandboxing mechanism as it is
   // very hard in a unit test.
diff --git a/sandbox/linux/suid/client/setuid_sandbox_host.cc b/sandbox/linux/suid/client/setuid_sandbox_host.cc
index 784f6073..7a103bf 100644
--- a/sandbox/linux/suid/client/setuid_sandbox_host.cc
+++ b/sandbox/linux/suid/client/setuid_sandbox_host.cc
@@ -37,7 +37,7 @@
 // setuid sandbox. Old versions of the sandbox will ignore this.
 void SetSandboxAPIEnvironmentVariable(base::Environment* env) {
   env->SetVar(kSandboxEnvironmentApiRequest,
-              base::IntToString(kSUIDSandboxApiNumber));
+              base::NumberToString(kSUIDSandboxApiNumber));
 }
 
 // Unset environment variables that are expected to be set by the setuid
diff --git a/sandbox/win/src/policy_target_test.cc b/sandbox/win/src/policy_target_test.cc
index 32df7b7..39b5afb4 100644
--- a/sandbox/win/src/policy_target_test.cc
+++ b/sandbox/win/src/policy_target_test.cc
@@ -427,7 +427,7 @@
   base::string16 arguments(L"\"");
   arguments += prog_name;
   arguments += L"\" -child 0 shared_memory_handle ";
-  arguments += base::UintToString16(
+  arguments += base::NumberToString16(
       base::win::HandleToUint32(read_only_view.handle().GetHandle()));
 
   // Launch the app.
diff --git a/services/identity/public/cpp/BUILD.gn b/services/identity/public/cpp/BUILD.gn
index f8268045..b92c284 100644
--- a/services/identity/public/cpp/BUILD.gn
+++ b/services/identity/public/cpp/BUILD.gn
@@ -8,6 +8,9 @@
     "access_token_fetcher.h",
     "access_token_info.cc",
     "access_token_info.h",
+    "accounts_cookie_mutator.h",
+    "accounts_cookie_mutator_impl.cc",
+    "accounts_cookie_mutator_impl.h",
     "accounts_in_cookie_jar_info.cc",
     "accounts_in_cookie_jar_info.h",
     "accounts_mutator.h",
diff --git a/services/identity/public/cpp/DEPS b/services/identity/public/cpp/DEPS
index 8084961..9b5dac39 100644
--- a/services/identity/public/cpp/DEPS
+++ b/services/identity/public/cpp/DEPS
@@ -11,6 +11,7 @@
   "+components/signin/core/browser/signin_switches.h",
   "+components/signin/core/browser/ubertoken_fetcher_impl.h",
   "+components/signin/core/browser/ubertoken_fetcher.h",
+  "+google_apis/gaia/gaia_auth_fetcher.h",
   "+google_apis/gaia/gaia_auth_util.h",
   "+google_apis/gaia/google_service_auth_error.h",
   "+google_apis/gaia/oauth2_access_token_consumer.h",
@@ -25,6 +26,7 @@
   ],
   "identity_manager_unittest.cc": [
     "+google_apis/gaia/oauth2_token_service_delegate.h",
+    "+services/network/test/test_cookie_manager.h",
     "+services/network/test/test_url_loader_factory.h",
   ],
   "accounts_mutator_impl_unittest.cc": [
diff --git a/services/identity/public/cpp/accounts_cookie_mutator.h b/services/identity/public/cpp/accounts_cookie_mutator.h
new file mode 100644
index 0000000..7094270c
--- /dev/null
+++ b/services/identity/public/cpp/accounts_cookie_mutator.h
@@ -0,0 +1,25 @@
+// Copyright 2019 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.
+
+#ifndef SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNTS_COOKIE_MUTATOR_H_
+#define SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNTS_COOKIE_MUTATOR_H_
+
+#include "base/macros.h"
+
+namespace identity {
+
+// AccountsCookieMutator is the interface to support merging known local Google
+// accounts into the cookie jar tracking the list of logged-in Google sessions.
+class AccountsCookieMutator {
+ public:
+  AccountsCookieMutator() = default;
+  virtual ~AccountsCookieMutator() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutator);
+};
+
+}  // namespace identity
+
+#endif  // SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNTS_COOKIE_MUTATOR_H_
diff --git a/services/identity/public/cpp/accounts_cookie_mutator_impl.cc b/services/identity/public/cpp/accounts_cookie_mutator_impl.cc
new file mode 100644
index 0000000..160ba59
--- /dev/null
+++ b/services/identity/public/cpp/accounts_cookie_mutator_impl.cc
@@ -0,0 +1,19 @@
+// Copyright 2019 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.
+
+#include "services/identity/public/cpp/accounts_cookie_mutator_impl.h"
+
+#include "components/signin/core/browser/gaia_cookie_manager_service.h"
+
+namespace identity {
+
+AccountsCookieMutatorImpl::AccountsCookieMutatorImpl(
+    GaiaCookieManagerService* gaia_cookie_manager_service)
+    : gaia_cookie_manager_service_(gaia_cookie_manager_service) {
+  DCHECK(gaia_cookie_manager_service_);
+}
+
+AccountsCookieMutatorImpl::~AccountsCookieMutatorImpl() {}
+
+}  // namespace identity
diff --git a/services/identity/public/cpp/accounts_cookie_mutator_impl.h b/services/identity/public/cpp/accounts_cookie_mutator_impl.h
new file mode 100644
index 0000000..c358d26
--- /dev/null
+++ b/services/identity/public/cpp/accounts_cookie_mutator_impl.h
@@ -0,0 +1,30 @@
+// Copyright 2019 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.
+
+#ifndef SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNTS_COOKIE_MUTATOR_IMPL_H_
+#define SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNTS_COOKIE_MUTATOR_IMPL_H_
+
+#include "base/macros.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator.h"
+
+class GaiaCookieManagerService;
+
+namespace identity {
+
+// Concrete implementation of the AccountsCookieMutator interface.
+class AccountsCookieMutatorImpl : public AccountsCookieMutator {
+ public:
+  explicit AccountsCookieMutatorImpl(
+      GaiaCookieManagerService* gaia_cookie_manager_service);
+  ~AccountsCookieMutatorImpl() override;
+
+ private:
+  GaiaCookieManagerService* gaia_cookie_manager_service_;
+
+  DISALLOW_COPY_AND_ASSIGN(AccountsCookieMutatorImpl);
+};
+
+}  // namespace identity
+
+#endif  // SERVICES_IDENTITY_PUBLIC_CPP_ACCOUNTS_COOKIE_MUTATOR_IMPL_H_
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc
index 2877af4..93de3ef 100644
--- a/services/identity/public/cpp/identity_manager.cc
+++ b/services/identity/public/cpp/identity_manager.cc
@@ -9,6 +9,7 @@
 #include "build/build_config.h"
 #include "components/signin/core/browser/ubertoken_fetcher_impl.h"
 #include "google_apis/gaia/gaia_auth_util.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator.h"
 #include "services/identity/public/cpp/accounts_mutator.h"
 #include "services/identity/public/cpp/primary_account_mutator.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
@@ -38,13 +39,16 @@
     AccountTrackerService* account_tracker_service,
     GaiaCookieManagerService* gaia_cookie_manager_service,
     std::unique_ptr<PrimaryAccountMutator> primary_account_mutator,
-    std::unique_ptr<AccountsMutator> accounts_mutator)
+    std::unique_ptr<AccountsMutator> accounts_mutator,
+    std::unique_ptr<AccountsCookieMutator> accounts_cookie_mutator)
     : signin_manager_(signin_manager),
       token_service_(token_service),
       account_tracker_service_(account_tracker_service),
       gaia_cookie_manager_service_(gaia_cookie_manager_service),
       primary_account_mutator_(std::move(primary_account_mutator)),
-      accounts_mutator_(std::move(accounts_mutator)) {
+      accounts_mutator_(std::move(accounts_mutator)),
+      accounts_cookie_mutator_(std::move(accounts_cookie_mutator)) {
+  DCHECK(accounts_cookie_mutator_);
   signin_manager_->AddObserver(this);
   token_service_->AddDiagnosticsObserver(this);
   token_service_->AddObserver(this);
@@ -260,6 +264,14 @@
   return accounts_mutator_.get();
 }
 
+AccountsCookieMutator* IdentityManager::GetAccountsCookieMutator() {
+  return accounts_cookie_mutator_.get();
+}
+
+void IdentityManager::StartObservingCookieChanges() {
+  gaia_cookie_manager_service_->InitCookieListener();
+}
+
 void IdentityManager::LegacyLoadCredentials(
     const std::string& primary_account_id) {
   token_service_->LoadCredentials(primary_account_id);
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h
index ac0a6030..a5095bcf 100644
--- a/services/identity/public/cpp/identity_manager.h
+++ b/services/identity/public/cpp/identity_manager.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_IDENTITY_PUBLIC_CPP_IDENTITY_MANAGER_H_
 #define SERVICES_IDENTITY_PUBLIC_CPP_IDENTITY_MANAGER_H_
 
+#include <memory>
 #include <string>
 
 #include "base/observer_list.h"
@@ -46,6 +47,7 @@
 namespace identity {
 
 class AccountsMutator;
+class AccountsCookieMutator;
 class PrimaryAccountMutator;
 enum class ClearPrimaryAccountPolicy;
 struct CookieParams;
@@ -68,7 +70,8 @@
 
     // Called when an account becomes the user's primary account.
     // This method is not called during a reauth.
-    virtual void OnPrimaryAccountSet(const AccountInfo& primary_account_info) {}
+    virtual void OnPrimaryAccountSet(
+        const CoreAccountInfo& primary_account_info) {}
 
     // Called when when the user moves from having a primary account to no
     // longer having a primary account.
@@ -197,7 +200,8 @@
       AccountTrackerService* account_tracker_service,
       GaiaCookieManagerService* gaia_cookie_manager_service,
       std::unique_ptr<PrimaryAccountMutator> primary_account_mutator,
-      std::unique_ptr<AccountsMutator> accounts_mutator);
+      std::unique_ptr<AccountsMutator> accounts_mutator,
+      std::unique_ptr<AccountsCookieMutator> accounts_cookie_mutator);
   ~IdentityManager() override;
 
   // Provides access to the extended information of the user's primary account.
@@ -349,6 +353,13 @@
   // returns null.
   AccountsMutator* GetAccountsMutator();
 
+  // Returns pointer to the object used to manipulate the cookies stored and the
+  // accounts associated with them. Guaranteed to be non-null.
+  AccountsCookieMutator* GetAccountsCookieMutator();
+
+  // Turns on observation of network::CookieManager changes.
+  void StartObservingCookieChanges();
+
   // Loads credentials from a backing persistent store to make them available
   // after service is used between profile restarts.
   // NOTE: In normal usage this method SHOULD NOT be called as the loading of
@@ -382,8 +393,8 @@
 
  private:
   // These test helpers need to use some of the private methods below.
-  friend AccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
-                                       const std::string& email);
+  friend CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
+                                           const std::string& email);
   friend void SetRefreshTokenForPrimaryAccount(
       IdentityManager* identity_manager,
       const std::string& token_value);
@@ -513,6 +524,10 @@
   // supported on the current platform.
   std::unique_ptr<AccountsMutator> accounts_mutator_;
 
+  // AccountsCookieMutator instance. Guaranteed to be non-null, as this
+  // functionality is supported on all platforms.
+  std::unique_ptr<AccountsCookieMutator> accounts_cookie_mutator_;
+
   // Lists of observers.
   // Makes sure lists are empty on destruction.
   base::ObserverList<Observer, true>::Unchecked observer_list_;
diff --git a/services/identity/public/cpp/identity_manager_unittest.cc b/services/identity/public/cpp/identity_manager_unittest.cc
index 4fdb6ad..290eda7 100644
--- a/services/identity/public/cpp/identity_manager_unittest.cc
+++ b/services/identity/public/cpp/identity_manager_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "services/identity/public/cpp/identity_manager.h"
 
+#include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -23,10 +25,14 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "google_apis/gaia/oauth2_token_service_delegate.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator_impl.h"
 #include "services/identity/public/cpp/accounts_mutator.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
 #include "services/identity/public/cpp/primary_account_mutator.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_cookie_manager.h"
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -224,7 +230,7 @@
     on_cookie_deleted_by_user_callback_ = std::move(callback);
   }
 
-  const AccountInfo& primary_account_from_set_callback() {
+  const CoreAccountInfo& primary_account_from_set_callback() {
     return primary_account_from_set_callback_;
   }
   const AccountInfo& primary_account_from_cleared_callback() {
@@ -311,7 +317,8 @@
 
  private:
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override {
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override {
     primary_account_from_set_callback_ = primary_account_info;
     if (on_primary_account_set_callback_)
       std::move(on_primary_account_set_callback_).Run();
@@ -412,7 +419,7 @@
   base::OnceClosure on_error_state_of_refresh_token_updated_callback_;
   base::OnceClosure on_refresh_tokens_loaded_callback_;
   base::OnceClosure on_accounts_in_cookie_updated_callback_;
-  AccountInfo primary_account_from_set_callback_;
+  CoreAccountInfo primary_account_from_set_callback_;
   AccountInfo primary_account_from_cleared_callback_;
   AccountInfo account_from_refresh_token_updated_callback_;
   std::string account_from_refresh_token_removed_callback_;
@@ -514,8 +521,6 @@
 
     account_tracker_.Initialize(&pref_service_, base::FilePath());
 
-    gaia_cookie_manager_service_.InitCookieListener();
-
     RecreateSigninAndIdentityManager(
         signin::AccountConsistencyMethod::kDisabled,
         SigninManagerSetup::kWithAuthenticatedAccout);
@@ -604,7 +609,9 @@
 
     identity_manager_.reset(new IdentityManager(
         signin_manager_.get(), &token_service_, &account_tracker_,
-        &gaia_cookie_manager_service_, nullptr, nullptr));
+        &gaia_cookie_manager_service_, nullptr, nullptr,
+        std::make_unique<AccountsCookieMutatorImpl>(
+            &gaia_cookie_manager_service_)));
     identity_manager_observer_.reset(
         new TestIdentityManagerObserver(identity_manager_.get()));
     identity_manager_diagnostics_observer_.reset(
@@ -634,6 +641,8 @@
     consumer->OnOAuthMultiloginFinished(result);
   }
 
+  TestSigninClient* signin_client() { return &signin_client_; }
+
   network::TestURLLoaderFactory* test_url_loader_factory() {
     return &test_url_loader_factory_;
   }
@@ -676,7 +685,7 @@
   signin_manager()->SignIn(kTestGaiaId, kTestEmail);
   run_loop.Run();
 
-  AccountInfo primary_account_from_set_callback =
+  CoreAccountInfo primary_account_from_set_callback =
       identity_manager_observer()->primary_account_from_set_callback();
   EXPECT_EQ(kTestGaiaId, primary_account_from_set_callback.gaia);
   EXPECT_EQ(kTestEmail, primary_account_from_set_callback.email);
@@ -1477,6 +1486,12 @@
             identity_manager_diagnostics_observer()->token_requestor_scopes());
 }
 
+TEST_F(IdentityManagerTest, GetAccountsCookieMutator) {
+  AccountsCookieMutator* mutator =
+      identity_manager()->GetAccountsCookieMutator();
+  EXPECT_TRUE(mutator);
+}
+
 // Tests that requesting a load of accounts results in the notification
 // firing that tokens were loaded.
 TEST_F(IdentityManagerTest, LegacyLoadCredentials) {
@@ -2245,6 +2260,69 @@
   run_loop.Run();
 }
 
+TEST_F(IdentityManagerTest, StartObservingCookieChanges) {
+  const char kTestAccountId[] = "account_id";
+  const char kTestAccountId2[] = "account_id2";
+  const std::vector<std::string> account_ids = {kTestAccountId,
+                                                kTestAccountId2};
+
+  auto test_cookie_manager = std::make_unique<network::TestCookieManager>();
+  network::TestCookieManager* test_cookie_manager_ptr =
+      test_cookie_manager.get();
+  signin_client()->set_cookie_manager(std::move(test_cookie_manager));
+
+  identity_manager()->StartObservingCookieChanges();
+
+  // Needed to insert request in the queue.
+  gaia_cookie_manager_service()->SetAccountsInCookie(account_ids,
+                                                     gaia::GaiaSource::kChrome);
+
+  // Sample success cookie response.
+  std::string data =
+      R"()]}'
+      {
+        "status": "OK",
+        "cookies":[
+        {
+            "name":"APISID",
+            "value":"vAlUe1",
+            "domain":".google.com",
+            "path":"/",
+            "isSecure":true,
+            "isHttpOnly":false,
+            "priority":"HIGH",
+            "maxAge":63070000
+          }
+        ]
+      }
+    )";
+  OAuthMultiloginResult result(data);
+
+  SimulateOAuthMultiloginFinished(gaia_cookie_manager_service(), result);
+  base::RunLoop().RunUntilIdle();
+
+  base::RunLoop run_loop;
+  identity_manager_observer()->set_on_cookie_deleted_by_user_callback(
+      run_loop.QuitClosure());
+
+  const std::vector<net::CanonicalCookie>& cookies = result.cookies();
+
+  // Dispatch a known change of a known cookie instance *through the mojo
+  // pipe* in order to ensure the GCMS is listening to CookieManager changes.
+  //
+  // It is important the the cause of the change is known here (ie
+  // network::mojom::CookieChangeCause::EXPLICIT) so the test can block of the
+  // proper IdentityManager observer callback to be called (in this case
+  // OnAccountsCookieDeletedByUserAction).
+  //
+  // Note that this call differs from calling SimulateCookieDeletedByUser()
+  // directly in the sense that SimulateCookieDeletedByUser() does not go
+  // through any mojo pipe.
+  test_cookie_manager_ptr->DispatchCookieChange(
+      cookies[0], network::mojom::CookieChangeCause::EXPLICIT);
+  run_loop.Run();
+}
+
 TEST_F(IdentityManagerTest,
        BatchChangeObserversAreNotifiedOnCredentialsUpdate) {
   signin_manager()->SetAuthenticatedAccountInfo(kTestGaiaId, kTestEmail);
diff --git a/services/identity/public/cpp/identity_test_environment.cc b/services/identity/public/cpp/identity_test_environment.cc
index c3d0b4fda..898c0aa7 100644
--- a/services/identity/public/cpp/identity_test_environment.cc
+++ b/services/identity/public/cpp/identity_test_environment.cc
@@ -13,6 +13,8 @@
 #include "components/signin/core/browser/test_signin_client.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "google_apis/gaia/oauth2_access_token_consumer.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator.h"
+#include "services/identity/public/cpp/accounts_cookie_mutator_impl.h"
 #include "services/identity/public/cpp/accounts_mutator.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
 #include "services/identity/public/cpp/primary_account_mutator.h"
@@ -265,10 +267,14 @@
         token_service_, account_tracker_service_, signin_manager_);
 #endif
 
+    std::unique_ptr<AccountsCookieMutator> accounts_cookie_mutator =
+        std::make_unique<AccountsCookieMutatorImpl>(
+            gaia_cookie_manager_service_);
+
     owned_identity_manager_ = std::make_unique<IdentityManager>(
         signin_manager_, token_service_, account_tracker_service_,
         gaia_cookie_manager_service_, std::move(primary_account_mutator),
-        std::move(accounts_mutator));
+        std::move(accounts_mutator), std::move(accounts_cookie_mutator));
   }
 
   this->identity_manager()->AddDiagnosticsObserver(this);
@@ -286,7 +292,7 @@
                                : owned_identity_manager_.get();
 }
 
-AccountInfo IdentityTestEnvironment::SetPrimaryAccount(
+CoreAccountInfo IdentityTestEnvironment::SetPrimaryAccount(
     const std::string& email) {
   return identity::SetPrimaryAccount(identity_manager(), email);
 }
@@ -516,4 +522,18 @@
   account_fetcher_service_->EnableNetworkFetchesForTest();
 }
 
+void IdentityTestEnvironment::SimulateSuccessfulFetchOfAccountInfo(
+    const std::string& account_id,
+    const std::string& email,
+    const std::string& gaia,
+    const std::string& hosted_domain,
+    const std::string& full_name,
+    const std::string& given_name,
+    const std::string& locale,
+    const std::string& picture_url) {
+  account_fetcher_service_->FakeUserInfoFetchSuccess(
+      account_id, email, gaia, hosted_domain, full_name, given_name, locale,
+      picture_url);
+}
+
 }  // namespace identity
diff --git a/services/identity/public/cpp/identity_test_environment.h b/services/identity/public/cpp/identity_test_environment.h
index 505626c..9e43e53 100644
--- a/services/identity/public/cpp/identity_test_environment.h
+++ b/services/identity/public/cpp/identity_test_environment.h
@@ -98,9 +98,9 @@
   // Sets the primary account for the given email address, generating a GAIA ID
   // that corresponds uniquely to that email address. On non-ChromeOS, results
   // in the firing of the IdentityManager and SigninManager callbacks for signin
-  // success. Blocks until the primary account is set. Returns the AccountInfo
-  // of the newly-set account.
-  AccountInfo SetPrimaryAccount(const std::string& email);
+  // success. Blocks until the primary account is set. Returns the
+  // CoreAccountInfo of the newly-set account.
+  CoreAccountInfo SetPrimaryAccount(const std::string& email);
 
   // Sets a refresh token for the primary account (which must already be set).
   // Before updating the refresh token, blocks until refresh tokens are loaded.
@@ -268,6 +268,17 @@
   // Enable interaction with AccountFetcherService in testing context.
   void EnableOnAccountUpdatedAndOnAccountRemovedWithInfoCallbacks();
 
+  // Simulate account fetching using AccountTrackerService without sending
+  // network requests.
+  void SimulateSuccessfulFetchOfAccountInfo(const std::string& account_id,
+                                            const std::string& email,
+                                            const std::string& gaia,
+                                            const std::string& hosted_domain,
+                                            const std::string& full_name,
+                                            const std::string& given_name,
+                                            const std::string& locale,
+                                            const std::string& picture_url);
+
  private:
   friend class ::IdentityTestEnvironmentChromeBrowserStateAdaptor;
   friend class ::IdentityTestEnvironmentProfileAdaptor;
diff --git a/services/identity/public/cpp/identity_test_utils.cc b/services/identity/public/cpp/identity_test_utils.cc
index 73549bd..c07b158 100644
--- a/services/identity/public/cpp/identity_test_utils.cc
+++ b/services/identity/public/cpp/identity_test_utils.cc
@@ -37,7 +37,8 @@
 
  private:
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
   void OnRefreshTokensLoaded() override;
@@ -70,7 +71,7 @@
 }
 
 void OneShotIdentityManagerObserver::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   if (event_to_wait_on_ != IdentityManagerEvent::PRIMARY_ACCOUNT_SET)
     return;
 
@@ -172,8 +173,8 @@
 
 }  // namespace
 
-AccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
-                              const std::string& email) {
+CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
+                                  const std::string& email) {
   DCHECK(!identity_manager->HasPrimaryAccount());
 
   SigninManagerBase* signin_manager = identity_manager->GetSigninManager();
@@ -241,9 +242,15 @@
 
 AccountInfo MakePrimaryAccountAvailable(IdentityManager* identity_manager,
                                         const std::string& email) {
-  AccountInfo account_info = SetPrimaryAccount(identity_manager, email);
+  CoreAccountInfo account_info = SetPrimaryAccount(identity_manager, email);
   SetRefreshTokenForPrimaryAccount(identity_manager);
-  return account_info;
+  base::Optional<AccountInfo> primary_account_info =
+      identity_manager->FindAccountInfoForAccountWithRefreshTokenByAccountId(
+          account_info.account_id);
+  // Ensure that extended information for the account is available after setting
+  // the refresh token.
+  DCHECK(primary_account_info.has_value());
+  return primary_account_info.value();
 }
 
 void ClearPrimaryAccount(IdentityManager* identity_manager,
diff --git a/services/identity/public/cpp/identity_test_utils.h b/services/identity/public/cpp/identity_test_utils.h
index a85962d9..71772edf 100644
--- a/services/identity/public/cpp/identity_test_utils.h
+++ b/services/identity/public/cpp/identity_test_utils.h
@@ -48,10 +48,10 @@
 // address, generating a GAIA ID that corresponds uniquely to that email
 // address. On non-ChromeOS, results in the firing of the IdentityManager and
 // SigninManager callbacks for signin success. Blocks until the primary account
-// is set. Returns the AccountInfo of the newly-set account.
+// is set. Returns the CoreAccountInfo of the newly-set account.
 // NOTE: See disclaimer at top of file re: direct usage.
-AccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
-                              const std::string& email);
+CoreAccountInfo SetPrimaryAccount(IdentityManager* identity_manager,
+                                  const std::string& email);
 
 // Sets a refresh token for the primary account (which must already be set).
 // Blocks until the refresh token is set. If |token_value| is empty a default
diff --git a/services/identity/public/cpp/primary_account_access_token_fetcher.cc b/services/identity/public/cpp/primary_account_access_token_fetcher.cc
index ed24abd7..33cca9d 100644
--- a/services/identity/public/cpp/primary_account_access_token_fetcher.cc
+++ b/services/identity/public/cpp/primary_account_access_token_fetcher.cc
@@ -71,7 +71,7 @@
 }
 
 void PrimaryAccountAccessTokenFetcher::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   ProcessSigninStateChange();
 }
 
diff --git a/services/identity/public/cpp/primary_account_access_token_fetcher.h b/services/identity/public/cpp/primary_account_access_token_fetcher.h
index c403b83..b5a93d7e 100644
--- a/services/identity/public/cpp/primary_account_access_token_fetcher.h
+++ b/services/identity/public/cpp/primary_account_access_token_fetcher.h
@@ -62,7 +62,8 @@
   void StartAccessTokenRequest();
 
   // IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnRefreshTokenUpdatedForAccount(
       const AccountInfo& account_info) override;
 
diff --git a/services/identity/public/objc/identity_manager_observer_bridge.h b/services/identity/public/objc/identity_manager_observer_bridge.h
index 58b8c598..12f54b9 100644
--- a/services/identity/public/objc/identity_manager_observer_bridge.h
+++ b/services/identity/public/objc/identity_manager_observer_bridge.h
@@ -22,7 +22,7 @@
 // IdentityManager::Observer in identity_manager.h for the specification of
 // these semantics.
 
-- (void)onPrimaryAccountSet:(const AccountInfo&)primaryAccountInfo;
+- (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo;
 - (void)onPrimaryAccountCleared:(const AccountInfo&)previousPrimaryAccountInfo;
 - (void)onPrimaryAccountSigninFailed:(const GoogleServiceAuthError&)error;
 - (void)onRefreshTokenUpdatedForAccount:(const AccountInfo&)accountInfo;
@@ -47,7 +47,8 @@
   ~IdentityManagerObserverBridge() override;
 
   // IdentityManager::Observer.
-  void OnPrimaryAccountSet(const AccountInfo& primary_account_info) override;
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const AccountInfo& previous_primary_account_info) override;
   void OnPrimaryAccountSigninFailed(
diff --git a/services/identity/public/objc/identity_manager_observer_bridge.mm b/services/identity/public/objc/identity_manager_observer_bridge.mm
index f80a3ad..d26af7b9 100644
--- a/services/identity/public/objc/identity_manager_observer_bridge.mm
+++ b/services/identity/public/objc/identity_manager_observer_bridge.mm
@@ -22,7 +22,7 @@
 }
 
 void IdentityManagerObserverBridge::OnPrimaryAccountSet(
-    const AccountInfo& primary_account_info) {
+    const CoreAccountInfo& primary_account_info) {
   if ([delegate_ respondsToSelector:@selector(onPrimaryAccountSet:)]) {
     [delegate_ onPrimaryAccountSet:primary_account_info];
   }
diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index 6616065e..160673fb 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -57,6 +57,7 @@
     mojom::URLLoaderFactory* network_loader_factory,
     const base::RepeatingCallback<void(int)>& request_finalizer,
     const OriginAccessList* origin_access_list,
+    const OriginAccessList* factory_bound_origin_access_list,
     PreflightController* preflight_controller)
     : binding_(this, std::move(loader_request)),
       routing_id_(routing_id),
@@ -70,6 +71,7 @@
       request_finalizer_(request_finalizer),
       traffic_annotation_(traffic_annotation),
       origin_access_list_(origin_access_list),
+      factory_bound_origin_access_list_(factory_bound_origin_access_list),
       preflight_controller_(preflight_controller),
       weak_factory_(this) {
   binding_.set_connection_error_handler(base::BindOnce(
@@ -469,9 +471,19 @@
   DCHECK(request_.request_initiator);
 
   // The source origin and destination URL pair may be in the allow list.
-  if (origin_access_list_->IsAllowed(*request_.request_initiator,
-                                     request_.url)) {
-    return;
+  switch (origin_access_list_->CheckAccessState(*request_.request_initiator,
+                                                request_.url)) {
+    case OriginAccessList::AccessState::kAllowed:
+      return;
+    case OriginAccessList::AccessState::kBlocked:
+      break;
+    case OriginAccessList::AccessState::kNotListed:
+      if (factory_bound_origin_access_list_->CheckAccessState(
+              *request_.request_initiator, request_.url) ==
+          OriginAccessList::AccessState::kAllowed) {
+        return;
+      }
+      break;
   }
 
   // When a request is initiated in a unique opaque origin (e.g., in a sandboxed
@@ -526,7 +538,8 @@
   if (request_mode == mojom::FetchRequestMode::kNoCors) {
     if (tainted_origin ||
         (!origin->IsSameOriginWith(url::Origin::Create(url)) &&
-         !origin_access_list->IsAllowed(*origin, url))) {
+         origin_access_list->CheckAccessState(*origin, url) !=
+             OriginAccessList::AccessState::kAllowed)) {
       return mojom::FetchResponseType::kOpaque;
     }
   }
diff --git a/services/network/cors/cors_url_loader.h b/services/network/cors/cors_url_loader.h
index 84ca043d..bfed966 100644
--- a/services/network/cors/cors_url_loader.h
+++ b/services/network/cors/cors_url_loader.h
@@ -50,6 +50,7 @@
       mojom::URLLoaderFactory* network_loader_factory,
       const base::RepeatingCallback<void(int)>& request_finalizer,
       const OriginAccessList* origin_access_list,
+      const OriginAccessList* factory_bound_origin_access_list,
       PreflightController* preflight_controller);
 
   ~CorsURLLoader() override;
@@ -175,6 +176,7 @@
 
   // Outlives |this|.
   const OriginAccessList* const origin_access_list_;
+  const OriginAccessList* const factory_bound_origin_access_list_;
   PreflightController* preflight_controller_;
 
   // Used to run asynchronous class instance bound callbacks safely.
diff --git a/services/network/cors/cors_url_loader_factory.cc b/services/network/cors/cors_url_loader_factory.cc
index 683e269d..c02b285 100644
--- a/services/network/cors/cors_url_loader_factory.cc
+++ b/services/network/cors/cors_url_loader_factory.cc
@@ -25,18 +25,28 @@
     mojom::URLLoaderFactoryParamsPtr params,
     scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
     mojom::URLLoaderFactoryRequest request,
-    const OriginAccessList* origin_access_list)
+    const OriginAccessList* origin_access_list,
+    std::unique_ptr<mojom::URLLoaderFactory> network_loader_factory_for_testing)
     : context_(context),
       disable_web_security_(params->disable_web_security),
       process_id_(params->process_id),
-      network_loader_factory_(std::make_unique<network::URLLoaderFactory>(
-          context,
-          std::move(params),
-          std::move(resource_scheduler_client),
-          this)),
       origin_access_list_(origin_access_list) {
   DCHECK(context_);
   DCHECK(origin_access_list_);
+  factory_bound_origin_access_list_ = std::make_unique<OriginAccessList>();
+  if (params->factory_bound_allow_patterns.size()) {
+    DCHECK(params->request_initiator_site_lock);
+    factory_bound_origin_access_list_->SetAllowListForOrigin(
+        *params->request_initiator_site_lock,
+        params->factory_bound_allow_patterns);
+  }
+  network_loader_factory_ =
+      network_loader_factory_for_testing
+          ? std::move(network_loader_factory_for_testing)
+          : std::make_unique<network::URLLoaderFactory>(
+                context, std::move(params),
+                std::move(resource_scheduler_client), this);
+
   bindings_.AddBinding(this, std::move(request));
   bindings_.set_connection_error_handler(base::BindRepeating(
       &CorsURLLoaderFactory::DeleteIfNeeded, base::Unretained(this)));
@@ -55,6 +65,7 @@
       preflight_finalizer_(preflight_finalizer),
       origin_access_list_(origin_access_list) {
   DCHECK(origin_access_list_);
+  factory_bound_origin_access_list_ = std::make_unique<OriginAccessList>();
   // Ideally this should be per-profile, but per-factory would be enough for
   // this code path that is eventually removed.
   owned_preflight_controller_ = std::make_unique<PreflightController>();
@@ -88,7 +99,7 @@
     const ResourceRequest& resource_request,
     mojom::URLLoaderClientPtr client,
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
-  if (!IsSane(resource_request)) {
+  if (!IsSane(context_, resource_request)) {
     client->OnComplete(URLLoaderCompletionStatus(net::ERR_INVALID_ARGUMENT));
     return;
   }
@@ -101,7 +112,8 @@
                        base::Unretained(this)),
         resource_request, std::move(client), traffic_annotation,
         network_loader_factory_.get(), preflight_finalizer_,
-        origin_access_list_, preflight_controller_);
+        origin_access_list_, factory_bound_origin_access_list_.get(),
+        preflight_controller_);
     auto* raw_loader = loader.get();
     OnLoaderCreated(std::move(loader));
     raw_loader->Start();
@@ -128,7 +140,8 @@
     context_->DestroyURLLoaderFactory(this);
 }
 
-bool CorsURLLoaderFactory::IsSane(const ResourceRequest& request) {
+bool CorsURLLoaderFactory::IsSane(const NetworkContext* context,
+                                  const ResourceRequest& request) {
   // CORS needs a proper origin (including a unique opaque origin). If the
   // request doesn't have one, CORS cannot work.
   if (!request.request_initiator &&
@@ -150,6 +163,21 @@
     return false;
   }
 
+  if (context) {
+    net::HttpRequestHeaders::Iterator header_iterator(
+        request.cors_exempt_headers);
+    const auto& allowed_exempt_headers = context->cors_exempt_header_list();
+    while (header_iterator.GetNext()) {
+      if (allowed_exempt_headers.find(header_iterator.name()) !=
+          allowed_exempt_headers.end()) {
+        continue;
+      }
+      LOG(WARNING) << "|cors_exempt_headers| contains unexpected key: "
+                   << header_iterator.name();
+      return false;
+    }
+  }
+
   // TODO(yhirano): If the request mode is "no-cors", the redirect mode should
   // be "follow".
   return true;
diff --git a/services/network/cors/cors_url_loader_factory.h b/services/network/cors/cors_url_loader_factory.h
index a79eb7f..a072867 100644
--- a/services/network/cors/cors_url_loader_factory.h
+++ b/services/network/cors/cors_url_loader_factory.h
@@ -34,13 +34,17 @@
     : public mojom::URLLoaderFactory {
  public:
   // |origin_access_list| should always outlive this factory instance.
-  // Used by network::NetworkContext.
+  // Used by network::NetworkContext. |network_loader_factory_for_testing|
+  // should be nullptr unless you need to overwrite the default factory for
+  // testing.
   CorsURLLoaderFactory(
       NetworkContext* context,
       mojom::URLLoaderFactoryParamsPtr params,
       scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
       mojom::URLLoaderFactoryRequest request,
-      const OriginAccessList* origin_access_list);
+      const OriginAccessList* origin_access_list,
+      std::unique_ptr<mojom::URLLoaderFactory>
+          network_loader_factory_for_testing);
   // Used by content::ResourceMessageFilter.
   // TODO(yhirano): Remove this once when the network service is fully enabled.
   CorsURLLoaderFactory(
@@ -72,7 +76,8 @@
 
   void DeleteIfNeeded();
 
-  static bool IsSane(const ResourceRequest& request);
+  static bool IsSane(const NetworkContext* context,
+                     const ResourceRequest& request);
 
   mojo::BindingSet<mojom::URLLoaderFactory> bindings_;
 
@@ -99,6 +104,10 @@
   // it's safe.
   const OriginAccessList* const origin_access_list_;
 
+  // Owns factory bound OriginAccessList that to have factory specific
+  // additional allowed access list.
+  std::unique_ptr<OriginAccessList> factory_bound_origin_access_list_;
+
   // Usually |preflight_controoler_| is owned by NetworkContext, but we create
   // own one if NetworkContext is not provided, e.g. for legacy code path.
   // TODO(toyoshim): Remove owned controller once the network service is fully
diff --git a/services/network/cors/cors_url_loader_factory_unittest.cc b/services/network/cors/cors_url_loader_factory_unittest.cc
index 869c71b..08d944e3 100644
--- a/services/network/cors/cors_url_loader_factory_unittest.cc
+++ b/services/network/cors/cors_url_loader_factory_unittest.cc
@@ -72,7 +72,8 @@
     cors_url_loader_factory_ = std::make_unique<CorsURLLoaderFactory>(
         network_context_.get(), std::move(factory_params),
         resource_scheduler_client,
-        mojo::MakeRequest(&cors_url_loader_factory_ptr_), &origin_access_list_);
+        mojo::MakeRequest(&cors_url_loader_factory_ptr_), &origin_access_list_,
+        nullptr);
   }
 
   void CreateLoaderAndStart(const ResourceRequest& request) {
diff --git a/services/network/cors/cors_url_loader_unittest.cc b/services/network/cors/cors_url_loader_unittest.cc
index be4cfcd..1dc5d3a 100644
--- a/services/network/cors/cors_url_loader_unittest.cc
+++ b/services/network/cors/cors_url_loader_unittest.cc
@@ -4,23 +4,35 @@
 
 #include "services/network/cors/cors_url_loader.h"
 
+#include <utility>
+#include <vector>
+
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_builder.h"
 #include "services/network/cors/cors_url_loader_factory.h"
+#include "services/network/network_context.h"
+#include "services/network/network_service.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/mojom/cors.mojom.h"
+#include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "services/network/resource_scheduler.h"
+#include "services/network/resource_scheduler_client.h"
 #include "services/network/test/test_url_loader_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -118,19 +130,31 @@
  public:
   using ReferrerPolicy = net::URLRequest::ReferrerPolicy;
 
-  CorsURLLoaderTest() {
-    std::unique_ptr<TestURLLoaderFactory> factory =
-        std::make_unique<TestURLLoaderFactory>();
-    test_url_loader_factory_ = factory->GetWeakPtr();
-    cors_url_loader_factory_ = std::make_unique<CorsURLLoaderFactory>(
-        false, std::move(factory), base::RepeatingCallback<void(int)>(),
-        &origin_access_list_, 0);
+  CorsURLLoaderTest()
+      : scoped_task_environment_(
+            base::test::ScopedTaskEnvironment::MainThreadType::IO),
+        resource_scheduler_(true) {
+    net::URLRequestContextBuilder context_builder;
+    context_builder.set_proxy_resolution_service(
+        net::ProxyResolutionService::CreateDirect());
+    url_request_context_ = context_builder.Build();
   }
 
  protected:
   // testing::Test implementation.
   void SetUp() override {
     feature_list_.InitAndEnableFeature(features::kOutOfBlinkCors);
+
+    network_service_ = NetworkService::CreateForTesting();
+
+    auto context_params = mojom::NetworkContextParams::New();
+    context_params->initial_proxy_config =
+        net::ProxyConfigWithAnnotation::CreateDirect();
+    network_context_ = std::make_unique<NetworkContext>(
+        network_service_.get(), mojo::MakeRequest(&network_context_ptr_),
+        std::move(context_params));
+
+    ResetFactory(base::nullopt);
   }
 
   void CreateLoaderAndStart(const GURL& origin,
@@ -237,6 +261,26 @@
         mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
   }
 
+  void AddBlockListEntryForOrigin(const url::Origin& source_origin,
+                                  const std::string& protocol,
+                                  const std::string& domain,
+                                  const mojom::CorsOriginAccessMatchMode mode) {
+    origin_access_list_.AddBlockListEntryForOrigin(
+        source_origin, protocol, domain, mode,
+        mojom::CorsOriginAccessMatchPriority::kHighPriority);
+  }
+
+  void AddFactoryBoundAllowListEntryForOrigin(
+      const url::Origin& source_origin,
+      const std::string& protocol,
+      const std::string& domain,
+      const mojom::CorsOriginAccessMatchMode mode) {
+    factory_bound_allow_patterns_.push_back(mojom::CorsOriginPattern::New(
+        protocol, domain, mode,
+        mojom::CorsOriginAccessMatchPriority::kDefaultPriority));
+    ResetFactory(source_origin);
+  }
+
   static net::RedirectInfo CreateRedirectInfo(
       int status_code,
       base::StringPiece method,
@@ -253,14 +297,50 @@
   }
 
  private:
-  // Be the first member so it is destroyed last.
-  base::MessageLoop message_loop_;
+  void ResetFactory(base::Optional<url::Origin> initiator) {
+    std::unique_ptr<TestURLLoaderFactory> factory =
+        std::make_unique<TestURLLoaderFactory>();
+    test_url_loader_factory_ = factory->GetWeakPtr();
+
+    auto factory_params = network::mojom::URLLoaderFactoryParams::New();
+    if (initiator) {
+      factory_params->request_initiator_site_lock = *initiator;
+      std::vector<network::mojom::CorsOriginPatternPtr> cloned_patterns;
+      for (const auto& item : factory_bound_allow_patterns_)
+        cloned_patterns.push_back(item.Clone());
+      factory_params->factory_bound_allow_patterns = std::move(cloned_patterns);
+    }
+    constexpr int kProcessId = 573;
+    factory_params->process_id = kProcessId;
+    constexpr int kRouteId = 765;
+    auto resource_scheduler_client =
+        base::MakeRefCounted<ResourceSchedulerClient>(
+            kProcessId, kRouteId, &resource_scheduler_,
+            url_request_context_->network_quality_estimator());
+    cors_url_loader_factory_ = std::make_unique<CorsURLLoaderFactory>(
+        network_context_.get(), std::move(factory_params),
+        resource_scheduler_client,
+        mojo::MakeRequest(&cors_url_loader_factory_ptr_), &origin_access_list_,
+        std::move(factory));
+  }
 
   // Testing instance to enable kOutOfBlinkCors feature.
   base::test::ScopedFeatureList feature_list_;
 
+  // Test environment.
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  std::unique_ptr<net::URLRequestContext> url_request_context_;
+  ResourceScheduler resource_scheduler_;
+  std::unique_ptr<NetworkService> network_service_;
+  std::unique_ptr<NetworkContext> network_context_;
+  mojom::NetworkContextPtr network_context_ptr_;
+
   // CorsURLLoaderFactory instance under tests.
   std::unique_ptr<mojom::URLLoaderFactory> cors_url_loader_factory_;
+  mojom::URLLoaderFactoryPtr cors_url_loader_factory_ptr_;
+
+  // Factory bound origin access list for testing.
+  std::vector<mojom::CorsOriginPatternPtr> factory_bound_allow_patterns_;
 
   // TestURLLoaderFactory instance owned by CorsURLLoaderFactory.
   base::WeakPtr<TestURLLoaderFactory> test_url_loader_factory_;
@@ -1007,9 +1087,10 @@
 }
 
 // Tests if OriginAccessList is actually used to decide the cors flag.
-// Does not verify detailed functionalities that are verified in
-// OriginAccessListTest.
-TEST_F(CorsURLLoaderTest, OriginAccessList) {
+// Details for the OriginAccessList behaviors are verified in
+// OriginAccessListTest, but this test intends to verify if CorsURlLoader calls
+// the list properly.
+TEST_F(CorsURLLoaderTest, OriginAccessList_Allowed) {
   const GURL origin("http://example.com");
   const GURL url("http://other.com/foo.png");
 
@@ -1035,6 +1116,83 @@
   EXPECT_EQ(net::OK, client().completion_status().error_code);
 }
 
+// Check if higher-priority block list wins.
+TEST_F(CorsURLLoaderTest, OriginAccessList_Blocked) {
+  const GURL origin("http://example.com");
+  const GURL url("http://other.com/foo.png");
+
+  AddAllowListEntryForOrigin(
+      url::Origin::Create(origin), url.scheme(), url.host(),
+      mojom::CorsOriginAccessMatchMode::kDisallowSubdomains);
+  AddBlockListEntryForOrigin(
+      url::Origin::Create(origin), url.scheme(), url.host(),
+      mojom::CorsOriginAccessMatchMode::kDisallowSubdomains);
+
+  CreateLoaderAndStart(origin, url, mojom::FetchRequestMode::kCors);
+
+  NotifyLoaderClientOnReceiveResponse();
+
+  RunUntilComplete();
+
+  EXPECT_TRUE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::ERR_FAILED, client().completion_status().error_code);
+}
+
+// CorsURLLoader manages two lists, per-NetworkContext list and
+// per-URLLoaderFactory list. This test verifies if per-URLLoaderFactory list
+// works.
+TEST_F(CorsURLLoaderTest, OriginAccessList_AllowedByFactoryList) {
+  const GURL origin("http://example.com");
+  const GURL url("http://other.com/foo.png");
+
+  AddFactoryBoundAllowListEntryForOrigin(
+      url::Origin::Create(origin), url.scheme(), url.host(),
+      mojom::CorsOriginAccessMatchMode::kDisallowSubdomains);
+
+  CreateLoaderAndStart(origin, url, mojom::FetchRequestMode::kCors);
+
+  NotifyLoaderClientOnReceiveResponse();
+  NotifyLoaderClientOnComplete(net::OK);
+
+  RunUntilComplete();
+
+  EXPECT_TRUE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_TRUE(client().has_received_response());
+  EXPECT_EQ(network::mojom::FetchResponseType::kBasic,
+            client().response_head().response_type);
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::OK, client().completion_status().error_code);
+}
+
+// Checks if CorsURLLoader can respect the per-NetworkContext block list.
+TEST_F(CorsURLLoaderTest, OriginAccessList_AllowedByFactoryListButBlocked) {
+  const GURL origin("http://example.com");
+  const GURL url("http://other.com/foo.png");
+
+  AddFactoryBoundAllowListEntryForOrigin(
+      url::Origin::Create(origin), url.scheme(), url.host(),
+      mojom::CorsOriginAccessMatchMode::kDisallowSubdomains);
+  AddBlockListEntryForOrigin(
+      url::Origin::Create(origin), url.scheme(), url.host(),
+      mojom::CorsOriginAccessMatchMode::kDisallowSubdomains);
+
+  CreateLoaderAndStart(origin, url, mojom::FetchRequestMode::kCors);
+
+  NotifyLoaderClientOnReceiveResponse();
+
+  RunUntilComplete();
+
+  EXPECT_TRUE(IsNetworkLoaderStarted());
+  EXPECT_FALSE(client().has_received_redirect());
+  EXPECT_FALSE(client().has_received_response());
+  EXPECT_TRUE(client().has_received_completion());
+  EXPECT_EQ(net::ERR_FAILED, client().completion_status().error_code);
+}
+
 // Tests if OriginAccessList is actually used to decide response tainting.
 TEST_F(CorsURLLoaderTest, OriginAccessList_NoCors) {
   const GURL origin("http://example.com");
diff --git a/services/network/cors/preflight_controller.cc b/services/network/cors/preflight_controller.cc
index af0e3d37..6ea7305 100644
--- a/services/network/cors/preflight_controller.cc
+++ b/services/network/cors/preflight_controller.cc
@@ -125,8 +125,9 @@
     base::Optional<CorsErrorStatus>* detected_error_status) {
   DCHECK(detected_error_status);
 
+  const int response_code = head.headers ? head.headers->response_code() : 0;
   *detected_error_status = CheckPreflightAccess(
-      final_url, head.headers->response_code(),
+      final_url, response_code,
       GetHeaderString(head.headers, header_names::kAccessControlAllowOrigin),
       GetHeaderString(head.headers,
                       header_names::kAccessControlAllowCredentials),
@@ -136,7 +137,7 @@
     return nullptr;
 
   base::Optional<mojom::CorsError> error;
-  error = CheckPreflight(head.headers->response_code());
+  error = CheckPreflight(response_code);
   if (error) {
     *detected_error_status = CorsErrorStatus(*error);
     return nullptr;
@@ -377,6 +378,18 @@
   return CreatePreflightRequest(request, tainted);
 }
 
+// static
+std::unique_ptr<PreflightResult>
+PreflightController::CreatePreflightResultForTesting(
+    const GURL& final_url,
+    const ResourceResponseHead& head,
+    const ResourceRequest& original_request,
+    bool tainted,
+    base::Optional<CorsErrorStatus>* detected_error_status) {
+  return CreatePreflightResult(final_url, head, original_request, tainted,
+                               detected_error_status);
+}
+
 PreflightController::PreflightController() = default;
 
 PreflightController::~PreflightController() = default;
diff --git a/services/network/cors/preflight_controller.h b/services/network/cors/preflight_controller.h
index defe11b..347ddb9 100644
--- a/services/network/cors/preflight_controller.h
+++ b/services/network/cors/preflight_controller.h
@@ -43,6 +43,13 @@
   static std::unique_ptr<ResourceRequest> CreatePreflightRequestForTesting(
       const ResourceRequest& request,
       bool tainted = false);
+  // Creates a PreflightResult for a specified response parameters for testing.
+  static std::unique_ptr<PreflightResult> CreatePreflightResultForTesting(
+      const GURL& final_url,
+      const ResourceResponseHead& head,
+      const ResourceRequest& original_request,
+      bool tainted,
+      base::Optional<CorsErrorStatus>* detected_error_status);
 
   PreflightController();
   ~PreflightController();
diff --git a/services/network/cors/preflight_controller_unittest.cc b/services/network/cors/preflight_controller_unittest.cc
index 78ac635..65c21c6 100644
--- a/services/network/cors/preflight_controller_unittest.cc
+++ b/services/network/cors/preflight_controller_unittest.cc
@@ -352,6 +352,24 @@
   EXPECT_EQ(1u, access_count());
 }
 
+TEST_F(PreflightControllerTest, CheckResponseWithNullHeaders) {
+  GURL url = GURL("https://google.com/finullurl");
+  const ResourceResponseHead response_head;
+  ResourceRequest request;
+  request.url = url;
+  request.request_initiator = url::Origin::Create(request.url);
+  const bool tainted = false;
+  base::Optional<CorsErrorStatus> detected_error_status;
+
+  EXPECT_FALSE(response_head.headers);
+
+  std::unique_ptr<PreflightResult> result =
+      PreflightController::CreatePreflightResultForTesting(
+          url, response_head, request, tainted, &detected_error_status);
+
+  EXPECT_FALSE(result);
+}
+
 }  // namespace
 
 }  // namespace cors
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index afe74a3..e3e8b02 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -549,7 +549,7 @@
   resource_scheduler_ =
       std::make_unique<ResourceScheduler>(enable_resource_scheduler_);
 
-  InitializeCorsOriginAccessList();
+  InitializeCorsParams();
 }
 
 // TODO(mmenke): Share URLRequestContextBulder configuration between two
@@ -578,7 +578,7 @@
   resource_scheduler_ =
       std::make_unique<ResourceScheduler>(enable_resource_scheduler_);
 
-  InitializeCorsOriginAccessList();
+  InitializeCorsParams();
 }
 
 NetworkContext::NetworkContext(NetworkService* network_service,
@@ -687,7 +687,7 @@
     scoped_refptr<ResourceSchedulerClient> resource_scheduler_client) {
   url_loader_factories_.emplace(std::make_unique<cors::CorsURLLoaderFactory>(
       this, std::move(params), std::move(resource_scheduler_client),
-      std::move(request), &cors_origin_access_list_));
+      std::move(request), &cors_origin_access_list_, nullptr));
 }
 
 void NetworkContext::SetClient(mojom::NetworkContextClientPtr client) {
@@ -2273,7 +2273,7 @@
 }
 #endif
 
-void NetworkContext::InitializeCorsOriginAccessList() {
+void NetworkContext::InitializeCorsParams() {
   for (const auto& pattern : params_->cors_origin_access_list) {
     url::Origin origin = url::Origin::Create(GURL(pattern->source_origin));
     cors_origin_access_list_.SetAllowListForOrigin(origin,
@@ -2281,6 +2281,8 @@
     cors_origin_access_list_.SetBlockListForOrigin(origin,
                                                    pattern->block_patterns);
   }
+  for (const auto& key : params_->cors_exempt_header_list)
+    cors_exempt_header_list_.insert(key);
 }
 
 }  // namespace network
diff --git a/services/network/network_context.h b/services/network/network_context.h
index f435890..d180429 100644
--- a/services/network/network_context.h
+++ b/services/network/network_context.h
@@ -11,6 +11,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <unordered_set>
 #include <utility>
 #include <vector>
 
@@ -151,6 +152,10 @@
 
   CookieManager* cookie_manager() { return cookie_manager_.get(); }
 
+  const std::unordered_set<std::string>& cors_exempt_header_list() const {
+    return cors_exempt_header_list_;
+  }
+
 #if defined(OS_ANDROID)
   base::android::ApplicationStatusListener* app_status_listener() const {
     return app_status_listener_.get();
@@ -436,7 +441,7 @@
   void OnSetExpectCTTestReportFailure();
 #endif  // BUILDFLAG(IS_CT_SUPPORTED)
 
-  void InitializeCorsOriginAccessList();
+  void InitializeCorsParams();
 
   NetworkService* const network_service_;
 
@@ -571,6 +576,10 @@
   // Manages allowed origin access lists.
   cors::OriginAccessList cors_origin_access_list_;
 
+  // Manages header keys that are allowed to be used in
+  // ResourceRequest::cors_exempt_headers.
+  std::unordered_set<std::string> cors_exempt_header_list_;
+
   // Manages CORS preflight requests and its cache.
   cors::PreflightController cors_preflight_controller_;
 
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 9c7114f..b0c52273 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -4277,27 +4277,42 @@
   }
 }
 
-class TestHeaderClient : public mojom::TrustedURLLoaderHeaderClient {
+class TestURLLoaderHeaderClient : public mojom::TrustedURLLoaderHeaderClient {
  public:
+  class TestHeaderClient : public mojom::TrustedHeaderClient {
+   public:
+    TestHeaderClient() : binding(this) {}
+
+    // network::mojom::TrustedHeaderClient:
+    void OnBeforeSendHeaders(const net::HttpRequestHeaders& headers,
+                             OnBeforeSendHeadersCallback callback) override {
+      auto new_headers = headers;
+      new_headers.SetHeader("foo", "bar");
+      std::move(callback).Run(on_before_send_headers_result, new_headers);
+    }
+    void OnHeadersReceived(const std::string& headers,
+                           OnHeadersReceivedCallback callback) override {
+      auto new_headers =
+          base::MakeRefCounted<net::HttpResponseHeaders>(headers);
+      new_headers->AddHeader("baz: qux");
+      std::move(callback).Run(on_headers_received_result,
+                              new_headers->raw_headers(), GURL());
+    }
+
+    int on_before_send_headers_result = net::OK;
+    int on_headers_received_result = net::OK;
+    mojo::Binding<mojom::TrustedHeaderClient> binding;
+  };
+
   // network::mojom::TrustedURLLoaderHeaderClient:
-  void OnBeforeSendHeaders(int32_t request_id,
-                           const net::HttpRequestHeaders& headers,
-                           OnBeforeSendHeadersCallback callback) override {
-    auto new_headers = headers;
-    new_headers.SetHeader("foo", "bar");
-    std::move(callback).Run(on_before_send_headers_result, new_headers);
-  }
-  void OnHeadersReceived(int32_t request_id,
-                         const std::string& headers,
-                         OnHeadersReceivedCallback callback) override {
-    auto new_headers = base::MakeRefCounted<net::HttpResponseHeaders>(headers);
-    new_headers->AddHeader("baz: qux");
-    std::move(callback).Run(on_headers_received_result,
-                            new_headers->raw_headers(), GURL());
+  void OnLoaderCreated(
+      int32_t request_id,
+      network::mojom::TrustedHeaderClientRequest request) override {
+    header_client.binding.Close();
+    header_client.binding.Bind(std::move(request));
   }
 
-  int on_before_send_headers_result = net::OK;
-  int on_headers_received_result = net::OK;
+  TestHeaderClient header_client;
 };
 
 TEST_F(NetworkContextTest, HeaderClientModifiesHeaders) {
@@ -4316,7 +4331,7 @@
       mojom::URLLoaderFactoryParams::New();
   params->process_id = mojom::kBrowserProcessId;
   params->is_corb_enabled = false;
-  mojo::MakeStrongBinding(std::make_unique<TestHeaderClient>(),
+  mojo::MakeStrongBinding(std::make_unique<TestURLLoaderHeaderClient>(),
                           mojo::MakeRequest(&params->header_client));
   network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
                                           std::move(params));
@@ -4378,7 +4393,7 @@
   ResourceRequest request;
   request.url = test_server.GetURL("/echo");
 
-  auto header_client = std::make_unique<TestHeaderClient>();
+  auto header_client = std::make_unique<TestURLLoaderHeaderClient>();
   auto* raw_header_client = header_client.get();
 
   mojom::URLLoaderFactoryPtr loader_factory;
@@ -4393,7 +4408,8 @@
 
   // First, fail request on OnBeforeSendHeaders.
   {
-    raw_header_client->on_before_send_headers_result = net::ERR_FAILED;
+    raw_header_client->header_client.on_before_send_headers_result =
+        net::ERR_FAILED;
     mojom::URLLoaderPtr loader;
     TestURLLoaderClient client;
     loader_factory->CreateLoaderAndStart(
@@ -4408,8 +4424,9 @@
 
   // Next, fail request on OnHeadersReceived.
   {
-    raw_header_client->on_before_send_headers_result = net::OK;
-    raw_header_client->on_headers_received_result = net::ERR_FAILED;
+    raw_header_client->header_client.on_before_send_headers_result = net::OK;
+    raw_header_client->header_client.on_headers_received_result =
+        net::ERR_FAILED;
     mojom::URLLoaderPtr loader;
     TestURLLoaderClient client;
     loader_factory->CreateLoaderAndStart(
diff --git a/services/network/public/cpp/cors/origin_access_list.cc b/services/network/public/cpp/cors/origin_access_list.cc
index 6a30263..fb6c24b 100644
--- a/services/network/public/cpp/cors/origin_access_list.cc
+++ b/services/network/public/cpp/cors/origin_access_list.cc
@@ -67,25 +67,36 @@
   block_list_.clear();
 }
 
-bool OriginAccessList::IsAllowed(const url::Origin& source_origin,
-                                 const GURL& destination) const {
+OriginAccessList::AccessState OriginAccessList::CheckAccessState(
+    const url::Origin& source_origin,
+    const GURL& destination) const {
   if (source_origin.opaque())
-    return false;
+    return AccessState::kBlocked;
+
   std::string source = source_origin.Serialize();
   url::Origin destination_origin = url::Origin::Create(destination);
   network::mojom::CorsOriginAccessMatchPriority allow_list_priority =
       GetHighestPriorityOfRuleForOrigin(source, destination_origin,
                                         allow_list_);
-  if (allow_list_priority ==
-      network::mojom::CorsOriginAccessMatchPriority::kNoMatchingOrigin)
-    return false;
   network::mojom::CorsOriginAccessMatchPriority block_list_priority =
       GetHighestPriorityOfRuleForOrigin(source, destination_origin,
                                         block_list_);
+
+  if (allow_list_priority ==
+      network::mojom::CorsOriginAccessMatchPriority::kNoMatchingOrigin) {
+    return block_list_priority ==
+                   network::mojom::CorsOriginAccessMatchPriority::
+                       kNoMatchingOrigin
+               ? AccessState::kNotListed
+               : AccessState::kBlocked;
+  }
+
   if (block_list_priority ==
       network::mojom::CorsOriginAccessMatchPriority::kNoMatchingOrigin)
-    return true;
-  return allow_list_priority > block_list_priority;
+    return AccessState::kAllowed;
+
+  return (allow_list_priority > block_list_priority) ? AccessState::kAllowed
+                                                     : AccessState::kBlocked;
 }
 
 std::vector<mojo::StructPtr<mojom::CorsOriginAccessPatterns>>
diff --git a/services/network/public/cpp/cors/origin_access_list.h b/services/network/public/cpp/cors/origin_access_list.h
index 9f8f318..90409682 100644
--- a/services/network/public/cpp/cors/origin_access_list.h
+++ b/services/network/public/cpp/cors/origin_access_list.h
@@ -30,6 +30,14 @@
  public:
   using CorsOriginPatternPtr = mojo::InlinedStructPtr<mojom::CorsOriginPattern>;
 
+  // Represents if a queried conditions are is allowed, blocked, or not listed
+  // in the access list.
+  enum class AccessState {
+    kAllowed,
+    kBlocked,
+    kNotListed,
+  };
+
   OriginAccessList();
   ~OriginAccessList();
 
@@ -75,10 +83,9 @@
   // Clears the old block list.
   void ClearBlockList();
 
-  // Returns true if |destination| is in the allow list, and not in the block
-  // list of the |source_origin|.
-  bool IsAllowed(const url::Origin& source_origin,
-                 const GURL& destination) const;
+  // Returns |destination|'s AccessState in the list for |source_origin|.
+  AccessState CheckAccessState(const url::Origin& source_origin,
+                               const GURL& destination) const;
 
   // Creates mojom::CorsPriginAccessPatterns instance vector that represents
   // |this| OriginAccessList instance.
diff --git a/services/network/public/cpp/cors/origin_access_list_unittest.cc b/services/network/public/cpp/cors/origin_access_list_unittest.cc
index f64e153..f332518 100644
--- a/services/network/public/cpp/cors/origin_access_list_unittest.cc
+++ b/services/network/public/cpp/cors/origin_access_list_unittest.cc
@@ -52,7 +52,9 @@
     return https_google_origin_;
   }
   bool IsAllowed(const url::Origin& destination_origin) const {
-    return list_.IsAllowed(source_origin_, destination_origin.GetURL());
+    return list_.CheckAccessState(source_origin_,
+                                  destination_origin.GetURL()) ==
+           OriginAccessList::AccessState::kAllowed;
   }
   void SetAllowListEntry(const std::string& protocol,
                          const std::string& host,
diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc
index 5ac1cf9..454d425 100644
--- a/services/network/public/cpp/resource_request.cc
+++ b/services/network/public/cpp/resource_request.cc
@@ -22,8 +22,8 @@
          referrer_policy == request.referrer_policy &&
          is_prerendering == request.is_prerendering &&
          headers.ToString() == request.headers.ToString() &&
-         requested_with_header == request.requested_with_header &&
-         client_data_header == request.client_data_header &&
+         cors_exempt_headers.ToString() ==
+             request.cors_exempt_headers.ToString() &&
          load_flags == request.load_flags &&
          allow_credentials == request.allow_credentials &&
          plugin_child_id == request.plugin_child_id &&
diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h
index 346344d0..11df12af 100644
--- a/services/network/public/cpp/resource_request.h
+++ b/services/network/public/cpp/resource_request.h
@@ -47,8 +47,7 @@
       net::URLRequest::NEVER_CLEAR_REFERRER;
   bool is_prerendering = false;
   net::HttpRequestHeaders headers;
-  std::string requested_with_header;
-  std::string client_data_header;
+  net::HttpRequestHeaders cors_exempt_headers;
   int load_flags = 0;
   bool allow_credentials = true;
   int plugin_child_id = -1;
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc
index f67fb04..be488ca 100644
--- a/services/network/public/cpp/url_request_mojom_traits.cc
+++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -162,8 +162,7 @@
       !data.ReadReferrer(&out->referrer) ||
       !data.ReadReferrerPolicy(&out->referrer_policy) ||
       !data.ReadHeaders(&out->headers) ||
-      !data.ReadRequestedWithHeader(&out->requested_with_header) ||
-      !data.ReadClientDataHeader(&out->client_data_header) ||
+      !data.ReadCorsExemptHeaders(&out->cors_exempt_headers) ||
       !data.ReadPriority(&out->priority) ||
       !data.ReadCorsPreflightPolicy(&out->cors_preflight_policy) ||
       !data.ReadFetchRequestMode(&out->fetch_request_mode) ||
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h
index 20dd802..276e03b4 100644
--- a/services/network/public/cpp/url_request_mojom_traits.h
+++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -85,13 +85,9 @@
       const network::ResourceRequest& request) {
     return request.headers;
   }
-  static const std::string& requested_with_header(
+  static const net::HttpRequestHeaders& cors_exempt_headers(
       const network::ResourceRequest& request) {
-    return request.requested_with_header;
-  }
-  static const std::string& client_data_header(
-      const network::ResourceRequest& request) {
-    return request.client_data_header;
+    return request.cors_exempt_headers;
   }
   static int32_t load_flags(const network::ResourceRequest& request) {
     return request.load_flags;
diff --git a/services/network/public/cpp/url_request_mojom_traits_unittest.cc b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
index 246a2c3..bd41643 100644
--- a/services/network/public/cpp/url_request_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/url_request_mojom_traits_unittest.cc
@@ -57,8 +57,7 @@
       net::URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN;
   original.is_prerendering = false;
   original.headers.SetHeader("Accept", "text/xml");
-  original.requested_with_header = "dummy_requested_with_header";
-  original.client_data_header = "dummy_client_data_header";
+  original.cors_exempt_headers.SetHeader("X-Requested-With", "ForTesting");
   original.load_flags = 3;
   original.allow_credentials = true;
   original.plugin_child_id = 5;
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 4a7ed8ae..5dbaadd4 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -102,22 +102,29 @@
   array<X509Certificate> trust_anchors;
 };
 
-// Interface to allow modifying the full request and response headers. This
-// interface exposes sensitive headers such as set-cookie, so should only be
-// sent to trusted processes.
-interface TrustedURLLoaderHeaderClient {
+// Sent by TrustedURLLoaderHeaderClient to perform modifications for a request.
+interface TrustedHeaderClient {
   // Allows modifying request headers before the request is sent.
-  OnBeforeSendHeaders(int32 request_id, HttpRequestHeaders headers) =>
+  OnBeforeSendHeaders(HttpRequestHeaders headers) =>
       (int32 result, HttpRequestHeaders? headers);
 
   // Allows modifying response headers, including sensitive headers such as
   // set-cookie. This should only be used from a trusted process.
-  OnHeadersReceived(int32 request_id, string headers) =>
+  OnHeadersReceived(string headers) =>
       (int32 result,
        string? headers,
        url.mojom.Url allowed_unsafe_redirect_url);
 };
 
+// Interface to allow modifying the full request and response headers. This
+// interface exposes sensitive headers such as set-cookie, so should only be
+// sent to trusted processes.
+interface TrustedURLLoaderHeaderClient {
+  // When a new URLLoader is created, this will be called to pass a
+  // corresponding |header_client|.
+  OnLoaderCreated(int32 request_id, TrustedHeaderClient& header_client);
+};
+
 // Parameters for constructing a network context.
 struct NetworkContextParams {
   // Name used by memory tools to identify the context.
@@ -333,6 +340,10 @@
   // Specifies the initial set of allowed and blocked origins for the
   // URLLoaderFactory consumers to access beyond the same-origin-policy.
   array<CorsOriginAccessPatterns> cors_origin_access_list;
+
+  // Specifies header keys that are allowed to be used in
+  // network::url_request.cors_exempt_headers.
+  array<string> cors_exempt_header_list;
 };
 
 struct NetworkConditions {
@@ -428,6 +439,14 @@
   // headers and Cookie response headers to be modified. This has a performance
   // impact because of the extra process hops, so use should be minimized.
   TrustedURLLoaderHeaderClient? header_client;
+
+  // If non-empty array is given, |factory_bound_allow_patterns| is used for
+  // CORS checks in addition to the per-context allow patterns that is managed
+  // via NetworkContext interface. This still respects the per-context block
+  // lists. To use this feature, caller should set |request_initiator_site_lock|
+  // in order to bind the allow patterns to the factory specific initiator
+  // origin.
+  array<CorsOriginPattern> factory_bound_allow_patterns;
 };
 
 // Callback interface for NetworkContext when routing identifiers aren't
diff --git a/services/network/public/mojom/url_loader.mojom b/services/network/public/mojom/url_loader.mojom
index 049da805..a269edda 100644
--- a/services/network/public/mojom/url_loader.mojom
+++ b/services/network/public/mojom/url_loader.mojom
@@ -118,19 +118,21 @@
   // Additional HTTP request headers.
   HttpRequestHeaders headers;
 
-  // 'X-Requested-With' header value. Some consumers want to set this header,
-  // but such internal headers must be ignored by CORS checks (which run inside
-  // Network Service), so the value is stored here (rather than in |headers|)
-  // and later populated in the headers after CORS check.
-  // TODO(toyoshim): Remove it once PPAPI is deprecated.
-  string requested_with_header;
-
-  // 'X-Client-Data' header value. See comments for |requested_with_header|
-  // above, too.
-  // TODO(toyoshim): Consider to rename this to have a chrome specific prefix
-  // such as 'Chrome-' instead of 'X-', and to add 'Chrome-' prefixed header
-  // names into the forbidden header name list.
-  string client_data_header;
+  // HTTP request headers that has been internally added. Some consumers want to
+  // set additional HTTP headers, but such internal headers must be ignored by
+  // CORS check (which run inside Network Service), so the values are stored
+  // here (rather than in |headers|) and later merged into the |headers| after
+  // CORS check.
+  //
+  // *Warning*: Adding new headers to this list is strongly discouraged. What
+  // usually you need is to update the fetch spec, and add your custom headers
+  // to the CORS-safelisted header so to pass proper CORS checks. 'Proxy-' or
+  // 'Sec-' prefixes are also available. See cors::IsCORSSafelistedHeader and
+  // cors::IsForbiddenHeader for details.
+  //
+  // You should ask Loading and CORS OWNERS when you need to add your own
+  // headers to the list.
+  HttpRequestHeaders cors_exempt_headers;  // See Note above before using.
 
   // net::URLRequest load flags.
   int32 load_flags;
diff --git a/services/network/test/test_cookie_manager.cc b/services/network/test/test_cookie_manager.cc
index 9f758da..782c596 100644
--- a/services/network/test/test_cookie_manager.cc
+++ b/services/network/test/test_cookie_manager.cc
@@ -27,4 +27,12 @@
   cookie_change_listeners_.push_back(std::move(listener));
 }
 
+void TestCookieManager::DispatchCookieChange(
+    const net::CanonicalCookie& cookie,
+    network::mojom::CookieChangeCause cause) {
+  for (auto& cookie_change_listener_ : cookie_change_listeners_) {
+    cookie_change_listener_->OnCookieChange(cookie, cause);
+  }
+}
+
 }  // namespace network
diff --git a/services/network/test/test_cookie_manager.h b/services/network/test/test_cookie_manager.h
index dc00adb..8d5bc4d 100644
--- a/services/network/test/test_cookie_manager.h
+++ b/services/network/test/test_cookie_manager.h
@@ -47,6 +47,9 @@
   void SetForceKeepSessionState() override {}
   void BlockThirdPartyCookies(bool block) override {}
 
+  void DispatchCookieChange(const net::CanonicalCookie& cookie,
+                            network::mojom::CookieChangeCause cause);
+
  private:
   // List of observers receiving cookie change notifications.
   std::vector<network::mojom::CookieChangeListenerPtr> cookie_change_listeners_;
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index b4f63482..56bfbba 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -309,7 +309,7 @@
     scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
     base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder,
     base::WeakPtr<NetworkUsageAccumulator> network_usage_accumulator,
-    mojom::TrustedURLLoaderHeaderClient* header_client)
+    mojom::TrustedURLLoaderHeaderClient* url_loader_header_client)
     : url_request_context_(url_request_context),
       network_service_client_(network_service_client),
       delete_callback_(std::move(delete_callback)),
@@ -341,7 +341,6 @@
       custom_proxy_use_alternate_proxy_list_(
           request.custom_proxy_use_alternate_proxy_list),
       fetch_window_id_(request.fetch_window_id),
-      header_client_(header_client),
       weak_ptr_factory_(this) {
   DCHECK(delete_callback_);
   if (!base::FeatureList::IsEnabled(features::kNetworkService)) {
@@ -353,6 +352,15 @@
         << "disabled, as that skips security checks in ResourceDispatcherHost. "
         << "The only acceptable usage is the browser using SimpleURLLoader.";
   }
+  if (url_loader_header_client &&
+      (options_ & mojom::kURLLoadOptionUseHeaderClient)) {
+    url_loader_header_client->OnLoaderCreated(
+        request_id_, mojo::MakeRequest(&header_client_));
+    // Make sure the loader dies if |header_client_| has an error, otherwise
+    // requests can hang.
+    header_client_.set_connection_error_handler(
+        base::BindOnce(&URLLoader::OnConnectionError, base::Unretained(this)));
+  }
   if (want_raw_headers_) {
     options_ |= mojom::kURLLoadOptionSendSSLInfoWithResponse |
                 mojom::kURLLoadOptionSendSSLInfoForCertificateError;
@@ -367,20 +375,15 @@
   url_request_->set_attach_same_site_cookies(request.attach_same_site_cookies);
   url_request_->SetReferrer(ComputeReferrer(request.referrer));
   url_request_->set_referrer_policy(request.referrer_policy);
-  url_request_->SetExtraRequestHeaders(request.headers);
-  // X-Requested-With and X-Client-Data header must be set here to avoid
-  // breaking CORS checks. They are non-empty when the values are given by the
-  // UA code, therefore they should be ignored by CORS checks.
-  if (!request.requested_with_header.empty()) {
-    url_request_->SetExtraRequestHeaderByName(
-        "X-Requested-With", request.requested_with_header, true);
-  }
-  if (!request.client_data_header.empty()) {
-    url_request_->SetExtraRequestHeaderByName("X-Client-Data",
-                                              request.client_data_header, true);
-  }
   url_request_->set_upgrade_if_insecure(request.upgrade_if_insecure);
 
+  // |cors_excempt_headers| must be merged here to avoid breaking CORS checks.
+  // They are non-empty when the values are given by the UA code, therefore
+  // they should be ignored by CORS checks.
+  net::HttpRequestHeaders merged_headers = request.headers;
+  merged_headers.MergeFrom(request.cors_exempt_headers);
+  url_request_->SetExtraRequestHeaders(merged_headers);
+
   url_request_->SetUserData(kUserDataKey,
                             std::make_unique<UnownedPointer>(this));
 
@@ -993,12 +996,11 @@
 
 int URLLoader::OnBeforeStartTransaction(net::CompletionOnceCallback callback,
                                         net::HttpRequestHeaders* headers) {
-  if (header_client_ && (options_ & mojom::kURLLoadOptionUseHeaderClient)) {
+  if (header_client_) {
     header_client_->OnBeforeSendHeaders(
-        request_id_, *headers,
-        base::BindOnce(&URLLoader::OnBeforeSendHeadersComplete,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback),
-                       headers));
+        *headers, base::BindOnce(&URLLoader::OnBeforeSendHeadersComplete,
+                                 weak_ptr_factory_.GetWeakPtr(),
+                                 std::move(callback), headers));
     return net::ERR_IO_PENDING;
   }
   return net::OK;
@@ -1009,9 +1011,9 @@
     const net::HttpResponseHeaders* original_response_headers,
     scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
     GURL* allowed_unsafe_redirect_url) {
-  if (header_client_ && (options_ & mojom::kURLLoadOptionUseHeaderClient)) {
+  if (header_client_) {
     header_client_->OnHeadersReceived(
-        request_id_, original_response_headers->raw_headers(),
+        original_response_headers->raw_headers(),
         base::BindOnce(&URLLoader::OnHeadersReceivedComplete,
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback),
                        override_response_headers, allowed_unsafe_redirect_url));
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index 6e4ac69..8ab338a 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -69,7 +69,7 @@
       scoped_refptr<ResourceSchedulerClient> resource_scheduler_client,
       base::WeakPtr<KeepaliveStatisticsRecorder> keepalive_statistics_recorder,
       base::WeakPtr<NetworkUsageAccumulator> network_usage_accumulator,
-      mojom::TrustedURLLoaderHeaderClient* header_client);
+      mojom::TrustedURLLoaderHeaderClient* url_loader_header_client);
   ~URLLoader() override;
 
   // mojom::URLLoader implementation:
@@ -319,7 +319,7 @@
   // network::ResourceRequest::fetch_window_id for details.
   base::Optional<base::UnguessableToken> fetch_window_id_;
 
-  mojom::TrustedURLLoaderHeaderClient* header_client_ = nullptr;
+  mojom::TrustedHeaderClientPtr header_client_;
 
   base::WeakPtrFactory<URLLoader> weak_ptr_factory_;
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 0c64fa3..680fde6a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1280,25 +1280,6 @@
             ]
         }
     ],
-    "CompressParkableStringsInBackground": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CompressParkableStringsInBackground"
-                    ]
-                }
-            ]
-        }
-    ],
     "ContextualSearch": [
         {
             "platforms": [
@@ -2598,6 +2579,22 @@
             ]
         }
     ],
+    "MirrorMultilogin": [
+        {
+            "platforms": [
+                "android",
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "UseMultiloginEndpoint"
+                    ]
+                }
+            ]
+        }
+    ],
     "ModernMediaControls": [
         {
             "platforms": [
@@ -2835,7 +2832,9 @@
                 {
                     "name": "Enabled",
                     "enable_features": [
-                        "new-password-form-parsing"
+                        "new-password-form-parsing",
+                        "new-password-form-parsing-for-saving",
+                        "only-new-password-form-parsing"
                     ]
                 }
             ]
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 1c856aa1..9ea16b0 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -2222,6 +2222,8 @@
   kLayoutJankExplicitlyRequested = 2782,
   kMediaSessionSkipAd = 2783,
   kAdFrameSizeIntervention = 2784,
+  kV8UserActivation_HasBeenActive_AttributeGetter = 2785,
+  kV8UserActivation_IsActive_AttributeGetter = 2786,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_url_request.h b/third_party/blink/public/platform/web_url_request.h
index 3aba024..1820058 100644
--- a/third_party/blink/public/platform/web_url_request.h
+++ b/third_party/blink/public/platform/web_url_request.h
@@ -318,12 +318,6 @@
   BLINK_PLATFORM_EXPORT const WebString GetRequestedWithHeader() const;
   BLINK_PLATFORM_EXPORT void SetRequestedWithHeader(const WebString&);
 
-  // Remembers 'X-Client-Data' header value. Blink should not set this header
-  // value until CORS checks are done to avoid running checks even against
-  // headers that are internally set.
-  BLINK_PLATFORM_EXPORT const WebString GetClientDataHeader() const;
-  BLINK_PLATFORM_EXPORT void SetClientDataHeader(const WebString&);
-
   // https://fetch.spec.whatwg.org/#concept-request-window
   // See network::ResourceRequest::fetch_window_id for details.
   BLINK_PLATFORM_EXPORT const base::UnguessableToken& GetFetchWindowId() const;
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 00c5a555..6fa653b 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -684,11 +684,6 @@
   // This function should be called after pairs of PrintBegin() and PrintEnd().
   virtual void DispatchAfterPrintEvent() = 0;
 
-  // If the frame contains a full-frame plugin or the given node refers to a
-  // plugin whose content indicates that printed output should not be scaled,
-  // return true, otherwise return false.
-  virtual bool IsPrintScalingDisabledForPlugin(const WebNode& = WebNode()) = 0;
-
   // Advance the focus of the WebView to next text input element from current
   // input field wrt sequential navigation with TAB or Shift + TAB
   // WebFocusTypeForward simulates TAB and WebFocusTypeBackward simulates
diff --git a/third_party/blink/public/web/web_plugin.h b/third_party/blink/public/web/web_plugin.h
index 017fa466..1088264 100644
--- a/third_party/blink/public/web/web_plugin.h
+++ b/third_party/blink/public/web/web_plugin.h
@@ -138,9 +138,6 @@
   // Whether the plugin supports its own paginated print. The other print
   // interface methods are called only if this method returns true.
   virtual bool SupportsPaginatedPrint() { return false; }
-  // Returns true if the printed content should not be scaled to
-  // the printer's printable area.
-  virtual bool IsPrintScalingDisabled() { return false; }
   // Returns true on success and sets the out parameter to the print preset
   // options for the document.
   virtual bool GetPrintPresetOptionsFromDocument(WebPrintPresetOptions*) {
diff --git a/third_party/blink/renderer/bindings/templates/interface.cc.tmpl b/third_party/blink/renderer/bindings/templates/interface.cc.tmpl
index 069ccbe..a9a54a18 100644
--- a/third_party/blink/renderer/bindings/templates/interface.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/interface.cc.tmpl
@@ -140,7 +140,7 @@
   {{internal_namespace}}::IndexedPropertySetter(index, v8_value, info);
   {% endif %}
 
-  {% elif named_property_setter %}
+  {% elif not indexed_property_getter and named_property_setter %}
 
   const AtomicString& property_name = AtomicString::Number(index);
 
diff --git a/third_party/blink/renderer/core/css/css_variable_data.cc b/third_party/blink/renderer/core/css/css_variable_data.cc
index 9a3089ca..12b25dc 100644
--- a/third_party/blink/renderer/core/css/css_variable_data.cc
+++ b/third_party/blink/renderer/core/css/css_variable_data.cc
@@ -87,7 +87,7 @@
       has_font_units_(false),
       has_root_font_units_(false),
       absolutized_(false),
-      base_url_(base_url),
+      base_url_(base_url.IsValid() ? base_url.GetString() : String()),
       charset_(charset) {
   DCHECK(!range.AtEnd());
   ConsumeAndUpdateTokens(range);
diff --git a/third_party/blink/renderer/core/css/css_variable_data.h b/third_party/blink/renderer/core/css/css_variable_data.h
index dc38000..a630a7d 100644
--- a/third_party/blink/renderer/core/css/css_variable_data.h
+++ b/third_party/blink/renderer/core/css/css_variable_data.h
@@ -74,7 +74,7 @@
   // is required for e.g. registered properties with 'em' units.
   bool IsAbsolutized() const { return absolutized_; }
 
-  const KURL& BaseURL() const { return base_url_; }
+  const String& BaseURL() const { return base_url_; }
 
   const WTF::TextEncoding& Charset() const { return charset_; }
 
@@ -124,7 +124,7 @@
   bool has_font_units_;
   bool has_root_font_units_;
   bool absolutized_;
-  KURL base_url_;
+  String base_url_;
   WTF::TextEncoding charset_;
   DISALLOW_COPY_AND_ASSIGN(CSSVariableData);
 };
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
index 48160c18..02878a9 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.cc
@@ -38,7 +38,7 @@
 
 CSSParserToken ResolveUrl(const CSSParserToken& token,
                           Vector<String>& backing_strings,
-                          const KURL& base_url,
+                          const String& base_url,
                           WTF::TextEncoding charset) {
   DCHECK(token.GetType() == kUrlToken || token.GetType() == kStringToken);
 
@@ -48,14 +48,35 @@
     return token;
 
   String relative_url = string_view.ToString();
-  KURL absolute_url = charset.IsValid() ? KURL(base_url, relative_url, charset)
-                                        : KURL(base_url, relative_url);
+  KURL base = !base_url.IsNull() ? KURL(base_url) : KURL();
+  KURL absolute_url = charset.IsValid() ? KURL(base, relative_url, charset)
+                                        : KURL(base, relative_url);
 
   backing_strings.push_back(absolute_url.GetString());
 
   return token.CopyWithUpdatedString(StringView(backing_strings.back()));
 }
 
+// Rewrites (in-place) kUrlTokens and kFunctionToken/CSSValueUrls to contain
+// absolute URLs.
+void ResolveRelativeUrls(Vector<CSSParserToken>& tokens,
+                         Vector<String>& backing_strings,
+                         const String& base_url,
+                         const WTF::TextEncoding& charset) {
+  CSSParserToken* token = tokens.begin();
+  CSSParserToken* end = tokens.end();
+
+  while (token < end) {
+    if (token->GetType() == kUrlToken) {
+      *token = ResolveUrl(*token, backing_strings, base_url, charset);
+    } else if (token->FunctionId() == CSSValueUrl) {
+      if (token + 1 < end && token[1].GetType() == kStringToken)
+        token[1] = ResolveUrl(token[1], backing_strings, base_url, charset);
+    }
+    ++token;
+  }
+}
+
 // Registered properties need to substitute as absolute values. This means
 // that 'em' units (for instance) are converted to 'px ' and calc()-expressions
 // are resolved. This function creates new tokens equivalent to the computed
@@ -266,25 +287,6 @@
                                cycle_detected);
 }
 
-void CSSVariableResolver::ResolveRelativeUrls(
-    Vector<CSSParserToken>& tokens,
-    Vector<String>& backing_strings,
-    const KURL& base_url,
-    const WTF::TextEncoding& charset) {
-  CSSParserToken* token = tokens.begin();
-  CSSParserToken* end = tokens.end();
-
-  while (token < end) {
-    if (token->GetType() == kUrlToken) {
-      *token = ResolveUrl(*token, backing_strings, base_url, charset);
-    } else if (token->FunctionId() == CSSValueUrl) {
-      if (token + 1 < end && token[1].GetType() == kStringToken)
-        token[1] = ResolveUrl(token[1], backing_strings, base_url, charset);
-    }
-    ++token;
-  }
-}
-
 bool CSSVariableResolver::ShouldResolveRelativeUrls(
     const AtomicString& name,
     const CSSVariableData& variable_data) {
diff --git a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
index a68e4265..4dceb2d 100644
--- a/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
+++ b/third_party/blink/renderer/core/css/resolver/css_variable_resolver.h
@@ -11,10 +11,6 @@
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
 
-namespace WTF {
-class TextEncoding;
-}  // namespace WTF
-
 namespace blink {
 
 class CSSCustomPropertyDeclaration;
@@ -22,7 +18,6 @@
 class CSSPendingSubstitutionValue;
 class CSSVariableData;
 class CSSVariableReferenceValue;
-class KURL;
 class PropertyRegistration;
 class PropertyRegistry;
 class StyleInheritedVariables;
@@ -182,12 +177,6 @@
       CSSVariableData*,
       const Options&,
       bool& cycle_detected);
-  // Rewrites (in-place) kUrlTokens and kFunctionToken/CSSValueUrls to contain
-  // absolute URLs.
-  void ResolveRelativeUrls(Vector<CSSParserToken>& tokens,
-                           Vector<String>& backing_strings,
-                           const KURL& base_url,
-                           const WTF::TextEncoding& charset);
 
   bool ShouldResolveRelativeUrls(const AtomicString& name,
                                  const CSSVariableData&);
diff --git a/third_party/blink/renderer/core/css/style_recalc.cc b/third_party/blink/renderer/core/css/style_recalc.cc
index 921bcfc..757a6a7 100644
--- a/third_party/blink/renderer/core/css/style_recalc.cc
+++ b/third_party/blink/renderer/core/css/style_recalc.cc
@@ -26,6 +26,8 @@
     return true;
   if (node.NeedsStyleRecalc())
     return true;
+  if (node.GetForceReattachLayoutTree())
+    return true;
   if (propagate_ != kClearEnsured)
     return false;
   if (const ComputedStyle* old_style = node.GetComputedStyle())
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index 2282c92..474a8eb 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -514,15 +514,15 @@
   void UpdateStyleAndLayout();
   void LayoutUpdated();
   enum RunPostLayoutTasks {
-    kRunPostLayoutTasksAsyhnchronously,
+    kRunPostLayoutTasksAsynchronously,
     kRunPostLayoutTasksSynchronously,
   };
   void UpdateStyleAndLayoutIgnorePendingStylesheets(
-      RunPostLayoutTasks = kRunPostLayoutTasksAsyhnchronously);
+      RunPostLayoutTasks = kRunPostLayoutTasksAsynchronously);
   // Same as UpdateStyleAndLayoutIgnorePendingStyleSheets()
   // but allows style & layout tree calculation for invisible nodes.
   void UpdateStyleAndLayoutIgnorePendingStylesheetsConsideringInvisibleNodes(
-      RunPostLayoutTasks = kRunPostLayoutTasksAsyhnchronously);
+      RunPostLayoutTasks = kRunPostLayoutTasksAsynchronously);
   void UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(Node*);
   scoped_refptr<ComputedStyle> StyleForElementIgnoringPendingStylesheets(
       Element*);
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.cc b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
index 6c8603c..28d64ae1 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
@@ -257,20 +257,12 @@
   if (start == bfc)
     return;
   DCHECK(bfc) << start;
-  if (std::any_of(blocks_.begin(), blocks_.end(),
-                  [bfc](const LayoutObject* object) {
-                    return bfc == object ||
-                           bfc->GetNode()->IsDescendantOf(object->GetNode());
-                  }))
-    return;
-  auto** itr = std::remove_if(
-      blocks_.begin(), blocks_.end(), [bfc](const LayoutObject* object) {
-        return object->GetNode()->IsDescendantOf(bfc->GetNode());
-      });
-  blocks_.resize(static_cast<wtf_size_t>(std::distance(blocks_.begin(), itr)));
+  Element* bfc_element = ToElement(bfc->GetNode());
+  DCHECK(bfc_element);
   // Mark BFC root is added into the list.
-  bfc->MutableStyle()->SetForceLegacyLayout(true);
-  blocks_.push_back(bfc);
+  // TODO(futhark): We should not mutate the ComputedStyle here.
+  bfc_element->MutableComputedStyle()->SetForceLegacyLayout(true);
+  reattach_elements_.push_back(bfc_element);
 }
 
 bool ReattachLegacyLayoutObjectList::IsCollecting() const {
@@ -283,10 +275,10 @@
   if (state == State::kBuildingLegacyLayoutTree)
     return;
   DCHECK_EQ(state, State::kCollecting);
-  if (blocks_.IsEmpty())
+  if (reattach_elements_.IsEmpty())
     return;
-  for (const LayoutObject* block : blocks_)
-    ToElement(*block->GetNode()).LazyReattachIfAttached();
+  for (Element* element : reattach_elements_)
+    element->SetForceReattachLayoutTree();
   state_ = State::kForcingLegacyLayout;
   document_->GetStyleEngine().RecalcStyle({});
   document_->GetStyleEngine().RebuildLayoutTree();
@@ -295,6 +287,7 @@
 
 void ReattachLegacyLayoutObjectList::Trace(Visitor* visitor) {
   visitor->Trace(document_);
+  visitor->Trace(reattach_elements_);
 }
 
 ReattachLegacyLayoutObjectList& Document::GetReattachLegacyLayoutObjectList() {
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.h b/third_party/blink/renderer/core/dom/layout_tree_builder.h
index 8265951..9345cd5 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder.h
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder.h
@@ -161,9 +161,9 @@
  private:
   Member<Document> document_;
 
-  // A list of block formatting context or layout object associated to
-  // document element.
-  Vector<const LayoutObject*> blocks_;
+  // A list of elements establishing a block formatting context which need to
+  // be re-attached to use legacy fallback.
+  HeapVector<Member<Element>> reattach_elements_;
 
   enum class State {
     kInvalid,
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
index e0ad521..2bddbc7 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.cc
@@ -397,10 +397,6 @@
   return web_plugin_->SupportsPaginatedPrint();
 }
 
-bool WebPluginContainerImpl::IsPrintScalingDisabled() const {
-  return web_plugin_->IsPrintScalingDisabled();
-}
-
 bool WebPluginContainerImpl::GetPrintPresetOptionsFromDocument(
     WebPrintPresetOptions* preset_options) const {
   return web_plugin_->GetPrintPresetOptionsFromDocument(preset_options);
diff --git a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
index 717faa0..e0a9970 100644
--- a/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
+++ b/third_party/blink/renderer/core/exported/web_plugin_container_impl.h
@@ -169,9 +169,6 @@
   // Whether the plugin supports its own paginated print. The other print
   // interface methods are called only if this method returns true.
   bool SupportsPaginatedPrint() const;
-  // If the plugin content should not be scaled to the printable area of
-  // the page, then this method should return true.
-  bool IsPrintScalingDisabled() const;
   // Returns true on success and sets the out parameter to the print preset
   // options for the document.
   bool GetPrintPresetOptionsFromDocument(WebPrintPresetOptions*) const;
diff --git a/third_party/blink/renderer/core/frame/user_activation.idl b/third_party/blink/renderer/core/frame/user_activation.idl
index 24b22ec..f6a9ea0 100644
--- a/third_party/blink/renderer/core/frame/user_activation.idl
+++ b/third_party/blink/renderer/core/frame/user_activation.idl
@@ -5,6 +5,6 @@
 // https://github.com/dtapuska/useractivation
 [RuntimeEnabled=UserActivationAPI]
 interface UserActivation {
-  readonly attribute boolean hasBeenActive;
-  readonly attribute boolean isActive;
+  [Measure] readonly attribute boolean hasBeenActive;
+  [Measure] readonly attribute boolean isActive;
 };
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index a605fe6c..d0b604d9 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1550,18 +1550,6 @@
   print_context_.Clear();
 }
 
-bool WebLocalFrameImpl::IsPrintScalingDisabledForPlugin(const WebNode& node) {
-  DCHECK(GetFrame());
-  WebPluginContainerImpl* plugin_container =
-      node.IsNull() ? GetFrame()->GetWebPluginContainer()
-                    : ToWebPluginContainerImpl(node.PluginContainer());
-
-  if (!plugin_container || !plugin_container->SupportsPaginatedPrint())
-    return false;
-
-  return plugin_container->IsPrintScalingDisabled();
-}
-
 bool WebLocalFrameImpl::GetPrintPresetOptionsForPlugin(
     const WebNode& node,
     WebPrintPresetOptions* preset_options) {
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 985cdbb..eeafc63 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -220,7 +220,6 @@
   float GetPrintPageShrink(int page) override;
   void PrintEnd() override;
   void DispatchAfterPrintEvent() override;
-  bool IsPrintScalingDisabledForPlugin(const WebNode&) override;
   bool GetPrintPresetOptionsForPlugin(const WebNode&,
                                       WebPrintPresetOptions*) override;
   bool HasCustomPageSizeStyle(int page_index) override;
diff --git a/third_party/blink/renderer/core/page/drag_controller.cc b/third_party/blink/renderer/core/page/drag_controller.cc
index 2dfcd7d..3b0b20a 100644
--- a/third_party/blink/renderer/core/page/drag_controller.cc
+++ b/third_party/blink/renderer/core/page/drag_controller.cc
@@ -215,7 +215,6 @@
 void DragController::DragEnded() {
   drag_initiator_ = nullptr;
   did_initiate_drag_ = false;
-  drag_state_ = nullptr;
   page_->GetDragCaret().Clear();
 }
 
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
index fcbbf06..6f39524 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy.h
@@ -56,7 +56,7 @@
 
  private:
   String name_;
-  Member<TrustedTypePolicyOptions> policy_options_;
+  TraceWrapperMember<TrustedTypePolicyOptions> policy_options_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h
index 530da4b..531bf9f 100644
--- a/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h
+++ b/third_party/blink/renderer/core/trustedtypes/trusted_type_policy_factory.h
@@ -57,7 +57,7 @@
   const WrapperTypeInfo* GetWrapperTypeInfoFromScriptValue(ScriptState*,
                                                            const ScriptValue&);
 
-  HeapHashMap<String, Member<TrustedTypePolicy>> policy_map_;
+  HeapHashMap<String, TraceWrapperMember<TrustedTypePolicy>> policy_map_;
 
   bool hadAssignmentError = false;
 };
diff --git a/third_party/blink/renderer/devtools/front_end/elements/ComputedStyleWidget.js b/third_party/blink/renderer/devtools/front_end/elements/ComputedStyleWidget.js
index 729cd112c..5ff492a4 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/ComputedStyleWidget.js
+++ b/third_party/blink/renderer/devtools/front_end/elements/ComputedStyleWidget.js
@@ -181,7 +181,7 @@
       propertyElement.appendChild(propertyNameElement);
 
       const colon = createElementWithClass('span', 'delimeter');
-      colon.textContent = ':';
+      colon.textContent = ': ';
       propertyNameElement.appendChild(colon);
 
       const propertyValueElement = propertyElement.createChild('span', 'property-value');
diff --git a/third_party/blink/renderer/devtools/front_end/elements/computedStyleWidgetTree.css b/third_party/blink/renderer/devtools/front_end/elements/computedStyleWidgetTree.css
index f33a4aec0..ef6371b 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/computedStyleWidgetTree.css
+++ b/third_party/blink/renderer/devtools/front_end/elements/computedStyleWidgetTree.css
@@ -5,27 +5,22 @@
  */
 
 .computed-style-property {
-    display: flex;
     overflow: hidden;
     flex: auto;
+    text-overflow: ellipsis;
 }
 
 .computed-style-property .property-name {
-    min-width: 5em;
+    width: 16em;
     text-overflow: ellipsis;
     overflow: hidden;
-    flex-shrink: 1;
-    flex-basis: 16em;
-    flex-grow: 1;
+    max-width: 55%;
+    display: inline-block;
 }
 
 .computed-style-property .property-value {
     margin-left: 2em;
     position: relative;
-    display: flex;
-    flex-shrink: 0;
-    flex-basis: 5em;
-    flex-grow: 10;
 }
 
 .computed-style-property .property-value-text {
@@ -43,6 +38,7 @@
     display: none;
     position: absolute;
     left: -16px;
+    top: 0;
 }
 
 .goto-source-icon:hover {
diff --git a/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js b/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
index c660186..fdf322c 100644
--- a/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
@@ -163,7 +163,6 @@
 
     let dumpText = '';
     dumpText += treeElement.title.querySelector('.property-name').textContent;
-    dumpText += ' ';
     dumpText += treeElement.title.querySelector('.property-value').textContent;
     TestRunner.addResult(dumpText);
 
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index a3105b5..924033a 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -1064,13 +1064,6 @@
     return;
   }
 
-  if (!details->hasTotal()) {
-    resolver->Reject(
-        DOMException::Create(DOMExceptionCode::kSyntaxError, "Total required"));
-    ClearResolversAndCloseMojoConnection();
-    return;
-  }
-
   PaymentDetailsPtr validated_details =
       payments::mojom::blink::PaymentDetails::New();
   ValidateAndConvertPaymentDetailsUpdate(
diff --git a/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc b/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
index 5c447b8..a06711f 100644
--- a/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
+++ b/third_party/blink/renderer/modules/webaudio/offline_audio_context.cc
@@ -262,9 +262,12 @@
     return promise;
   }
 
-  // Quantize (to the lower boundary) the suspend time by the render quantum.
+  // Find the sample frame and round up to the nearest render quantum
+  // boundary.  This assumes the render quantum is a power of two.
   size_t frame = when * sampleRate();
-  frame -= frame % DestinationHandler().RenderQuantumFrames();
+  frame = audio_utilities::kRenderQuantumFrames *
+          ((frame + audio_utilities::kRenderQuantumFrames - 1) /
+           audio_utilities::kRenderQuantumFrames);
 
   // The specified suspend time is in the past; reject the promise.
   if (frame < CurrentSampleFrame()) {
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_manager.h b/third_party/blink/renderer/platform/bindings/parkable_string_manager.h
index 3a68651..20e510d 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_manager.h
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_manager.h
@@ -22,7 +22,7 @@
 class ParkableString;
 
 const base::Feature kCompressParkableStringsInBackground{
-    "CompressParkableStringsInBackground", base::FEATURE_DISABLED_BY_DEFAULT};
+    "CompressParkableStringsInBackground", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kCompressParkableStringsInForeground{
     "CompressParkableStringsInForeground", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
index be453c0c..4955041e 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
@@ -97,12 +97,6 @@
     EXPECT_TRUE(parkable.Impl()->is_parked());
     return parkable;
   }
-
-  void SetUp() override {
-    ParkableStringTestBase::SetUp();
-    scoped_feature_list_.InitAndEnableFeature(
-        kCompressParkableStringsInBackground);
-  }
 };
 
 // The main aim of this test is to check that the compressed size of a string
diff --git a/third_party/blink/renderer/platform/exported/web_url_request.cc b/third_party/blink/renderer/platform/exported/web_url_request.cc
index ab20203..970d9ee 100644
--- a/third_party/blink/renderer/platform/exported/web_url_request.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_request.cc
@@ -426,14 +426,6 @@
   resource_request_->SetRequestedWithHeader(value);
 }
 
-const WebString WebURLRequest::GetClientDataHeader() const {
-  return resource_request_->GetClientDataHeader();
-}
-
-void WebURLRequest::SetClientDataHeader(const WebString& value) {
-  resource_request_->SetClientDataHeader(value);
-}
-
 const base::UnguessableToken& WebURLRequest::GetFetchWindowId() const {
   return resource_request_->GetFetchWindowId();
 }
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
index 8b46875..a621ea0 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
@@ -61,11 +61,12 @@
       : frame_index_(frame_index), client_id_(client_id) {}
   ~FrameSettingImageProvider() override = default;
 
-  ScopedDecodedDrawImage GetDecodedDrawImage(
+  ImageProvider::ScopedResult GetRasterContent(
       const cc::DrawImage& draw_image) override {
+    DCHECK(!draw_image.paint_image().IsPaintWorklet());
     auto sk_image =
         draw_image.paint_image().GetSkImageForFrame(frame_index_, client_id_);
-    return ScopedDecodedDrawImage(
+    return ScopedResult(
         cc::DecodedDrawImage(sk_image, SkSize::MakeEmpty(), SkSize::Make(1, 1),
                              draw_image.filter_quality(), true));
   }
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 46b7901..bdb7f35 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -743,17 +743,18 @@
   ~CanvasImageProvider() override = default;
 
   // cc::ImageProvider implementation.
-  ScopedDecodedDrawImage GetDecodedDrawImage(const cc::DrawImage&) override;
+  cc::ImageProvider::ScopedResult GetRasterContent(
+      const cc::DrawImage&) override;
 
   void ReleaseLockedImages() { locked_images_.clear(); }
 
  private:
-  void CanUnlockImage(ScopedDecodedDrawImage);
+  void CanUnlockImage(ScopedResult);
   void CleanupLockedImages();
 
   bool is_hardware_decode_cache_;
   bool cleanup_task_pending_ = false;
-  std::vector<ScopedDecodedDrawImage> locked_images_;
+  std::vector<ScopedResult> locked_images_;
   cc::PlaybackImageProvider playback_image_provider_n32_;
   base::Optional<cc::PlaybackImageProvider> playback_image_provider_f16_;
 
@@ -780,20 +781,22 @@
   }
 }
 
-cc::ImageProvider::ScopedDecodedDrawImage
-CanvasResourceProvider::CanvasImageProvider::GetDecodedDrawImage(
+cc::ImageProvider::ScopedResult
+CanvasResourceProvider::CanvasImageProvider::GetRasterContent(
     const cc::DrawImage& draw_image) {
+  // TODO(xidachen): Ensure this function works for paint worklet generated
+  // images.
   // If we like to decode high bit depth image source to half float backed
   // image, we need to sniff the image bit depth here to avoid double decoding.
-  ImageProvider::ScopedDecodedDrawImage scoped_decoded_image;
+  ImageProvider::ScopedResult scoped_decoded_image;
   if (playback_image_provider_f16_ &&
       draw_image.paint_image().is_high_bit_depth()) {
     DCHECK(playback_image_provider_f16_);
     scoped_decoded_image =
-        playback_image_provider_f16_->GetDecodedDrawImage(draw_image);
+        playback_image_provider_f16_->GetRasterContent(draw_image);
   } else {
     scoped_decoded_image =
-        playback_image_provider_n32_.GetDecodedDrawImage(draw_image);
+        playback_image_provider_n32_.GetRasterContent(draw_image);
   }
 
   // Holding onto locked images here is a performance optimization for the
@@ -805,8 +808,9 @@
   // decode cache has its own limit.  In the software case, just unlock
   // immediately and let the discardable system manage the cache logic
   // behind the scenes.
-  if (!scoped_decoded_image.needs_unlock() || !is_hardware_decode_cache_)
+  if (!scoped_decoded_image.needs_unlock() || !is_hardware_decode_cache_) {
     return scoped_decoded_image;
+  }
 
   constexpr int kMaxLockedImagesCount = 500;
   if (!scoped_decoded_image.decoded_image().is_budgeted() ||
@@ -816,14 +820,14 @@
   }
 
   auto decoded_draw_image = scoped_decoded_image.decoded_image();
-  return ScopedDecodedDrawImage(
-      decoded_draw_image, base::BindOnce(&CanvasImageProvider::CanUnlockImage,
-                                         weak_factory_.GetWeakPtr(),
-                                         std::move(scoped_decoded_image)));
+  return ScopedResult(decoded_draw_image,
+                      base::BindOnce(&CanvasImageProvider::CanUnlockImage,
+                                     weak_factory_.GetWeakPtr(),
+                                     std::move(scoped_decoded_image)));
 }
 
 void CanvasResourceProvider::CanvasImageProvider::CanUnlockImage(
-    ScopedDecodedDrawImage image) {
+    ScopedResult image) {
   // We should early out and avoid calling this function for software decodes.
   DCHECK(is_hardware_decode_cache_);
 
diff --git a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
index f070dd5..328417c6 100644
--- a/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
+++ b/third_party/blink/renderer/platform/graphics/video_frame_submitter.cc
@@ -373,7 +373,8 @@
   resource_provider_->PrepareSendToParent(resources,
                                           &compositor_frame.resource_list);
 
-  // TODO(lethalantidote): Address third/fourth arg in SubmitCompositorFrame.
+  // We can pass nullptr for the HitTestData as the CompositorFram will not
+  // contain any SurfaceDrawQuads.
   compositor_frame_sink_->SubmitCompositorFrame(
       child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
           .local_surface_id(),
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index 055f2dc..e093416 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -383,11 +383,13 @@
 
   if (reason != BlinkGC::GCReason::kIdleGC &&
       reason != BlinkGC::GCReason::kPreciseGC &&
-      reason != BlinkGC::GCReason::kIncrementalIdleGC &&
-      reason != BlinkGC::GCReason::kIncrementalV8FollowupGC &&
       reason != BlinkGC::GCReason::kForcedGC)
     return false;
 
+  // TODO(keishi): crbug.com/918064 Heap compaction for incremental marking
+  // needs to be disabled until this crash is fixed.
+  CHECK_NE(marking_type, BlinkGC::kIncrementalMarking);
+
   // Compaction enable rules:
   //  - It's been a while since the last time.
   //  - "Considerable" amount of heap memory is bound up in freelist
diff --git a/third_party/blink/renderer/platform/testing/image_decode_bench.cc b/third_party/blink/renderer/platform/testing/image_decode_bench.cc
index dcece3b..951632a 100644
--- a/third_party/blink/renderer/platform/testing/image_decode_bench.cc
+++ b/third_party/blink/renderer/platform/testing/image_decode_bench.cc
@@ -4,13 +4,7 @@
 
 // Provides a minimal wrapping of the Blink image decoders. Used to perform
 // a non-threaded, memory-to-memory image decode using micro second accuracy
-// clocks to measure image decode time. Basic usage:
-//
-//   % ninja -C out/Release image_decode_bench &&
-//      ./out/Release/image_decode_bench file [iterations]
-//
-// TODO(noel): Consider adding md5 checksum support to WTF. Use it to compute
-// the decoded image frame md5 and output that value.
+// clocks to measure image decode time.
 //
 // TODO(noel): Consider integrating this tool in Chrome telemetry for realz,
 // using the image corpora used to assess Blink image decode performance. See
@@ -84,54 +78,58 @@
 
 }  // namespace
 
-int ImageDecodeBenchMain(int argc, char* argv[]) {
-  base::CommandLine::Init(argc, argv);
-  const char* program = argv[0];
+void ImageDecodeBenchMain(int argc, char* argv[]) {
+  int option, iterations = 1;
 
-  if (argc < 2) {
-    fprintf(stderr, "Usage: %s file [iterations]\n", program);
+  auto usage_exit = [&] {
+    fprintf(stderr, "Usage: %s [-i iterations] file [file...]\n", argv[0]);
     exit(1);
+  };
+
+  for (option = 1; option < argc; ++option) {
+    if (argv[option][0] != '-')
+      break;  // End of optional arguments.
+    if (std::string(argv[option]) != "-i")
+      usage_exit();
+    iterations = (++option < argc) ? atoi(argv[option]) : 0;
+    if (iterations < 1)
+      usage_exit();
   }
 
-  // Control bench decode iterations.
+  if (option >= argc)
+    usage_exit();
 
-  size_t decode_iterations = 1;
-  if (argc >= 3) {
-    char* end = nullptr;
-    decode_iterations = strtol(argv[2], &end, 10);
-    if (*end != '\0' || !decode_iterations) {
-      fprintf(stderr,
-              "Second argument should be number of iterations. "
-              "The default is 1. You supplied %s\n",
-              argv[2]);
-      exit(1);
-    }
-  }
+  // Setup Blink platform.
 
   std::unique_ptr<Platform> platform = std::make_unique<Platform>();
   Platform::CreateMainThreadAndInitialize(platform.get());
 
-  // Read entire file content into |data| (a contiguous block of memory) then
-  // decode it to verify the image and record its ImageMeta data.
+  // Bench each image file.
 
-  ImageMeta image = {argv[1], 0, 0, 0, 0};
-  scoped_refptr<SharedBuffer> data = ReadFile(argv[1]);
-  DecodeImageData(data.get(), &image);
+  while (option < argc) {
+    const char* name = argv[option++];
 
-  // Image decode bench for decode_iterations.
+    // Read entire file content into |data| (a contiguous block of memory) then
+    // decode it to verify the image and record its ImageMeta data.
 
-  double total_time = 0.0;
-  for (size_t i = 0; i < decode_iterations; ++i) {
-    image.time = 0.0;
+    ImageMeta image = {name, 0, 0, 0, 0};
+    scoped_refptr<SharedBuffer> data = ReadFile(name);
     DecodeImageData(data.get(), &image);
-    total_time += image.time;
+
+    // Image decode bench for iterations.
+
+    double total_time = 0.0;
+    for (int i = 0; i < iterations; ++i) {
+      image.time = 0.0;
+      DecodeImageData(data.get(), &image);
+      total_time += image.time;
+    }
+
+    // Results to stdout.
+
+    double average_time = total_time / iterations;
+    printf("%f %f %s\n", total_time, average_time, name);
   }
-
-  // Results to stdout.
-
-  double average_time = total_time / decode_iterations;
-  printf("%f %f\n", total_time, average_time);
-  return 0;
 }
 
 }  // namespace blink
@@ -139,5 +137,7 @@
 int main(int argc, char* argv[]) {
   base::MessageLoop message_loop;
   mojo::core::Init();
-  return blink::ImageDecodeBenchMain(argc, argv);
+  base::CommandLine::Init(argc, argv);
+  blink::ImageDecodeBenchMain(argc, argv);
+  return 0;
 }
diff --git a/third_party/blink/renderer/platform/weborigin/security_policy.cc b/third_party/blink/renderer/platform/weborigin/security_policy.cc
index 2a893a7..d821a98 100644
--- a/third_party/blink/renderer/platform/weborigin/security_policy.cc
+++ b/third_party/blink/renderer/platform/weborigin/security_policy.cc
@@ -227,15 +227,19 @@
     const SecurityOrigin* active_origin,
     const SecurityOrigin* target_origin) {
   MutexLocker lock(GetMutex());
-  return GetOriginAccessList().IsAllowed(active_origin->ToUrlOrigin(),
-                                         target_origin->ToUrlOrigin().GetURL());
+  return GetOriginAccessList().CheckAccessState(
+             active_origin->ToUrlOrigin(),
+             target_origin->ToUrlOrigin().GetURL()) ==
+         network::cors::OriginAccessList::AccessState::kAllowed;
 }
 
 bool SecurityPolicy::IsOriginAccessToURLAllowed(
     const SecurityOrigin* active_origin,
     const KURL& url) {
   MutexLocker lock(GetMutex());
-  return GetOriginAccessList().IsAllowed(active_origin->ToUrlOrigin(), url);
+  return GetOriginAccessList().CheckAccessState(active_origin->ToUrlOrigin(),
+                                                url) ==
+         network::cors::OriginAccessList::AccessState::kAllowed;
 }
 
 void SecurityPolicy::AddOriginAccessAllowListEntry(
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index b8ff288..d38b620 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -4379,7 +4379,6 @@
 
 #crbug.com/765738 [ Linux Win Mac ] http/tests/wasm/wasm_remote_postMessage_test.https.html [ Pass Timeout ]
 crbug.com/892212 http/tests/wasm/wasm_remote_postMessage_test.https.html [ Pass Failure Timeout ]
-crbug.com/928458 http/tests/wasm/wasm_worker_termination_while_compiling.html [ Pass Crash ]
 
 # ====== Random order flaky tests from here ======
 # These tests are flaky when run in random order, which is the default on Linux & Mac since since 2016-12-16.
@@ -6047,6 +6046,3 @@
 crbug.com/929122 [ Linux ] external/wpt/html/dom/interfaces.worker.html [ Timeout Failure ]
 crbug.com/929355 [ Linux ] external/wpt/webrtc/RTCPeerConnection-track-stats.https.html [ Pass Failure ]
 crbug.com/929435 [ Mac ] external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html [ Pass Failure ]
-
-# Sheriff 2019-02-07
-crbug.com/929929 external/wpt/background-fetch/fetch.https.window.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/background-fetch/fetch.https.window.js b/third_party/blink/web_tests/external/wpt/background-fetch/fetch.https.window.js
index 35b5709..d4bc8bf 100644
--- a/third_party/blink/web_tests/external/wpt/background-fetch/fetch.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/background-fetch/fetch.https.window.js
@@ -347,22 +347,9 @@
     uniqueId(), ['resources/feature-name.txt', '/common/slow.py']);
 
   const record = await registration.match('resources/feature-name.txt');
-
-  await new Promise(resolve => {
-    const expectedResultText = 'Background Fetch';
-
-    registration.onprogress = async event => {
-      if (event.target.downloaded < expectedResultText.length)
-        return;
-
-      const response = await record.responseReady;
-
-      assert_true(response.url.includes('resources/feature-name.txt'));
-      const completedResponseText = await response.text();
-      assert_equals(completedResponseText, expectedResultText);
-
-      resolve();
-    };
-  });
+  const response = await record.responseReady;
+  assert_true(response.url.includes('resources/feature-name.txt'));
+  const completedResponseText = await response.text();
+  assert_equals(completedResponseText, 'Background Fetch');
 
 }, 'Access to active fetches is supported.');
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html
index 386614f..0b09edd 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audionode-interface/audionode-disconnect-audioparam.html
@@ -22,7 +22,9 @@
       // Calculate the index for disconnection.
       function getDisconnectIndex(disconnectTime) {
         let disconnectIndex = disconnectTime * sampleRate;
-        return disconnectIndex -= (disconnectIndex) % renderQuantum;
+        disconnectIndex = renderQuantum *
+            Math.floor((disconnectIndex + renderQuantum - 1) / renderQuantum);
+        return disconnectIndex;
       }
 
       // Get the index of value change.
@@ -35,7 +37,6 @@
 
       // Task 1: test disconnect(AudioParam) method.
       audit.define('disconnect(AudioParam)', (task, should) => {
-
         // Creates a buffer source with value [1] and then connect it to two
         // gain nodes in series. The output of the buffer source is lowered by
         // half
@@ -89,14 +90,12 @@
               should(channelData, 'Channel #0').containValues([2.25, 1.5]);
               should(valueChangeIndex, 'The index of value change')
                   .beEqualTo(disconnectIndex);
-
             })
             .then(() => task.done());
       });
 
       // Task 2: test disconnect(AudioParam, output) method.
       audit.define('disconnect(AudioParam, output)', (task, should) => {
-
         // Create a 2-channel buffer source with [1, 2] in each channel and
         // make a serial connection through gain1 and gain 2. The make the
         // buffer source half with a gain node and connect it to a 2-output
@@ -168,7 +167,6 @@
                   valueChangeIndexCh1,
                   'The index of value change in channel #1')
                   .beEqualTo(disconnectIndex);
-
             })
             .then(() => task.done());
       });
@@ -191,19 +189,28 @@
         gain3.connect(context.destination);
 
         // gain1 is not connected to gain3.gain. Exception should be thrown.
-        should(function() {
-          gain1.disconnect(gain3.gain);
-        }, 'gain1.disconnect(gain3.gain)').throw(DOMException, 'InvalidAccessError');
+        should(
+            function() {
+              gain1.disconnect(gain3.gain);
+            },
+            'gain1.disconnect(gain3.gain)')
+            .throw(DOMException, 'InvalidAccessError');
 
         // When the output index is good but the destination is invalid.
-        should(function() {
-          splitter.disconnect(gain1.gain, 1);
-        }, 'splitter.disconnect(gain1.gain, 1)').throw(DOMException, 'InvalidAccessError');
+        should(
+            function() {
+              splitter.disconnect(gain1.gain, 1);
+            },
+            'splitter.disconnect(gain1.gain, 1)')
+            .throw(DOMException, 'InvalidAccessError');
 
         // When both arguments are wrong, throw IndexSizeError first.
-        should(function() {
-          splitter.disconnect(gain1.gain, 2);
-        }, 'splitter.disconnect(gain1.gain, 2)').throw(DOMException, 'IndexSizeError');
+        should(
+            function() {
+              splitter.disconnect(gain1.gain, 2);
+            },
+            'splitter.disconnect(gain1.gain, 2)')
+            .throw(DOMException, 'IndexSizeError');
 
         task.done();
       });
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/audiochannelmerger-disconnect.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/audiochannelmerger-disconnect.html
index 5fb18c8..ad74d5e 100644
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/audiochannelmerger-disconnect.html
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/audiochannelmerger-disconnect.html
@@ -58,7 +58,9 @@
               // Calculate the first zero index in the second channel.
               let channel1 = buffer.getChannelData(1);
               let disconnectIndex = disconnectTime * sampleRate;
-              disconnectIndex -= (disconnectIndex) % renderQuantum;
+              disconnectIndex = renderQuantum *
+                  Math.floor(
+                      (disconnectIndex + renderQuantum - 1) / renderQuantum);
               let firstZeroIndex = channel1.findIndex(function(element, index) {
                 if (element === 0)
                   return index;
@@ -70,7 +72,6 @@
               should(
                   firstZeroIndex, 'The index of first zero in the channel #1')
                   .beEqualTo(disconnectIndex);
-
             })
             .then(() => task.done());
       });
diff --git a/third_party/blink/web_tests/fast/scroll-behavior/middleclick-autoscroll-use-count.html b/third_party/blink/web_tests/fast/scroll-behavior/middleclick-autoscroll-use-count.html
new file mode 100644
index 0000000..fa24255
--- /dev/null
+++ b/third_party/blink/web_tests/fast/scroll-behavior/middleclick-autoscroll-use-count.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src='../../resources/gesture-util.js'></script>
+<style>
+
+  ::-webkit-scrollbar {
+    display: none;
+  }
+  body {
+    margin: 0px;
+    height: 1000px;
+    width: 1000px;
+  }
+  #scrollable {
+    background-color: #FF7F7F;
+    height: 600px;
+    width: 600px;
+    overflow: scroll;
+  }
+  #content {
+    height: 900px;
+    width: 900px;
+  }
+
+</style>
+
+<div id="scrollable">
+	<div id="content"></div>
+</div>
+
+<script>
+// from enum WebFeature in web_feature.mojom-shared.h
+const MIDDLE_CLICK_AUTOSCROLL = 1551;
+const SCROLL_BY_TOUCH_COUNTER = 1847;
+const SCROLL_BY_WHEEL_COUNTER = 1848;
+
+var scrollable = document.getElementById('scrollable');
+var rect = scrollable.getBoundingClientRect();
+var start_x = (rect.left + rect.right) / 2;
+var start_y = (rect.top + rect.bottom) / 2;
+
+async function middleClickAutoscroll() {
+  const middle_button = 1;
+  await waitForCompositorCommit();
+  await mouseMoveTo(start_x, start_y);
+  await mouseClickOn(start_x, start_y, middle_button);
+  await mouseMoveTo(start_x, start_y + 100);
+  await waitFor(() => {
+    return scrollable.scrollTop > 0;
+  });
+  await waitFor(() => {
+    return internals.isUseCounted(document, MIDDLE_CLICK_AUTOSCROLL);
+  }, "Didn't record middle click autoscroll");
+  await mouseClickOn(start_x, start_y + 100, middle_button);
+}
+
+async function wheelScrollDown() {
+  const precise_deltas = true;
+  await waitForCompositorCommit();
+  var previous_scroll_offset = scrollable.scrollTop;
+
+  await smoothScroll(50, start_x, start_y,
+      GestureSourceType.MOUSE_INPUT,'down', SPEED_INSTANT, precise_deltas);
+  await waitFor(() => {
+    return scrollable.scrollTop - previous_scroll_offset > 0;
+  }, "Didn't scroll by wheel");
+  await waitForCompositorCommit();
+}
+
+promise_test(async () => {
+  // Autoscroll and check that middle click autoscroll use is counted.
+  await middleClickAutoscroll();
+
+  // Scroll by wheel and wait for wheel scrolling use count to increase.
+  await wheelScrollDown();
+  await waitFor(() => {
+    return internals.isUseCounted(document, SCROLL_BY_WHEEL_COUNTER);
+  }, "Didn't record wheel use count");
+
+  // Now check that autoscrolling has not increased the touch scrolling use
+  // count. We do this checking after wheel count increment to make sure that
+  // we have waited long enough before verifying that the touch scrolling use
+  // count is not incremented due to autoscrolling.
+  assert_false(internals.isUseCounted(document, SCROLL_BY_TOUCH_COUNTER))
+}, "Autoscrolling should not update touch scrolling usecounter.");
+
+</script>
diff --git a/third_party/blink/web_tests/shadow-dom/crashes/editing-layoutng-legacy-crash.html b/third_party/blink/web_tests/shadow-dom/crashes/editing-layoutng-legacy-crash.html
new file mode 100644
index 0000000..4358de7e
--- /dev/null
+++ b/third_party/blink/web_tests/shadow-dom/crashes/editing-layoutng-legacy-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<div id="host">
+  <div style="overflow:scroll">
+    <div contenteditable></div>
+  </div>
+</div>
+<script>
+  test(() => {
+    const root = host.attachShadow({mode:"open"});
+    root.innerHTML = `
+      <div style="overflow:scroll">
+        <div contenteditable></div>
+        <slot></slot>
+      </div>
+    `;
+  }, " Editing causing fallback to legacy layout for nested BFCs should not crash.");
+</script>
diff --git a/third_party/blink/web_tests/webaudio/AudioBufferSource/audiobuffersource-late-start.html b/third_party/blink/web_tests/webaudio/AudioBufferSource/audiobuffersource-late-start.html
index 604a9d66..702f74c7 100644
--- a/third_party/blink/web_tests/webaudio/AudioBufferSource/audiobuffersource-late-start.html
+++ b/third_party/blink/web_tests/webaudio/AudioBufferSource/audiobuffersource-late-start.html
@@ -22,7 +22,9 @@
       // Calculate the index for actual start time.
       function getStartIndex(time) {
         let startIndex = time * sampleRate;
-        return startIndex -= (startIndex) % renderQuantum;
+        startIndex = renderQuantum *
+            Math.floor((startIndex + renderQuantum - 1) / renderQuantum);
+        return startIndex;
       }
 
       // Get the index of value change.
@@ -61,7 +63,6 @@
         // is found later than the first output sample.
         context.startRendering()
             .then(function(buffer) {
-
               let channelData = buffer.getChannelData(0);
               let startIndex = getStartIndex(startTime);
               let nonZeroValueIndex = getValueChangeIndex(channelData, 1.0);
@@ -73,7 +74,6 @@
               should(
                   nonZeroValueIndex, 'The index of the first non-zero sample')
                   .notBeEqualTo(0)
-
             })
             .then(() => task.done());
       });
diff --git a/third_party/blink/web_tests/webaudio/AudioParam/value-setter-warnings-expected.txt b/third_party/blink/web_tests/webaudio/AudioParam/value-setter-warnings-expected.txt
index e432964..42d024f 100644
--- a/third_party/blink/web_tests/webaudio/AudioParam/value-setter-warnings-expected.txt
+++ b/third_party/blink/web_tests/webaudio/AudioParam/value-setter-warnings-expected.txt
@@ -23,7 +23,7 @@
 PASS   test-4 : rendered successfully
 PASS < [test-4] All assertions passed. (total 1 assertions)
 PASS > [test-5] 
-PASS   Value setter overlaps setValueCurve threw NotSupportedError: "Failed to set the 'value' property on 'AudioParam': setValueAtTime(99, 0.4986666666666666) overlaps setValueCurveAtTime(..., 0.25, 0.5)".
+PASS   Value setter overlaps setValueCurve threw NotSupportedError: "Failed to set the 'value' property on 'AudioParam': setValueAtTime(99, 0.5013333333333333) overlaps setValueCurveAtTime(..., 0.25, 0.5)".
 PASS   test-5 : rendered successfully
 PASS < [test-5] All assertions passed. (total 2 assertions)
 PASS # AUDIT TASK RUNNER FINISHED: 6 tasks ran successfully.
diff --git a/third_party/blink/web_tests/webaudio/OfflineAudioContext/offlineaudiocontext-suspend-resume-basic.html b/third_party/blink/web_tests/webaudio/OfflineAudioContext/offlineaudiocontext-suspend-resume-basic.html
index 4e53073..26a07ff4d 100644
--- a/third_party/blink/web_tests/webaudio/OfflineAudioContext/offlineaudiocontext-suspend-resume-basic.html
+++ b/third_party/blink/web_tests/webaudio/OfflineAudioContext/offlineaudiocontext-suspend-resume-basic.html
@@ -77,10 +77,12 @@
         let context =
             new OfflineAudioContext(1, sampleRate * renderDuration, sampleRate);
 
-        // |suspendTime1| and |suspendTime2| are identical when quantized to
-        // the render quantum size.
-        let suspendTime1 = renderQuantum / sampleRate;
-        let suspendTime2 = 1.5 * renderQuantum / sampleRate;
+        // |suspendTime1| and |suspendTime2| are identical when quantized to the
+        // render quantum size.  The factors are arbitrary except they should be
+        // between 1 (exclusive) and 2 (inclusive).  Using a factor of 2 also
+        // tests that times are rounded up.
+        let suspendTime1 = 1.25 * renderQuantum / sampleRate;
+        let suspendTime2 = 2 * renderQuantum / sampleRate;
 
         should(
             () => context.suspend(suspendTime1).then(() => context.resume()),
@@ -88,7 +90,7 @@
             .notThrow();
 
         should(
-            context.suspend(suspendTime2),
+            context.suspend(suspendTime2).then(() => context.resume()),
             'Scheduling another suspend at the same rendering quantum')
             .beRejected();
 
@@ -97,7 +99,6 @@
 
       // Task: Resuming a running context should be resolved.
       audit.define('resume-before-suspend', (task, should) => {
-
         // Make the render length 5 times longer to minimize the flakiness.
         let longRenderDuration = renderDuration * 5;
         let context = new OfflineAudioContext(
diff --git a/third_party/closure_compiler/externs/arc_apps_private.js b/third_party/closure_compiler/externs/arc_apps_private.js
new file mode 100644
index 0000000..e32f62b7
--- /dev/null
+++ b/third_party/closure_compiler/externs/arc_apps_private.js
@@ -0,0 +1,46 @@
+// Copyright 2019 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.
+
+// This file was generated by:
+//   tools/json_schema_compiler/compiler.py.
+// NOTE: The format of types has changed. 'FooType' is now
+//   'chrome.arcAppsPrivate.FooType'.
+// Please run the closure compiler before committing changes.
+// See https://chromium.googlesource.com/chromium/src/+/master/docs/closure_compilation.md
+
+/** @fileoverview Externs generated from namespace: arcAppsPrivate */
+
+/**
+ * @const
+ */
+chrome.arcAppsPrivate = {};
+
+/**
+ * @typedef {{
+ *   packageName: string
+ * }}
+ */
+chrome.arcAppsPrivate.AppInfo;
+
+/**
+ * Returns info of the installed ARC apps that are launchable, including ready
+ * and non-ready apps.
+ * @param {function(!Array<!chrome.arcAppsPrivate.AppInfo>):void} callback
+ */
+chrome.arcAppsPrivate.getLaunchableApps = function(callback) {};
+
+/**
+ * Launches the ARC app with its package name. The app is launched immediately
+ * if it's ready, otherwise it will be launched when it becomes ready. The
+ * callback is called as soon as the launch is scheduled.
+ * @param {string} packageName
+ * @param {function():void=} callback
+ */
+chrome.arcAppsPrivate.launchApp = function(packageName, callback) {};
+
+/**
+ * Fires when a new app can be launched via $(ref:launchApp).
+ * @type {!ChromeEvent}
+ */
+chrome.arcAppsPrivate.onInstalled;
diff --git a/third_party/closure_compiler/externs/passwords_private.js b/third_party/closure_compiler/externs/passwords_private.js
index 4cf28f1..e0ba375 100644
--- a/third_party/closure_compiler/externs/passwords_private.js
+++ b/third_party/closure_compiler/externs/passwords_private.js
@@ -39,14 +39,7 @@
 /**
  * @typedef {{
  *   urls: !chrome.passwordsPrivate.UrlCollection,
- *   username: string
- * }}
- */
-chrome.passwordsPrivate.LoginPair;
-
-/**
- * @typedef {{
- *   loginPair: !chrome.passwordsPrivate.LoginPair,
+ *   username: string,
  *   numCharactersInPassword: number,
  *   federationText: (string|undefined),
  *   id: number
@@ -86,8 +79,8 @@
     id, new_username, new_password) {};
 
 /**
- * Removes the saved password corresponding to |loginPair|. If no saved password
- * for this pair exists, this function is a no-op.
+ * Removes the saved password corresponding to |id|. If no saved password for
+ * this pair exists, this function is a no-op.
  * @param {number} id The id for the password entry being removed.
  */
 chrome.passwordsPrivate.removeSavedPassword = function(id) {};
diff --git a/third_party/pffft/BUILD.gn b/third_party/pffft/BUILD.gn
new file mode 100644
index 0000000..06f121a
--- /dev/null
+++ b/third_party/pffft/BUILD.gn
@@ -0,0 +1,107 @@
+# Copyright 2019 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.
+
+import("//testing/libfuzzer/fuzzer_test.gni")
+import("//testing/test.gni")
+
+# TODO(crbug.com/webrtc/9577): Add architecture specific flags.
+
+config("simd_config") {
+  # TODO(crbug.com/webrtc/9577): Use SIMD while fuzzing.
+  # TODO(crbug.com/webrtc/9577): Fix SIMD not detected on Android, fuchsia, iOS.
+  if (use_fuzzing_engine || is_android || is_fuchsia || is_ios) {
+    cflags = [ "-DPFFFT_SIMD_DISABLE" ]
+  }
+}
+
+static_library("pffft") {
+  configs += [ ":simd_config" ]
+  sources = [
+    "src/pffft.c",
+    "src/pffft.h",
+  ]
+}
+
+# Fuzzing.
+
+group("fuzzers") {
+  testonly = true
+  deps = [
+    ":pffft_complex_fuzzer",
+    ":pffft_real_fuzzer",
+  ]
+}
+
+fuzzer_testdata_dir = "$target_gen_dir/testdata"
+
+action("generate_pffft_fuzzer_corpus") {
+  script = "generate_seed_corpus.py"
+  sources = [
+    "generate_seed_corpus.py",
+  ]
+  args = [ rebase_path(fuzzer_testdata_dir, root_build_dir) ]
+  outputs = [
+    fuzzer_testdata_dir,
+  ]
+}
+
+fuzzer_test("pffft_complex_fuzzer") {
+  sources = [
+    "pffft_fuzzer.cc",
+  ]
+  cflags = [ "-DTRANSFORM_COMPLEX" ]
+  deps = [
+    ":pffft",
+  ]
+  seed_corpus = fuzzer_testdata_dir
+  seed_corpus_deps = [ ":generate_pffft_fuzzer_corpus" ]
+}
+
+fuzzer_test("pffft_real_fuzzer") {
+  sources = [
+    "pffft_fuzzer.cc",
+  ]
+  cflags = [ "-DTRANSFORM_REAL" ]
+  deps = [
+    ":pffft",
+  ]
+  seed_corpus = fuzzer_testdata_dir
+  seed_corpus_deps = [ ":generate_pffft_fuzzer_corpus" ]
+}
+
+# Unit tests and benchmark.
+
+# This target must be used only for testing and benchmark purposes.
+static_library("fftpack") {
+  testonly = true
+  sources = [
+    "src/fftpack.c",
+    "src/fftpack.h",
+  ]
+  visibility = [ ":*" ]
+}
+
+executable("pffft_benchmark") {
+  configs += [ ":simd_config" ]
+  testonly = true
+  sources = [
+    "src/test_pffft.c",
+  ]
+  deps = [
+    ":fftpack",
+    ":pffft",
+  ]
+}
+
+test("pffft_unittest") {
+  sources = [
+    "pffft_unittest.cc",
+  ]
+  deps = [
+    ":fftpack",
+    ":pffft",
+    "//testing/gtest",
+    "//testing/gtest:gtest_main",
+  ]
+}
diff --git a/third_party/pffft/DEPS b/third_party/pffft/DEPS
new file mode 100644
index 0000000..fca61fc
--- /dev/null
+++ b/third_party/pffft/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+testing/gtest/include/gtest",
+]
diff --git a/third_party/pffft/README.chromium b/third_party/pffft/README.chromium
index a0e1164..eab510e 100644
--- a/third_party/pffft/README.chromium
+++ b/third_party/pffft/README.chromium
@@ -13,3 +13,9 @@
 PFFFT does 1D Fast Fourier Transforms, of single precision real and complex vectors. It tries do it fast, it tries to be correct, and it tries to be small. Computations do take advantage of SSE1 instructions on x86 cpus, Altivec on powerpc cpus, and NEON on ARM cpus.
 
 Local Modifications:
+ * 01-add_m_pi.diff: define M_PI if not defined
+ * 02-rmv_printf.diff: remove printf and stop including stdio.h
+ * 03-decl_validate_simd.diff: declare validate_pffft_simd() in pffft.h
+ * 04-add_m_ln2.diff: define M_LN2 if not defined
+ * 05-add_m_sqrt2.diff: define M_SQRT2 if not defined
+ * 06-add_m_pi_fftpack.diff: define M_PI if not defined (fftpack)
diff --git a/third_party/pffft/generate_seed_corpus.py b/third_party/pffft/generate_seed_corpus.py
new file mode 100755
index 0000000..c48aa87
--- /dev/null
+++ b/third_party/pffft/generate_seed_corpus.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# Copyright 2019 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.
+
+from __future__ import division
+from __future__ import print_function
+
+from array import array
+import os
+import random
+import sys
+
+MAX_INPUT_SIZE = int(1e6)
+MAX_FLOAT32 = 3.4028235e+38
+
+
+def IsValidSize(n):
+  if n == 0:
+    return False
+  # PFFFT only supports transforms for inputs of length N of the form
+  # N = (2^a)*(3^b)*(5^c) where a >= 5, b >=0, c >= 0.
+  FACTORS = [2, 3, 5]
+  factorization = [0, 0, 0]
+  for i, factor in enumerate(FACTORS):
+    while n % factor == 0:
+      n = n // factor
+      factorization[i] += 1
+  return factorization[0] >= 5 and n == 1
+
+
+def WriteFloat32ArrayToFile(file_path, size, generator):
+  """Generate an array of float32 values and writes to file."""
+  with open(file_path, 'wb') as f:
+    float_array = array('f', [generator() for _ in range(size)])
+    float_array.tofile(f)
+
+
+def main():
+  if len(sys.argv) < 2:
+    print('Usage: %s <path to output directory>' % sys.argv[0])
+    sys.exit(1)
+
+  output_path = sys.argv[1]
+  # Create output directory if missing.
+  if not os.path.exists(output_path):
+    os.makedirs(output_path)
+
+  # List of valid input sizes.
+  N = [n for n in range(MAX_INPUT_SIZE) if IsValidSize(n)]
+
+  # Set the seed to always generate the same random data.
+  random.seed(0)
+
+  # Generate different types of input arrays for each target length.
+  for n in N:
+    # Zeros.
+    WriteFloat32ArrayToFile(
+        os.path.join(output_path, 'zeros_%d' % n), n, lambda: 0)
+    # Max float 32.
+    WriteFloat32ArrayToFile(
+        os.path.join(output_path, 'max_%d' % n), n, lambda: MAX_FLOAT32)
+    # Random values in the s16 range.
+    rnd_s16 = lambda: 32768.0 * 2.0 * (random.random() - 0.5)
+    WriteFloat32ArrayToFile(
+        os.path.join(output_path, 'rnd_s16_%d' % n), n, rnd_s16)
+
+  sys.exit(0)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/pffft/patches/01-add_m_pi.diff b/third_party/pffft/patches/01-add_m_pi.diff
new file mode 100644
index 0000000..aa4b98c7
--- /dev/null
+++ b/third_party/pffft/patches/01-add_m_pi.diff
@@ -0,0 +1,14 @@
+diff --git a/third_party/pffft/src/pffft.c b/third_party/pffft/src/pffft.c
+index 0603f2b1f698..7934db448a09 100644
+--- a/third_party/pffft/src/pffft.c
++++ b/third_party/pffft/src/pffft.c
+@@ -82,6 +82,9 @@
+ #  define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ *varname__ = (type__*)_alloca(size__ * sizeof(type__))
+ #endif
+ 
++#ifndef M_PI
++#define M_PI 3.14159265358979323846
++#endif
+ 
+ /* 
+    vector support macros: the rest of the code is independant of
diff --git a/third_party/pffft/patches/02-rmv_printf.diff b/third_party/pffft/patches/02-rmv_printf.diff
new file mode 100644
index 0000000..3a91494
--- /dev/null
+++ b/third_party/pffft/patches/02-rmv_printf.diff
@@ -0,0 +1,60 @@
+diff --git a/third_party/pffft/src/pffft.c b/third_party/pffft/src/pffft.c
+index 7934db448a09..2e0c2f651438 100644
+--- a/third_party/pffft/src/pffft.c
++++ b/third_party/pffft/src/pffft.c
+@@ -59,7 +59,7 @@
+ 
+ #include "pffft.h"
+ #include <stdlib.h>
+-#include <stdio.h>
++// #include <stdio.h>
+ #include <math.h>
+ #include <assert.h>
+ 
+@@ -222,31 +222,35 @@ void validate_pffft_simd() {
+   memcpy(a3.f, f+12, 4*sizeof(float));
+ 
+   t = a0; u = a1; t.v = VZERO();
+-  printf("VZERO=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 0, 0, 0, 0);
++  // printf("VZERO=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
++  assertv4(t, 0, 0, 0, 0);
+   t.v = VADD(a1.v, a2.v);
+-  printf("VADD(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 12, 14, 16, 18);
++  // printf("VADD(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
++  assertv4(t, 12, 14, 16, 18);
+   t.v = VMUL(a1.v, a2.v);
+-  printf("VMUL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 32, 45, 60, 77);
++  // printf("VMUL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
++  assertv4(t, 32, 45, 60, 77);
+   t.v = VMADD(a1.v, a2.v,a0.v);
+-  printf("VMADD(4:7,8:11,0:3)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 32, 46, 62, 80);
++  // printf("VMADD(4:7,8:11,0:3)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
++  assertv4(t, 32, 46, 62, 80);
+ 
+   INTERLEAVE2(a1.v,a2.v,t.v,u.v);
+-  printf("INTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
++  // printf("INTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
+   assertv4(t, 4, 8, 5, 9); assertv4(u, 6, 10, 7, 11);
+   UNINTERLEAVE2(a1.v,a2.v,t.v,u.v);
+-  printf("UNINTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
++  // printf("UNINTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
+   assertv4(t, 4, 6, 8, 10); assertv4(u, 5, 7, 9, 11);
+ 
+   t.v=LD_PS1(f[15]);
+-  printf("LD_PS1(15)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
++  // printf("LD_PS1(15)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+   assertv4(t, 15, 15, 15, 15);
+   t.v = VSWAPHL(a1.v, a2.v);
+-  printf("VSWAPHL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
++  // printf("VSWAPHL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+   assertv4(t, 8, 9, 6, 7);
+   VTRANSPOSE4(a0.v, a1.v, a2.v, a3.v);
+-  printf("VTRANSPOSE4(0:3,4:7,8:11,12:15)=[%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", 
+-         a0.f[0], a0.f[1], a0.f[2], a0.f[3], a1.f[0], a1.f[1], a1.f[2], a1.f[3], 
+-         a2.f[0], a2.f[1], a2.f[2], a2.f[3], a3.f[0], a3.f[1], a3.f[2], a3.f[3]); 
++  // printf("VTRANSPOSE4(0:3,4:7,8:11,12:15)=[%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", 
++  //        a0.f[0], a0.f[1], a0.f[2], a0.f[3], a1.f[0], a1.f[1], a1.f[2], a1.f[3], 
++  //        a2.f[0], a2.f[1], a2.f[2], a2.f[3], a3.f[0], a3.f[1], a3.f[2], a3.f[3]); 
+   assertv4(a0, 0, 4, 8, 12); assertv4(a1, 1, 5, 9, 13); assertv4(a2, 2, 6, 10, 14); assertv4(a3, 3, 7, 11, 15);
+ }
+ #endif //!PFFFT_SIMD_DISABLE
diff --git a/third_party/pffft/patches/03-decl_validate_simd.diff b/third_party/pffft/patches/03-decl_validate_simd.diff
new file mode 100644
index 0000000..4531511
--- /dev/null
+++ b/third_party/pffft/patches/03-decl_validate_simd.diff
@@ -0,0 +1,16 @@
+diff --git a/third_party/pffft/src/pffft.h b/third_party/pffft/src/pffft.h
+index 2bfa7b3ebcfb..bb6f78d4b795 100644
+--- a/third_party/pffft/src/pffft.h
++++ b/third_party/pffft/src/pffft.h
+@@ -83,6 +83,11 @@
+ extern "C" {
+ #endif
+ 
++#ifndef PFFFT_SIMD_DISABLE
++  // Detects compiler bugs with respect to simd instruction.
++  void validate_pffft_simd();
++#endif
++
+   /* opaque struct holding internal stuff (precomputed twiddle factors)
+      this struct can be shared by many threads as it contains only
+      read-only data.  
diff --git a/third_party/pffft/patches/04-add_m_ln2.diff b/third_party/pffft/patches/04-add_m_ln2.diff
new file mode 100644
index 0000000..171c12ab
--- /dev/null
+++ b/third_party/pffft/patches/04-add_m_ln2.diff
@@ -0,0 +1,15 @@
+diff --git a/third_party/pffft/src/test_pffft.c b/third_party/pffft/src/test_pffft.c
+index ab78b4dcc0d5..deaf98ed9f09 100644
+--- a/third_party/pffft/src/test_pffft.c
++++ b/third_party/pffft/src/test_pffft.c
+@@ -45,6 +45,10 @@
+ #  include <fftw3.h>
+ #endif
+ 
++#ifndef M_LN2
++#define M_LN2 0.693147180559945309417
++#endif
++
+ #define MAX(x,y) ((x)>(y)?(x):(y))
+ 
+ double frand() {
diff --git a/third_party/pffft/patches/05-add_m_sqrt2.diff b/third_party/pffft/patches/05-add_m_sqrt2.diff
new file mode 100644
index 0000000..5b62372
--- /dev/null
+++ b/third_party/pffft/patches/05-add_m_sqrt2.diff
@@ -0,0 +1,15 @@
+diff --git a/third_party/pffft/src/pffft.c b/third_party/pffft/src/pffft.c
+index 2e0c2f651438..b89e6d7b6fbc 100644
+--- a/third_party/pffft/src/pffft.c
++++ b/third_party/pffft/src/pffft.c
+@@ -86,6 +86,10 @@
+ #define M_PI 3.14159265358979323846
+ #endif
+ 
++#ifndef M_SQRT2
++#define M_SQRT2 1.41421356237309504880
++#endif
++
+ /* 
+    vector support macros: the rest of the code is independant of
+    SSE/Altivec/NEON -- adding support for other platforms with 4-element
diff --git a/third_party/pffft/patches/06-add_m_pi_fftpack.diff b/third_party/pffft/patches/06-add_m_pi_fftpack.diff
new file mode 100644
index 0000000..0042440e
--- /dev/null
+++ b/third_party/pffft/patches/06-add_m_pi_fftpack.diff
@@ -0,0 +1,15 @@
+diff --git a/third_party/pffft/src/fftpack.c b/third_party/pffft/src/fftpack.c
+index b6375a80dab4..d801d16602b2 100644
+--- a/third_party/pffft/src/fftpack.c
++++ b/third_party/pffft/src/fftpack.c
+@@ -54,6 +54,10 @@
+ #include "fftpack.h"
+ #include <math.h>
+ 
++#ifndef M_PI
++#define M_PI 3.14159265358979323846
++#endif
++
+ typedef fftpack_real real;
+ typedef fftpack_int  integer;
+ 
diff --git a/third_party/pffft/pffft_fuzzer.cc b/third_party/pffft/pffft_fuzzer.cc
new file mode 100644
index 0000000..904ad984
--- /dev/null
+++ b/third_party/pffft/pffft_fuzzer.cc
@@ -0,0 +1,85 @@
+// Copyright 2019 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.
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <cstring>
+
+#include "third_party/pffft/src/pffft.h"
+
+namespace {
+
+#if defined(TRANSFORM_REAL)
+// Real FFT.
+constexpr pffft_transform_t kTransform = PFFFT_REAL;
+constexpr size_t kSizeOfOneSample = sizeof(float);
+#elif defined(TRANSFORM_COMPLEX)
+// Complex FFT.
+constexpr pffft_transform_t kTransform = PFFFT_COMPLEX;
+constexpr size_t kSizeOfOneSample = 2 * sizeof(float);  // Real plus imaginary.
+#else
+#error FFT transform type not defined.
+#endif
+
+bool IsValidSize(size_t n) {
+  if (n == 0) {
+    return false;
+  }
+  // PFFFT only supports transforms for inputs of length N of the form
+  // N = (2^a)*(3^b)*(5^c) where a >= 5, b >=0, c >= 0.
+  constexpr std::array<int, 3> kFactors = {2, 3, 5};
+  std::array<int, kFactors.size()> factorization{};
+  for (size_t i = 0; i < kFactors.size(); ++i) {
+    const int factor = kFactors[i];
+    while (n % factor == 0) {
+      n /= factor;
+      factorization[i]++;
+    }
+  }
+  return factorization[0] >= 5 && n == 1;
+}
+
+float* AllocatePffftBuffer(size_t number_of_bytes) {
+  return static_cast<float*>(pffft_aligned_malloc(number_of_bytes));
+}
+
+}  // namespace
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  // Set the number of FFT points to use |data| as input vector.
+  // The latter is truncated if the number of bytes is not an integer
+  // multiple of the size of one sample (which is either a real or a complex
+  // floating point number).
+  const size_t fft_size = size / kSizeOfOneSample;
+  if (!IsValidSize(fft_size)) {
+    return 0;
+  }
+
+  const size_t number_of_bytes = fft_size * kSizeOfOneSample;
+  assert(number_of_bytes <= size);
+
+  // Allocate input and output buffers.
+  float* in = AllocatePffftBuffer(number_of_bytes);
+  float* out = AllocatePffftBuffer(number_of_bytes);
+
+  // Copy input data.
+  std::memcpy(in, reinterpret_cast<const float*>(data), number_of_bytes);
+
+  // Setup FFT.
+  PFFFT_Setup* pffft_setup = pffft_new_setup(fft_size, kTransform);
+
+  // Call different PFFFT functions to maximize the coverage.
+  pffft_transform(pffft_setup, in, out, nullptr, PFFFT_FORWARD);
+  pffft_zconvolve_accumulate(pffft_setup, out, out, out, 1.f);
+  pffft_transform_ordered(pffft_setup, in, out, nullptr, PFFFT_BACKWARD);
+
+  // Release memory.
+  pffft_aligned_free(in);
+  pffft_aligned_free(out);
+  pffft_destroy_setup(pffft_setup);
+
+  return 0;
+}
diff --git a/third_party/pffft/pffft_unittest.cc b/third_party/pffft/pffft_unittest.cc
new file mode 100644
index 0000000..3f85c16
--- /dev/null
+++ b/third_party/pffft/pffft_unittest.cc
@@ -0,0 +1,194 @@
+// Copyright 2019 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.
+
+#include <cmath>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/pffft/src/fftpack.h"
+#include "third_party/pffft/src/pffft.h"
+
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+namespace pffft {
+namespace test {
+namespace {
+
+static constexpr int kFftSizes[] = {
+    16,  32,      64,  96,  128,  160,  192,  256,  288,  384,   5 * 96, 512,
+    576, 5 * 128, 800, 864, 1024, 2048, 2592, 4000, 4096, 12000, 36864};
+
+double frand() {
+  return rand() / (double)RAND_MAX;
+}
+
+void PffftValidate(int fft_size, bool complex_fft) {
+  PFFFT_Setup* pffft_status =
+      pffft_new_setup(fft_size, complex_fft ? PFFFT_COMPLEX : PFFFT_REAL);
+  ASSERT_TRUE(pffft_status) << "FFT size (" << fft_size << ") not supported.";
+
+  int num_floats = fft_size * (complex_fft ? 2 : 1);
+  int num_bytes = num_floats * sizeof(float);
+  float* ref = static_cast<float*>(pffft_aligned_malloc(num_bytes));
+  float* in = static_cast<float*>(pffft_aligned_malloc(num_bytes));
+  float* out = static_cast<float*>(pffft_aligned_malloc(num_bytes));
+  float* tmp = static_cast<float*>(pffft_aligned_malloc(num_bytes));
+  float* tmp2 = static_cast<float*>(pffft_aligned_malloc(num_bytes));
+
+  for (int pass = 0; pass < 2; ++pass) {
+    SCOPED_TRACE(pass);
+    float ref_max = 0;
+    int k;
+
+    // Compute reference solution with FFTPACK.
+    if (pass == 0) {
+      float* fftpack_buff =
+          static_cast<float*>(malloc(2 * num_bytes + 15 * sizeof(float)));
+      for (k = 0; k < num_floats; ++k) {
+        ref[k] = in[k] = frand() * 2 - 1;
+        out[k] = 1e30;
+      }
+
+      if (!complex_fft) {
+        rffti(fft_size, fftpack_buff);
+        rfftf(fft_size, ref, fftpack_buff);
+        // Use our ordering for real FFTs instead of the one of fftpack.
+        {
+          float refN = ref[fft_size - 1];
+          for (k = fft_size - 2; k >= 1; --k)
+            ref[k + 1] = ref[k];
+          ref[1] = refN;
+        }
+      } else {
+        cffti(fft_size, fftpack_buff);
+        cfftf(fft_size, ref, fftpack_buff);
+      }
+
+      free(fftpack_buff);
+    }
+
+    for (k = 0; k < num_floats; ++k) {
+      ref_max = MAX(ref_max, fabs(ref[k]));
+    }
+
+    // Pass 0: non canonical ordering of transform coefficients.
+    if (pass == 0) {
+      // Test forward transform, with different input / output.
+      pffft_transform(pffft_status, in, tmp, 0, PFFFT_FORWARD);
+      memcpy(tmp2, tmp, num_bytes);
+      memcpy(tmp, in, num_bytes);
+      pffft_transform(pffft_status, tmp, tmp, 0, PFFFT_FORWARD);
+      for (k = 0; k < num_floats; ++k) {
+        SCOPED_TRACE(k);
+        EXPECT_EQ(tmp2[k], tmp[k]);
+      }
+
+      // Test reordering.
+      pffft_zreorder(pffft_status, tmp, out, PFFFT_FORWARD);
+      pffft_zreorder(pffft_status, out, tmp, PFFFT_BACKWARD);
+      for (k = 0; k < num_floats; ++k) {
+        SCOPED_TRACE(k);
+        EXPECT_EQ(tmp2[k], tmp[k]);
+      }
+      pffft_zreorder(pffft_status, tmp, out, PFFFT_FORWARD);
+    } else {
+      // Pass 1: canonical ordering of transform coefficients.
+      pffft_transform_ordered(pffft_status, in, tmp, 0, PFFFT_FORWARD);
+      memcpy(tmp2, tmp, num_bytes);
+      memcpy(tmp, in, num_bytes);
+      pffft_transform_ordered(pffft_status, tmp, tmp, 0, PFFFT_FORWARD);
+      for (k = 0; k < num_floats; ++k) {
+        SCOPED_TRACE(k);
+        EXPECT_EQ(tmp2[k], tmp[k]);
+      }
+      memcpy(out, tmp, num_bytes);
+    }
+
+    {
+      for (k = 0; k < num_floats; ++k) {
+        SCOPED_TRACE(k);
+        EXPECT_NEAR(ref[k], out[k], 1e-3 * ref_max) << "Forward FFT mismatch";
+      }
+
+      if (pass == 0) {
+        pffft_transform(pffft_status, tmp, out, 0, PFFFT_BACKWARD);
+      } else {
+        pffft_transform_ordered(pffft_status, tmp, out, 0, PFFFT_BACKWARD);
+      }
+      memcpy(tmp2, out, num_bytes);
+      memcpy(out, tmp, num_bytes);
+      if (pass == 0) {
+        pffft_transform(pffft_status, out, out, 0, PFFFT_BACKWARD);
+      } else {
+        pffft_transform_ordered(pffft_status, out, out, 0, PFFFT_BACKWARD);
+      }
+      for (k = 0; k < num_floats; ++k) {
+        assert(tmp2[k] == out[k]);
+        out[k] *= 1.f / fft_size;
+      }
+      for (k = 0; k < num_floats; ++k) {
+        SCOPED_TRACE(k);
+        EXPECT_NEAR(in[k], out[k], 1e-3 * ref_max) << "Reverse FFT mismatch";
+      }
+    }
+
+    // Quick test of the circular convolution in FFT domain.
+    {
+      float conv_err = 0, conv_max = 0;
+
+      pffft_zreorder(pffft_status, ref, tmp, PFFFT_FORWARD);
+      memset(out, 0, num_bytes);
+      pffft_zconvolve_accumulate(pffft_status, ref, ref, out, 1.0);
+      pffft_zreorder(pffft_status, out, tmp2, PFFFT_FORWARD);
+
+      for (k = 0; k < num_floats; k += 2) {
+        float ar = tmp[k], ai = tmp[k + 1];
+        if (complex_fft || k > 0) {
+          tmp[k] = ar * ar - ai * ai;
+          tmp[k + 1] = 2 * ar * ai;
+        } else {
+          tmp[0] = ar * ar;
+          tmp[1] = ai * ai;
+        }
+      }
+
+      for (k = 0; k < num_floats; ++k) {
+        float d = fabs(tmp[k] - tmp2[k]), e = fabs(tmp[k]);
+        if (d > conv_err)
+          conv_err = d;
+        if (e > conv_max)
+          conv_max = e;
+      }
+      EXPECT_LT(conv_err, 1e-5 * conv_max) << "zconvolve error";
+    }
+  }
+
+  pffft_destroy_setup(pffft_status);
+  pffft_aligned_free(ref);
+  pffft_aligned_free(in);
+  pffft_aligned_free(out);
+  pffft_aligned_free(tmp);
+  pffft_aligned_free(tmp2);
+}
+
+}  // namespace
+
+TEST(PffftTest, ValidateReal) {
+  for (int fft_size : kFftSizes) {
+    SCOPED_TRACE(fft_size);
+    if (fft_size == 16) {
+      continue;
+    }
+    PffftValidate(fft_size, /*complex_fft=*/false);
+  }
+}
+
+TEST(PffftTest, ValidateComplex) {
+  for (int fft_size : kFftSizes) {
+    SCOPED_TRACE(fft_size);
+    PffftValidate(fft_size, /*complex_fft=*/true);
+  }
+}
+
+}  // namespace test
+}  // namespace pffft
diff --git a/third_party/pffft/src/fftpack.c b/third_party/pffft/src/fftpack.c
index b6375a8..d801d16 100644
--- a/third_party/pffft/src/fftpack.c
+++ b/third_party/pffft/src/fftpack.c
@@ -54,6 +54,10 @@
 #include "fftpack.h"
 #include <math.h>
 
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
 typedef fftpack_real real;
 typedef fftpack_int  integer;
 
diff --git a/third_party/pffft/src/pffft.c b/third_party/pffft/src/pffft.c
index 0603f2b1..b89e6d7b 100644
--- a/third_party/pffft/src/pffft.c
+++ b/third_party/pffft/src/pffft.c
@@ -59,7 +59,7 @@
 
 #include "pffft.h"
 #include <stdlib.h>
-#include <stdio.h>
+// #include <stdio.h>
 #include <math.h>
 #include <assert.h>
 
@@ -82,6 +82,13 @@
 #  define VLA_ARRAY_ON_STACK(type__, varname__, size__) type__ *varname__ = (type__*)_alloca(size__ * sizeof(type__))
 #endif
 
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#ifndef M_SQRT2
+#define M_SQRT2 1.41421356237309504880
+#endif
 
 /* 
    vector support macros: the rest of the code is independant of
@@ -219,31 +226,35 @@
   memcpy(a3.f, f+12, 4*sizeof(float));
 
   t = a0; u = a1; t.v = VZERO();
-  printf("VZERO=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 0, 0, 0, 0);
+  // printf("VZERO=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+  assertv4(t, 0, 0, 0, 0);
   t.v = VADD(a1.v, a2.v);
-  printf("VADD(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 12, 14, 16, 18);
+  // printf("VADD(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+  assertv4(t, 12, 14, 16, 18);
   t.v = VMUL(a1.v, a2.v);
-  printf("VMUL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 32, 45, 60, 77);
+  // printf("VMUL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+  assertv4(t, 32, 45, 60, 77);
   t.v = VMADD(a1.v, a2.v,a0.v);
-  printf("VMADD(4:7,8:11,0:3)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]); assertv4(t, 32, 46, 62, 80);
+  // printf("VMADD(4:7,8:11,0:3)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+  assertv4(t, 32, 46, 62, 80);
 
   INTERLEAVE2(a1.v,a2.v,t.v,u.v);
-  printf("INTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
+  // printf("INTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
   assertv4(t, 4, 8, 5, 9); assertv4(u, 6, 10, 7, 11);
   UNINTERLEAVE2(a1.v,a2.v,t.v,u.v);
-  printf("UNINTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
+  // printf("UNINTERLEAVE2(4:7,8:11)=[%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3], u.f[0], u.f[1], u.f[2], u.f[3]);
   assertv4(t, 4, 6, 8, 10); assertv4(u, 5, 7, 9, 11);
 
   t.v=LD_PS1(f[15]);
-  printf("LD_PS1(15)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+  // printf("LD_PS1(15)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
   assertv4(t, 15, 15, 15, 15);
   t.v = VSWAPHL(a1.v, a2.v);
-  printf("VSWAPHL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
+  // printf("VSWAPHL(4:7,8:11)=[%2g %2g %2g %2g]\n", t.f[0], t.f[1], t.f[2], t.f[3]);
   assertv4(t, 8, 9, 6, 7);
   VTRANSPOSE4(a0.v, a1.v, a2.v, a3.v);
-  printf("VTRANSPOSE4(0:3,4:7,8:11,12:15)=[%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", 
-         a0.f[0], a0.f[1], a0.f[2], a0.f[3], a1.f[0], a1.f[1], a1.f[2], a1.f[3], 
-         a2.f[0], a2.f[1], a2.f[2], a2.f[3], a3.f[0], a3.f[1], a3.f[2], a3.f[3]); 
+  // printf("VTRANSPOSE4(0:3,4:7,8:11,12:15)=[%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g] [%2g %2g %2g %2g]\n", 
+  //        a0.f[0], a0.f[1], a0.f[2], a0.f[3], a1.f[0], a1.f[1], a1.f[2], a1.f[3], 
+  //        a2.f[0], a2.f[1], a2.f[2], a2.f[3], a3.f[0], a3.f[1], a3.f[2], a3.f[3]); 
   assertv4(a0, 0, 4, 8, 12); assertv4(a1, 1, 5, 9, 13); assertv4(a2, 2, 6, 10, 14); assertv4(a3, 3, 7, 11, 15);
 }
 #endif //!PFFFT_SIMD_DISABLE
diff --git a/third_party/pffft/src/pffft.h b/third_party/pffft/src/pffft.h
index 2bfa7b3..e235f12e4 100644
--- a/third_party/pffft/src/pffft.h
+++ b/third_party/pffft/src/pffft.h
@@ -83,92 +83,113 @@
 extern "C" {
 #endif
 
-  /* opaque struct holding internal stuff (precomputed twiddle factors)
-     this struct can be shared by many threads as it contains only
-     read-only data.  
-  */
-  typedef struct PFFFT_Setup PFFFT_Setup;
+#ifndef PFFFT_SIMD_DISABLE
+// Detects compiler bugs with respect to simd instruction.
+void validate_pffft_simd();
+#endif
 
-  /* direction of the transform */
-  typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t;
-  
-  /* type of transform */
-  typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t;
+/* opaque struct holding internal stuff (precomputed twiddle factors)
+   this struct can be shared by many threads as it contains only
+   read-only data.
+*/
+typedef struct PFFFT_Setup PFFFT_Setup;
 
-  /*
-    prepare for performing transforms of size N -- the returned
-    PFFFT_Setup structure is read-only so it can safely be shared by
-    multiple concurrent threads. 
-  */
-  PFFFT_Setup *pffft_new_setup(int N, pffft_transform_t transform);
-  void pffft_destroy_setup(PFFFT_Setup *);
-  /* 
-     Perform a Fourier transform , The z-domain data is stored in the
-     most efficient order for transforming it back, or using it for
-     convolution. If you need to have its content sorted in the
-     "usual" way, that is as an array of interleaved complex numbers,
-     either use pffft_transform_ordered , or call pffft_zreorder after
-     the forward fft, and before the backward fft.
+/* direction of the transform */
+typedef enum { PFFFT_FORWARD, PFFFT_BACKWARD } pffft_direction_t;
 
-     Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x.
-     Typically you will want to scale the backward transform by 1/N.
-     
-     The 'work' pointer should point to an area of N (2*N for complex
-     fft) floats, properly aligned. If 'work' is NULL, then stack will
-     be used instead (this is probably the best strategy for small
-     FFTs, say for N < 16384).
+/* type of transform */
+typedef enum { PFFFT_REAL, PFFFT_COMPLEX } pffft_transform_t;
 
-     input and output may alias.
-  */
-  void pffft_transform(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction);
+/*
+  prepare for performing transforms of size N -- the returned
+  PFFFT_Setup structure is read-only so it can safely be shared by
+  multiple concurrent threads.
+*/
+PFFFT_Setup* pffft_new_setup(int N, pffft_transform_t transform);
+void pffft_destroy_setup(PFFFT_Setup*);
+/*
+   Perform a Fourier transform , The z-domain data is stored in the
+   most efficient order for transforming it back, or using it for
+   convolution. If you need to have its content sorted in the
+   "usual" way, that is as an array of interleaved complex numbers,
+   either use pffft_transform_ordered , or call pffft_zreorder after
+   the forward fft, and before the backward fft.
 
-  /* 
-     Similar to pffft_transform, but makes sure that the output is
-     ordered as expected (interleaved complex numbers).  This is
-     similar to calling pffft_transform and then pffft_zreorder.
-     
-     input and output may alias.
-  */
-  void pffft_transform_ordered(PFFFT_Setup *setup, const float *input, float *output, float *work, pffft_direction_t direction);
+   Transforms are not scaled: PFFFT_BACKWARD(PFFFT_FORWARD(x)) = N*x.
+   Typically you will want to scale the backward transform by 1/N.
 
-  /* 
-     call pffft_zreorder(.., PFFFT_FORWARD) after pffft_transform(...,
-     PFFFT_FORWARD) if you want to have the frequency components in
-     the correct "canonical" order, as interleaved complex numbers.
-     
-     (for real transforms, both 0-frequency and half frequency
-     components, which are real, are assembled in the first entry as
-     F(0)+i*F(n/2+1). Note that the original fftpack did place
-     F(n/2+1) at the end of the arrays).
-     
-     input and output should not alias.
-  */
-  void pffft_zreorder(PFFFT_Setup *setup, const float *input, float *output, pffft_direction_t direction);
+   The 'work' pointer should point to an area of N (2*N for complex
+   fft) floats, properly aligned. If 'work' is NULL, then stack will
+   be used instead (this is probably the best strategy for small
+   FFTs, say for N < 16384).
 
-  /* 
-     Perform a multiplication of the frequency components of dft_a and
-     dft_b and accumulate them into dft_ab. The arrays should have
-     been obtained with pffft_transform(.., PFFFT_FORWARD) and should
-     *not* have been reordered with pffft_zreorder (otherwise just
-     perform the operation yourself as the dft coefs are stored as
-     interleaved complex numbers).
-     
-     the operation performed is: dft_ab += (dft_a * fdt_b)*scaling
-     
-     The dft_a, dft_b and dft_ab pointers may alias.
-  */
-  void pffft_zconvolve_accumulate(PFFFT_Setup *setup, const float *dft_a, const float *dft_b, float *dft_ab, float scaling);
+   input and output may alias.
+*/
+void pffft_transform(PFFFT_Setup* setup,
+                     const float* input,
+                     float* output,
+                     float* work,
+                     pffft_direction_t direction);
 
-  /*
-    the float buffers must have the correct alignment (16-byte boundary
-    on intel and powerpc). This function may be used to obtain such
-    correctly aligned buffers.  
-  */
-  void *pffft_aligned_malloc(size_t nb_bytes);
-  void pffft_aligned_free(void *);
+/*
+   Similar to pffft_transform, but makes sure that the output is
+   ordered as expected (interleaved complex numbers).  This is
+   similar to calling pffft_transform and then pffft_zreorder.
 
-  /* return 4 or 1 wether support SSE/Altivec instructions was enable when building pffft.c */
-  int pffft_simd_size();
+   input and output may alias.
+*/
+void pffft_transform_ordered(PFFFT_Setup* setup,
+                             const float* input,
+                             float* output,
+                             float* work,
+                             pffft_direction_t direction);
+
+/*
+   call pffft_zreorder(.., PFFFT_FORWARD) after pffft_transform(...,
+   PFFFT_FORWARD) if you want to have the frequency components in
+   the correct "canonical" order, as interleaved complex numbers.
+
+   (for real transforms, both 0-frequency and half frequency
+   components, which are real, are assembled in the first entry as
+   F(0)+i*F(n/2+1). Note that the original fftpack did place
+   F(n/2+1) at the end of the arrays).
+
+   input and output should not alias.
+*/
+void pffft_zreorder(PFFFT_Setup* setup,
+                    const float* input,
+                    float* output,
+                    pffft_direction_t direction);
+
+/*
+   Perform a multiplication of the frequency components of dft_a and
+   dft_b and accumulate them into dft_ab. The arrays should have
+   been obtained with pffft_transform(.., PFFFT_FORWARD) and should
+   *not* have been reordered with pffft_zreorder (otherwise just
+   perform the operation yourself as the dft coefs are stored as
+   interleaved complex numbers).
+
+   the operation performed is: dft_ab += (dft_a * fdt_b)*scaling
+
+   The dft_a, dft_b and dft_ab pointers may alias.
+*/
+void pffft_zconvolve_accumulate(PFFFT_Setup* setup,
+                                const float* dft_a,
+                                const float* dft_b,
+                                float* dft_ab,
+                                float scaling);
+
+/*
+  the float buffers must have the correct alignment (16-byte boundary
+  on intel and powerpc). This function may be used to obtain such
+  correctly aligned buffers.
+*/
+void* pffft_aligned_malloc(size_t nb_bytes);
+void pffft_aligned_free(void*);
+
+/* return 4 or 1 wether support SSE/Altivec instructions was enable when
+ * building pffft.c */
+int pffft_simd_size();
 
 #ifdef __cplusplus
 }
diff --git a/third_party/pffft/src/test_pffft.c b/third_party/pffft/src/test_pffft.c
index 512f0ae..deaf98e 100644
--- a/third_party/pffft/src/test_pffft.c
+++ b/third_party/pffft/src/test_pffft.c
@@ -45,6 +45,10 @@
 #  include <fftw3.h>
 #endif
 
+#ifndef M_LN2
+#define M_LN2 0.693147180559945309417
+#endif
+
 #define MAX(x,y) ((x)>(y)?(x):(y))
 
 double frand() {
@@ -361,10 +365,6 @@
   pffft_aligned_free(Z);
 }
 
-#ifndef PFFFT_SIMD_DISABLE
-void validate_pffft_simd(); // a small function inside pffft.c that will detect compiler bugs with respect to simd instruction 
-#endif
-
 int main(int argc, char **argv) {
   int Nvalues[] = { 64, 96, 128, 160, 192, 256, 384, 5*96, 512, 5*128, 3*256, 800, 1024, 2048, 2400, 4096, 8192, 9*1024, 16384, 32768, 256*1024, 1024*1024, -1 };
   int i;
diff --git a/tools/determinism/deterministic_build_whitelist.pyl b/tools/determinism/deterministic_build_whitelist.pyl
index 97b4303..6c3ef009 100644
--- a/tools/determinism/deterministic_build_whitelist.pyl
+++ b/tools/determinism/deterministic_build_whitelist.pyl
@@ -85,7 +85,7 @@
     'content_perftests',
     'content_unittests',
     'courgette_unittests',
-    'crashpad_handler',
+    'chrome_crashpad_handler',
     'crypto_unittests',
     'device_unittests',
     'display_compositor_benchmark',
diff --git a/tools/gn/bootstrap/bootstrap.py b/tools/gn/bootstrap/bootstrap.py
index 1945852..49a33bb 100755
--- a/tools/gn/bootstrap/bootstrap.py
+++ b/tools/gn/bootstrap/bootstrap.py
@@ -52,6 +52,11 @@
       action='store_true',
       help='Do not run GN after building it. Causes --gn-gen-args '
       'to have no effect.')
+  parser.add_option(
+      '--use-custom-libcxx',
+      action='store_true',
+      help='Build with in-tree libc++. This may be necessary if the system '
+      'libstdc++ does not support C++14 features.')
   options, args = parser.parse_args(argv)
   if args:
     parser.error('Unrecognized command line arguments: %s.' % ', '.join(args))
@@ -66,6 +71,40 @@
   gn_path = options.output or os.path.join(out_dir, 'gn')
   gn_build_dir = os.path.join(out_dir, 'gn_build')
 
+  # TODO(thomasanderson): Remove this once Ubuntu Trusty reaches EOL, or when
+  # Chromium's infrastructure is upgraded from Trusty to Xenial, whichever comes
+  # second ideally.  This can be done by reverting this CL:
+  # https://chromium-review.googlesource.com/c/chromium/src/+/1460187/
+  if options.use_custom_libcxx:
+    libcxx_dir = os.path.join(gn_build_dir, 'libc++')
+    if not os.path.exists(libcxx_dir):
+      os.makedirs(libcxx_dir)
+    shutil.copy2(
+        os.path.join(BOOTSTRAP_DIR, 'libc++.ninja'),
+        os.path.join(libcxx_dir, 'build.ninja'))
+    with open(os.path.join(libcxx_dir, 'toolchain.ninja'), 'w') as f:
+      f.write('\n'.join([
+          'cxx = ' + os.environ.get('CXX', 'clang++'),
+          'ar = ' + os.environ.get('AR', 'ar'),
+          'cxxflags = ' + ' '.join(
+              os.environ.get('CFLAGS', '').split() +
+              os.environ.get('CXXFLAGS', '').split()),
+      ]) + '\n')
+    subprocess.check_call(['ninja', '-C', libcxx_dir])
+
+    def append_to_env(var, vals):
+      os.putenv(var, os.environ.get(var, '') + ' ' + ' '.join(vals))
+
+    append_to_env('LDFLAGS', [
+        '-nodefaultlibs', 'libc++.so', '-lc', '-lm', '-Wl,-rpath="\$$ORIGIN/."',
+        '-Wl,-rpath-link=.'
+    ])
+    append_to_env('CXXFLAGS', [
+        '-nostdinc++',
+        ' -isystem../../../buildtools/third_party/libc++/trunk/include',
+        '-isystem../../../buildtools/third_party/libc++abi/trunk/include'
+    ])
+
   cmd = [
       sys.executable,
       os.path.join(GN_ROOT, 'build', 'gen.py'),
diff --git a/tools/gn/bootstrap/libc++.ninja b/tools/gn/bootstrap/libc++.ninja
new file mode 100644
index 0000000..8851010
--- /dev/null
+++ b/tools/gn/bootstrap/libc++.ninja
@@ -0,0 +1,71 @@
+include toolchain.ninja
+
+buildtools = ../../../../buildtools
+libcxx = third_party/libc++/trunk/src
+libcxxabi = third_party/libc++abi/trunk/src
+
+defines = -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D_FILE_OFFSET_BITS=64
+includes = -isystem$buildtools/$libcxx/../include -isystem$buildtools/$libcxxabi/../include
+cflags = $cxxflags $defines $includes -O2 -g0 -pthread -pipe -fno-exceptions -std=c++14 -Wno-c++11-narrowing -nostdinc++ -fPIC
+
+rule cxx_libcxx
+  command = $cxx $cflags -D_LIBCPP_NO_EXCEPTIONS -D_LIBCPP_BUILDING_LIBRARY -DLIBCXX_BUILDING_LIBCXXABI -c $in -o $out
+  description = CXX $out
+
+rule cxx_libcxxabi
+  command = $cxx $cflags -D_LIBCXXABI_NO_EXCEPTIONS -DLIBCXXABI_SILENT_TERMINATE -c $in -o $out
+  description = CXX $out
+
+rule link
+  command = $cxx -shared -fPIC -o $out -Wl,--start-group $in -Wl,--end-group
+  description = LINK $out
+
+build $libcxx/algorithm.o: cxx_libcxx $buildtools/$libcxx/algorithm.cpp
+build $libcxx/any.o: cxx_libcxx $buildtools/$libcxx/any.cpp
+build $libcxx/bind.o: cxx_libcxx $buildtools/$libcxx/bind.cpp
+build $libcxx/chrono.o: cxx_libcxx $buildtools/$libcxx/chrono.cpp
+build $libcxx/condition_variable.o: cxx_libcxx $buildtools/$libcxx/condition_variable.cpp
+build $libcxx/debug.o: cxx_libcxx $buildtools/$libcxx/debug.cpp
+build $libcxx/exception.o: cxx_libcxx $buildtools/$libcxx/exception.cpp
+build $libcxx/functional.o: cxx_libcxx $buildtools/$libcxx/functional.cpp
+build $libcxx/future.o: cxx_libcxx $buildtools/$libcxx/future.cpp
+build $libcxx/hash.o: cxx_libcxx $buildtools/$libcxx/hash.cpp
+build $libcxx/ios.o: cxx_libcxx $buildtools/$libcxx/ios.cpp
+build $libcxx/iostream.o: cxx_libcxx $buildtools/$libcxx/iostream.cpp
+build $libcxx/locale.o: cxx_libcxx $buildtools/$libcxx/locale.cpp
+build $libcxx/memory.o: cxx_libcxx $buildtools/$libcxx/memory.cpp
+build $libcxx/mutex.o: cxx_libcxx $buildtools/$libcxx/mutex.cpp
+build $libcxx/new.o: cxx_libcxx $buildtools/$libcxx/new.cpp
+build $libcxx/optional.o: cxx_libcxx $buildtools/$libcxx/optional.cpp
+build $libcxx/random.o: cxx_libcxx $buildtools/$libcxx/random.cpp
+build $libcxx/regex.o: cxx_libcxx $buildtools/$libcxx/regex.cpp
+build $libcxx/shared_mutex.o: cxx_libcxx $buildtools/$libcxx/shared_mutex.cpp
+build $libcxx/stdexcept.o: cxx_libcxx $buildtools/$libcxx/stdexcept.cpp
+build $libcxx/string.o: cxx_libcxx $buildtools/$libcxx/string.cpp
+build $libcxx/strstream.o: cxx_libcxx $buildtools/$libcxx/strstream.cpp
+build $libcxx/system_error.o: cxx_libcxx $buildtools/$libcxx/system_error.cpp
+build $libcxx/thread.o: cxx_libcxx $buildtools/$libcxx/thread.cpp
+build $libcxx/typeinfo.o: cxx_libcxx $buildtools/$libcxx/typeinfo.cpp
+build $libcxx/utility.o: cxx_libcxx $buildtools/$libcxx/utility.cpp
+build $libcxx/valarray.o: cxx_libcxx $buildtools/$libcxx/valarray.cpp
+build $libcxx/variant.o: cxx_libcxx $buildtools/$libcxx/variant.cpp
+build $libcxx/vector.o: cxx_libcxx $buildtools/$libcxx/vector.cpp
+
+build $libcxxabi/abort_message.o: cxx_libcxxabi $buildtools/$libcxxabi/abort_message.cpp
+build $libcxxabi/cxa_aux_runtime.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_aux_runtime.cpp
+build $libcxxabi/cxa_default_handlers.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_default_handlers.cpp
+build $libcxxabi/cxa_demangle.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_demangle.cpp
+build $libcxxabi/cxa_exception_storage.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_exception_storage.cpp
+build $libcxxabi/cxa_guard.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_guard.cpp
+build $libcxxabi/cxa_handlers.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_handlers.cpp
+build $libcxxabi/cxa_noexception.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_noexception.cpp
+build $libcxxabi/cxa_unexpected.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_unexpected.cpp
+build $libcxxabi/cxa_vector.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_vector.cpp
+build $libcxxabi/cxa_virtual.o: cxx_libcxxabi $buildtools/$libcxxabi/cxa_virtual.cpp
+build $libcxxabi/fallback_malloc.o: cxx_libcxxabi $buildtools/$libcxxabi/fallback_malloc.cpp
+build $libcxxabi/private_typeinfo.o: cxx_libcxxabi $buildtools/$libcxxabi/private_typeinfo.cpp
+build $libcxxabi/stdlib_exception.o: cxx_libcxxabi $buildtools/$libcxxabi/stdlib_exception.cpp
+build $libcxxabi/stdlib_stdexcept.o: cxx_libcxxabi $buildtools/$libcxxabi/stdlib_stdexcept.cpp
+build $libcxxabi/stdlib_typeinfo.o: cxx_libcxxabi $buildtools/$libcxxabi/stdlib_typeinfo.cpp
+
+build ../libc++.so: link $libcxx/algorithm.o $libcxx/any.o $libcxx/bind.o $libcxx/chrono.o $libcxx/condition_variable.o $libcxx/debug.o $libcxx/exception.o $libcxx/functional.o $libcxx/future.o $libcxx/hash.o $libcxx/ios.o $libcxx/iostream.o $libcxx/locale.o $libcxx/memory.o $libcxx/mutex.o $libcxx/new.o $libcxx/optional.o $libcxx/random.o $libcxx/regex.o $libcxx/shared_mutex.o $libcxx/stdexcept.o $libcxx/string.o $libcxx/strstream.o $libcxx/system_error.o $libcxx/thread.o $libcxx/typeinfo.o $libcxx/utility.o $libcxx/valarray.o $libcxx/variant.o $libcxx/vector.o $libcxxabi/abort_message.o $libcxxabi/cxa_aux_runtime.o $libcxxabi/cxa_default_handlers.o $libcxxabi/cxa_demangle.o $libcxxabi/cxa_exception_storage.o $libcxxabi/cxa_guard.o $libcxxabi/cxa_handlers.o $libcxxabi/cxa_noexception.o $libcxxabi/cxa_unexpected.o $libcxxabi/cxa_vector.o $libcxxabi/cxa_virtual.o $libcxxabi/fallback_malloc.o $libcxxabi/private_typeinfo.o $libcxxabi/stdlib_exception.o $libcxxabi/stdlib_stdexcept.o $libcxxabi/stdlib_typeinfo.o
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6e363bf..21c5014 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21533,6 +21533,8 @@
   <int value="2782" label="LayoutJankExplicitlyRequested"/>
   <int value="2783" label="MediaSessionSkipAd"/>
   <int value="2784" label="AdFrameSizeIntervention"/>
+  <int value="2785" label="V8UserActivation_HasBeenActive_AttributeGetter"/>
+  <int value="2786" label="V8UserActivation_IsActive_AttributeGetter"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 1781a98c..69c14d44 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -9759,6 +9759,19 @@
   </summary>
 </histogram>
 
+<histogram name="BackgroundSync.Wakeup.DelayTime" units="ms"
+    expires_after="M75">
+  <owner>nator@chromium.org</owner>
+  <owner>peter@chromium.org</owner>
+  <owner>rayankans@chromium.org</owner>
+  <summary>
+    If BackgroundSync uses BackgroundTaskScheduler to wake up Chrome, this
+    records the delay from the soonest expected wake-up time. Recorded whenever
+    a BackgroundSync background task is called after loading the native parts of
+    the browser.
+  </summary>
+</histogram>
+
 <histogram name="BackgroundTracing.SamplingProfilerUnwindResult"
     enum="SamplingProfilerUnwindResult" expires_after="M71">
   <owner>ssid@chromium.org</owner>
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 929c971..d3a89e3 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -572,6 +572,11 @@
 const viz::LocalSurfaceIdAllocation&
 WindowPortMus::GetLocalSurfaceIdAllocation() {
   static base::NoDestructor<viz::LocalSurfaceIdAllocation> empty_allocation;
+  if (window_mus_type() == WindowMusType::EMBED) {
+    embed_local_surface_id_allocation_ =
+        window_->GetHost()->compositor()->GetLocalSurfaceIdAllocation();
+    return embed_local_surface_id_allocation_;
+  }
   return allocator_ ? allocator_->GetLocalSurfaceIdAllocation()
                     : *empty_allocation;
 }
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h
index bd3878c..5bef261 100644
--- a/ui/aura/mus/window_port_mus.h
+++ b/ui/aura/mus/window_port_mus.h
@@ -361,6 +361,9 @@
 
   bool did_set_frame_sink_ = false;
 
+  // TODO(sky): better factor this.
+  viz::LocalSurfaceIdAllocation embed_local_surface_id_allocation_;
+
   std::unique_ptr<PendingLayerTreeFrameSinkArgs>
       pending_layer_tree_frame_sink_args_;
 
diff --git a/ui/base/x/x11_display_util.cc b/ui/base/x/x11_display_util.cc
index d2e8eda..e34c5cc 100644
--- a/ui/base/x/x11_display_util.cc
+++ b/ui/base/x/x11_display_util.cc
@@ -57,6 +57,50 @@
   return output_to_monitor;
 }
 
+// Sets the work area on a list of displays.  The work area for each display
+// must already be initialized to the display bounds.  At most one display out
+// of |displays| will be affected.
+void ClipWorkArea(std::vector<display::Display>* displays,
+                  int64_t primary_display_index,
+                  float scale) {
+  XDisplay* xdisplay = gfx::GetXDisplay();
+  GLXWindow x_root_window = DefaultRootWindow(xdisplay);
+
+  std::vector<int> value;
+  if (!ui::GetIntArrayProperty(x_root_window, "_NET_WORKAREA", &value) ||
+      value.size() < 4) {
+    return;
+  }
+  gfx::Rect work_area = gfx::ScaleToEnclosingRect(
+      gfx::Rect(value[0], value[1], value[2], value[3]), 1.0f / scale);
+
+  // If the work area entirely contains exactly one display, assume it's meant
+  // for that display (and so do nothing).
+  if (std::count_if(displays->begin(), displays->end(),
+                    [&](const display::Display& display) {
+                      return work_area.Contains(display.bounds());
+                    }) == 1) {
+    return;
+  }
+
+  // If the work area is entirely contained within exactly one display, assume
+  // it's meant for that display and intersect the work area with only that
+  // display.
+  auto found = std::find_if(displays->begin(), displays->end(),
+                            [&](const display::Display& display) {
+                              return display.bounds().Contains(work_area);
+                            });
+
+  // If the work area spans multiple displays, intersect the work area with the
+  // primary display, like GTK does.
+  display::Display& primary =
+      found == displays->end() ? (*displays)[primary_display_index] : *found;
+
+  work_area.Intersect(primary.work_area());
+  if (!work_area.IsEmpty())
+    primary.set_work_area(work_area);
+}
+
 }  // namespace
 
 int GetXrandrVersion(XDisplay* xdisplay) {
@@ -85,9 +129,15 @@
       !display::IsDisplaySizeBlackListed(physical_size)) {
     DCHECK_LE(1.0f, scale);
     gfx_display.SetScaleAndBounds(scale, bounds_in_pixels);
+    gfx_display.set_work_area(
+        gfx::ScaleToEnclosingRect(bounds_in_pixels, 1.0f / scale));
+  } else {
+    scale = 1;
   }
 
-  return {gfx_display};
+  std::vector<display::Display> displays{gfx_display};
+  ClipWorkArea(&displays, 0, scale);
+  return displays;
 }
 
 std::vector<display::Display> BuildDisplaysFromXRandRInfo(
@@ -116,15 +166,6 @@
   int explicit_primary_display_index = -1;
   int monitor_order_primary_display_index = -1;
 
-  bool has_work_area = false;
-  gfx::Rect work_area_in_pixels;
-  std::vector<int> value;
-  if (ui::GetIntArrayProperty(x_root_window, "_NET_WORKAREA", &value) &&
-      value.size() >= 4) {
-    work_area_in_pixels = gfx::Rect(value[0], value[1], value[2], value[3]);
-    has_work_area = true;
-  }
-
   // As per-display scale factor is not supported right now,
   // the X11 root window's scale factor is always used.
   for (int i = 0; i < resources->noutput; ++i) {
@@ -161,20 +202,8 @@
 
       if (!display::Display::HasForceDeviceScaleFactor()) {
         display.SetScaleAndBounds(scale, crtc_bounds);
-      }
-
-      if (has_work_area) {
-        gfx::Rect intersection_in_pixels = crtc_bounds;
-        if (is_primary_display) {
-          intersection_in_pixels.Intersect(work_area_in_pixels);
-        }
-        // SetScaleAndBounds() above does the conversion from pixels to DIP for
-        // us, but set_work_area does not, so we need to do it here.
-        display.set_work_area(gfx::Rect(
-            gfx::ScaleToFlooredPoint(intersection_in_pixels.origin(),
-                                     1.0f / display.device_scale_factor()),
-            gfx::ScaleToFlooredSize(intersection_in_pixels.size(),
-                                    1.0f / display.device_scale_factor())));
+        display.set_work_area(
+            gfx::ScaleToEnclosingRect(crtc_bounds, 1.0f / scale));
       }
 
       switch (crtc->rotation) {
@@ -219,6 +248,7 @@
   if (displays.empty())
     return GetFallbackDisplayList(scale);
 
+  ClipWorkArea(&displays, *primary_display_index_out, scale);
   return displays;
 }
 
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 1cb2596f..9a35b6a1 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -95,9 +95,17 @@
 
 cc::InputHandler::ScrollInputType GestureScrollInputType(
     blink::WebGestureDevice device) {
-  return device == blink::kWebGestureDeviceTouchpad
-             ? cc::InputHandler::WHEEL
-             : cc::InputHandler::TOUCHSCREEN;
+  switch (device) {
+    case blink::kWebGestureDeviceTouchpad:
+      return cc::InputHandler::WHEEL;
+    case blink::kWebGestureDeviceTouchscreen:
+      return cc::InputHandler::TOUCHSCREEN;
+    case blink::kWebGestureDeviceSyntheticAutoscroll:
+      return cc::InputHandler::AUTOSCROLL;
+    default:
+      NOTREACHED();
+      return cc::InputHandler::SCROLL_INPUT_UNKNOWN;
+  }
 }
 
 cc::SnapFlingController::GestureScrollType GestureScrollEventType(