diff --git a/DEPS b/DEPS
index c553f910f..a617575 100644
--- a/DEPS
+++ b/DEPS
@@ -129,11 +129,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': '1f58a8e457baef95ebf081d673859410e6e6c635',
+  'skia_revision': '69da7ad49b55a9085ff4e442b5d67ea110ac5566',
   # 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': 'c6983f4880540c602fe2754ba9dc8329320b95a9',
+  'v8_revision': '72592828213b06ab2f4b69f2ed5685c1e43123b1',
   # 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.
@@ -141,11 +141,11 @@
   # 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': 'fb201c5e208dda3fb657ae3c543f0f6059ecb4f0',
+  'angle_revision': 'b9a71427efd22870c2fdb40462a74d99f06e609d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '0e3d328ac338c4c9474f5d967455ff501bd869cb',
+  'swiftshader_revision': 'bd3af855ae74d9767eb68adcdd1a66c29dc1d81e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -196,7 +196,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '810aaa1e18466131f6a75b1f631fb5b0dd81f344',
+  'catapult_revision': '9057b413d4b19b24786542b710e03cfa62bbb6af',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -805,7 +805,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'fe63720b9960bf520768a169ed0e31c7e834d0a7',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c5403d69e17c0cf99abb9eb10e827515afca499f',
       'condition': 'checkout_linux',
   },
 
@@ -1343,7 +1343,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '688fbfe33779392aa210d67d4aa12cb012f112c2',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '1c747f5717bcbde029a2d496832ee52388dbda05',
+    Var('webrtc_git') + '/src.git' + '@' + '59e875ce18bb24aa6d1ec5cdf128d7ef244fa37a',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 9e2e1414..f56ef0fb8 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -786,7 +786,6 @@
     'build/android/gyp/create_java_binary_script.pydeps',
     'build/android/gyp/create_size_info_files.pydeps',
     'build/android/gyp/create_stack_script.pydeps',
-    'build/android/gyp/create_test_runner_script.pydeps',
     'build/android/gyp/create_tool_wrapper.pydeps',
     'build/android/gyp/desugar.pydeps',
     'build/android/gyp/dexsplitter.pydeps',
diff --git a/ash/ime/ime_engine_factory_registry_unittest.cc b/ash/ime/ime_engine_factory_registry_unittest.cc
index 0414b45..c2b9279d 100644
--- a/ash/ime/ime_engine_factory_registry_unittest.cc
+++ b/ash/ime/ime_engine_factory_registry_unittest.cc
@@ -26,6 +26,15 @@
   }
 
  private:
+  // ime::mojom::ImeEngineClient:
+  void CommitText(const std::string& text) override {}
+  void UpdateCompositionText(const ui::CompositionText& composition,
+                             uint32_t cursor_pos,
+                             bool visible) override {}
+  void DeleteSurroundingText(int32_t offset, uint32_t length) override {}
+  void SendKeyEvent(std::unique_ptr<ui::Event> key_event) override {}
+  void Reconnect() override {}
+
   mojo::Binding<ime::mojom::ImeEngineClient> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(TestImeEngineClient);
diff --git a/base/values.cc b/base/values.cc
index 30b0d76..03acb86 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -407,10 +407,20 @@
   return FindKeyOfType(key, Type::LIST);
 }
 
-bool Value::RemoveKey(StringPiece key) {
+Value* Value::SetKey(StringPiece key, Value&& value) {
+  return SetKeyInternal(key, std::make_unique<Value>(std::move(value)));
+}
+
+Value* Value::SetKey(std::string&& key, Value&& value) {
   CHECK(is_dict());
-  // NOTE: Can't directly return dict_->erase(key) due to MSVC warning C4800.
-  return dict_.erase(key) != 0;
+  return dict_
+      .insert_or_assign(std::move(key),
+                        std::make_unique<Value>(std::move(value)))
+      .first->second.get();
+}
+
+Value* Value::SetKey(const char* key, Value&& value) {
+  return SetKeyInternal(key, std::make_unique<Value>(std::move(value)));
 }
 
 Value* Value::SetBoolKey(StringPiece key, bool value) {
@@ -441,20 +451,20 @@
   return SetKeyInternal(key, std::make_unique<Value>(value));
 }
 
-Value* Value::SetKey(StringPiece key, Value&& value) {
-  return SetKeyInternal(key, std::make_unique<Value>(std::move(value)));
-}
-
-Value* Value::SetKey(std::string&& key, Value&& value) {
+bool Value::RemoveKey(StringPiece key) {
   CHECK(is_dict());
-  return dict_
-      .insert_or_assign(std::move(key),
-                        std::make_unique<Value>(std::move(value)))
-      .first->second.get();
+  return dict_.erase(key) != 0;
 }
 
-Value* Value::SetKey(const char* key, Value&& value) {
-  return SetKeyInternal(key, std::make_unique<Value>(std::move(value)));
+Optional<Value> Value::ExtractKey(StringPiece key) {
+  CHECK(is_dict());
+  auto found = dict_.find(key);
+  if (found == dict_.end())
+    return nullopt;
+
+  Value value = std::move(*found->second);
+  dict_.erase(found);
+  return std::move(value);
 }
 
 Value* Value::FindPath(StringPiece path) {
@@ -572,25 +582,29 @@
 }
 
 bool Value::RemovePath(StringPiece path) {
+  return ExtractPath(path).has_value();
+}
+
+Optional<Value> Value::ExtractPath(StringPiece path) {
   if (!is_dict() || path.empty())
-    return false;
+    return nullopt;
 
   // NOTE: PathSplitter is not being used here because recursion is used to
   // ensure that dictionaries that become empty due to this operation are
   // removed automatically.
   size_t pos = path.find('.');
   if (pos == path.npos)
-    return RemoveKey(path);
+    return ExtractKey(path);
 
   auto found = dict_.find(path.substr(0, pos));
   if (found == dict_.end() || !found->second->is_dict())
-    return false;
+    return nullopt;
 
-  bool removed = found->second->RemovePath(path.substr(pos + 1));
-  if (removed && found->second->dict_.empty())
+  Optional<Value> extracted = found->second->ExtractPath(path.substr(pos + 1));
+  if (extracted && found->second->dict_.empty())
     dict_.erase(found);
 
-  return removed;
+  return extracted;
 }
 
 // DEPRECATED METHODS
diff --git a/base/values.h b/base/values.h
index 60263b56..3d034cd6 100644
--- a/base/values.h
+++ b/base/values.h
@@ -167,7 +167,7 @@
   bool is_dict() const { return type() == Type::DICTIONARY; }
   bool is_list() const { return type() == Type::LIST; }
 
-  // These will all fatally assert if the type doesn't match.
+  // These will all CHECK if the type doesn't match.
   bool GetBool() const;
   int GetInt() const;
   double GetDouble() const;  // Implicitly converts from int if necessary.
@@ -181,7 +181,7 @@
   // a pointer to the element. Otherwise it returns nullptr.
   // returned. Callers are expected to perform a check against null before using
   // the pointer.
-  // Note: This fatally asserts if type() is not Type::DICTIONARY.
+  // Note: This CHECKs if type() is not Type::DICTIONARY.
   //
   // Example:
   //   auto* found = FindKey("foo");
@@ -193,7 +193,7 @@
   // different type nullptr is returned.
   // Callers are expected to perform a check against null before using the
   // pointer.
-  // Note: This fatally asserts if type() is not Type::DICTIONARY.
+  // Note: This CHECKs if type() is not Type::DICTIONARY.
   //
   // Example:
   //   auto* found = FindKey("foo", Type::DOUBLE);
@@ -226,7 +226,7 @@
   // |SetKey| looks up |key| in the underlying dictionary and sets the mapped
   // value to |value|. If |key| could not be found, a new element is inserted.
   // A pointer to the modified item is returned.
-  // Note: This fatally asserts if type() is not Type::DICTIONARY.
+  // Note: This CHECKs if type() is not Type::DICTIONARY.
   // Note: Prefer Set<Type>Key() for simple values.
   //
   // Example:
@@ -251,16 +251,26 @@
   Value* SetStringKey(StringPiece key, std::string&& val);
   Value* SetStringKey(StringPiece key, StringPiece16 val);
 
-  // This attemps to remove the value associated with |key|. In case of failure,
-  // e.g. the key does not exist, |false| is returned and the underlying
+  // This attempts to remove the value associated with |key|. In case of
+  // failure, e.g. the key does not exist, false is returned and the underlying
   // dictionary is not changed. In case of success, |key| is deleted from the
-  // dictionary and the method returns |true|.
-  // Note: This fatally asserts if type() is not Type::DICTIONARY.
+  // dictionary and the method returns true.
+  // Note: This CHECKs if type() is not Type::DICTIONARY.
   //
   // Example:
-  //   bool success = RemoveKey("foo");
+  //   bool success = dict.RemoveKey("foo");
   bool RemoveKey(StringPiece key);
 
+  // This attempts to extract the value associated with |key|. In case of
+  // failure, e.g. the key does not exist, nullopt is returned and the
+  // underlying dictionary is not changed. In case of success, |key| is deleted
+  // from the dictionary and the method returns the extracted Value.
+  // Note: This CHECKs if type() is not Type::DICTIONARY.
+  //
+  // Example:
+  //   Optional<Value> maybe_value = dict.ExtractKey("foo");
+  Optional<Value> ExtractKey(StringPiece key);
+
   // Searches a hierarchy of dictionary values for a given value. If a path
   // of dictionaries exist, returns the item at that path. If any of the path
   // components do not exist or if any but the last path components are not
@@ -282,13 +292,12 @@
   // as either a std::initializer_list<StringPiece> or a
   // span<const StringPiece>. The latter is useful to use a
   // std::vector<std::string> as a parameter but creates huge dynamic
-  // allocations and should be avoided! Note: If there is only one component in
-  // the path, use FindKey() instead.
+  // allocations and should be avoided!
+  // Note: If there is only one component in the path, use FindKey() instead.
+  //
   // Example:
   //   std::vector<StringPiece> components = ...
   //   auto* found = FindPath(components);
-  //
-  // Note: If there is only one component in the path, use FindKey() instead.
   Value* FindPath(std::initializer_list<StringPiece> path);
   Value* FindPath(span<const StringPiece> path);
   const Value* FindPath(std::initializer_list<StringPiece> path) const;
@@ -355,24 +364,33 @@
 
   // Tries to remove a Value at the given path.
   //
-  // If the current value is not a dictionary or any path components does not
+  // If the current value is not a dictionary or any path component does not
   // exist, this operation fails, leaves underlying Values untouched and returns
   // |false|. In case intermediate dictionaries become empty as a result of this
   // path removal, they will be removed as well.
+  // Note: If there is only one component in the path, use ExtractKey() instead.
   //
   // Example:
   //   bool success = value.RemovePath("foo.bar");
-  //
-  //   std::vector<StringPiece> components = ...
-  //   bool success = value.RemovePath(components);
-  //
-  // Note: If there is only one component in the path, use RemoveKey() instead.
   bool RemovePath(StringPiece path);
 
   // Deprecated versions
   bool RemovePath(std::initializer_list<StringPiece> path);
   bool RemovePath(span<const StringPiece> path);
 
+  // Tries to extract a Value at the given path.
+  //
+  // If the current value is not a dictionary or any path component does not
+  // exist, this operation fails, leaves underlying Values untouched and returns
+  // nullopt. In case intermediate dictionaries become empty as a result of this
+  // path removal, they will be removed as well. Returns the extracted value on
+  // success.
+  // Note: If there is only one component in the path, use ExtractKey() instead.
+  //
+  // Example:
+  //   Optional<Value> maybe_value = value.ExtractPath("foo.bar");
+  Optional<Value> ExtractPath(StringPiece path);
+
   using dict_iterator_proxy = detail::dict_iterator_proxy;
   using const_dict_iterator_proxy = detail::const_dict_iterator_proxy;
 
@@ -380,12 +398,12 @@
   // dictionary. These are intended for iteration over all items in the
   // dictionary and are compatible with for-each loops and standard library
   // algorithms.
-  // Note: This fatally asserts if type() is not Type::DICTIONARY.
+  // Note: These CHECK if type() is not Type::DICTIONARY.
   dict_iterator_proxy DictItems();
   const_dict_iterator_proxy DictItems() const;
 
   // Returns the size of the dictionary, and if the dictionary is empty.
-  // Note: This fatally asserts if type() is not Type::DICTIONARY.
+  // Note: These CHECK if type() is not Type::DICTIONARY.
   size_t DictSize() const;
   bool DictEmpty() const;
 
@@ -394,8 +412,7 @@
   // passed in dictionary takes precedence and data already present will be
   // replaced. Values within |dictionary| are deep-copied, so |dictionary| may
   // be freed any time after this call.
-  // Note: This fatally asserts if type() or dictionary->type() is not
-  // Type::DICTIONARY.
+  // Note: This CHECKs if type() or dictionary->type() is not Type::DICTIONARY.
   void MergeDictionary(const Value* dictionary);
 
   // These methods allow the convenient retrieval of the contents of the Value.
@@ -631,18 +648,20 @@
   // |out_value|.  If |out_value| is NULL, the removed value will be deleted.
   // This method returns true if |path| is a valid path; otherwise it will
   // return false and the DictionaryValue object will be unchanged.
-  // DEPRECATED, use Value::RemovePath(path) instead.
+  // DEPRECATED, use Value::RemovePath(path) or Value::ExtractPath(path)
+  // instead.
   bool Remove(StringPiece path, std::unique_ptr<Value>* out_value);
 
   // Like Remove(), but without special treatment of '.'.  This allows e.g. URLs
   // to be used as paths.
-  // DEPRECATED, use Value::RemoveKey(key) instead.
+  // DEPRECATED, use Value::RemoveKey(key) or Value::ExtractKey(key) instead.
   bool RemoveWithoutPathExpansion(StringPiece key,
                                   std::unique_ptr<Value>* out_value);
 
   // Removes a path, clearing out all dictionaries on |path| that remain empty
   // after removing the value at |path|.
-  // DEPRECATED, use Value::RemovePath(path) instead.
+  // DEPRECATED, use Value::RemovePath(path) or Value::ExtractPath(path)
+  // instead.
   bool RemovePath(StringPiece path, std::unique_ptr<Value>* out_value);
 
   using Value::RemovePath;  // DictionaryValue::RemovePath shadows otherwise.
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
index fc71517..884309b 100644
--- a/base/values_unittest.cc
+++ b/base/values_unittest.cc
@@ -1076,6 +1076,20 @@
   EXPECT_FALSE(root.RemoveKey("one"));
 }
 
+TEST(ValuesTest, ExtractKey) {
+  Value root(Value::Type::DICTIONARY);
+  root.SetKey("one", Value(123));
+
+  // Extraction of missing key should fail.
+  EXPECT_EQ(nullopt, root.ExtractKey("two"));
+
+  // Extraction of existing key should succeed.
+  EXPECT_EQ(Value(123), root.ExtractKey("one"));
+
+  // Second extraction of previously existing key should fail.
+  EXPECT_EQ(nullopt, root.ExtractKey("one"));
+}
+
 TEST(ValuesTest, RemovePath) {
   Value root(Value::Type::DICTIONARY);
   root.SetPath("one.two.three", Value(123));
@@ -1090,16 +1104,42 @@
   EXPECT_FALSE(root.RemovePath("one.two.three"));
 
   // Intermediate empty dictionaries should be cleared.
-  EXPECT_FALSE(root.FindKey("one"));
+  EXPECT_EQ(nullptr, root.FindKey("one"));
 
   root.SetPath("one.two.three", Value(123));
   root.SetPath("one.two.four", Value(124));
 
   EXPECT_TRUE(root.RemovePath("one.two.three"));
   // Intermediate non-empty dictionaries should be kept.
-  EXPECT_TRUE(root.FindKey("one"));
-  EXPECT_TRUE(root.FindPath("one.two"));
-  EXPECT_TRUE(root.FindPath("one.two.four"));
+  EXPECT_NE(nullptr, root.FindKey("one"));
+  EXPECT_NE(nullptr, root.FindPath("one.two"));
+  EXPECT_NE(nullptr, root.FindPath("one.two.four"));
+}
+
+TEST(ValuesTest, ExtractPath) {
+  Value root(Value::Type::DICTIONARY);
+  root.SetPath("one.two.three", Value(123));
+
+  // Extraction of missing key should fail.
+  EXPECT_EQ(nullopt, root.ExtractPath("one.two.four"));
+
+  // Extraction of existing key should succeed.
+  EXPECT_EQ(Value(123), root.ExtractPath("one.two.three"));
+
+  // Second extraction of previously existing key should fail.
+  EXPECT_EQ(nullopt, root.ExtractPath("one.two.three"));
+
+  // Intermediate empty dictionaries should be cleared.
+  EXPECT_EQ(nullptr, root.FindKey("one"));
+
+  root.SetPath("one.two.three", Value(123));
+  root.SetPath("one.two.four", Value(124));
+
+  EXPECT_EQ(Value(123), root.ExtractPath("one.two.three"));
+  // Intermediate non-empty dictionaries should be kept.
+  EXPECT_NE(nullptr, root.FindKey("one"));
+  EXPECT_NE(nullptr, root.FindPath("one.two"));
+  EXPECT_NE(nullptr, root.FindPath("one.two.four"));
 }
 
 TEST(ValuesTest, Basic) {
diff --git a/build/android/gyp/create_test_runner_script.py b/build/android/gyp/create_test_runner_script.py
deleted file mode 100755
index bb7c465..0000000
--- a/build/android/gyp/create_test_runner_script.py
+++ /dev/null
@@ -1,163 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2015 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.
-
-"""Creates a script to run an android test using build/android/test_runner.py.
-"""
-
-import argparse
-import os
-import re
-import sys
-
-from util import build_utils
-
-SCRIPT_TEMPLATE = """\
-#!/usr/bin/env vpython
-#
-# This file was generated by build/android/gyp/create_test_runner_script.py
-
-import os
-import sys
-
-def main():
-  script_directory = os.path.dirname(__file__)
-
-  def ResolvePath(path):
-    \"\"\"Returns a normalized filepath given a path relative to this script.
-    \"\"\"
-    return os.path.normpath(os.path.join(script_directory, path))
-
-  test_runner_path = ResolvePath('{test_runner_path}')
-  test_runner_args = {test_runner_args}
-  test_runner_path_args = {test_runner_path_args}
-  for arg, path in test_runner_path_args:
-    test_runner_args.extend([arg, ResolvePath(path)])
-
-  os.execv(test_runner_path,
-           [test_runner_path] + test_runner_args + sys.argv[1:])
-
-if __name__ == '__main__':
-  sys.exit(main())
-"""
-
-
-def _GenerateAdditionalApksErrorString(incremental_apks):
-  err_string = ('Apks that are listed as additional_apks for '
-      'another target cannot be incremental. Please add never_incremental to '
-      'the following apks: %s')
-  return err_string % ', '.join(a for a in incremental_apks)
-
-
-def main(args):
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--script-output-path',
-                      help='Output path for executable script.')
-  parser.add_argument('--test-runner-path',
-                      help='Path to test_runner.py (optional).')
-
-  # We need to intercept any test runner path arguments and make all
-  # of the paths relative to the output script directory.
-  group = parser.add_argument_group('Test runner path arguments.')
-  group.add_argument('--additional-apk', action='append',
-                     dest='additional_apks', default=[])
-  group.add_argument('--additional-apk-list')
-  group.add_argument('--additional-apk-incremental', action='append',
-                     dest='additional_apks_incremental', default=[])
-  group.add_argument('--apk-under-test')
-  group.add_argument('--apk-under-test-incremental-install-json')
-  group.add_argument('--executable-dist-dir')
-  group.add_argument('--isolate-file-path')
-  group.add_argument('--output-directory')
-  group.add_argument('--runtime-deps-path')
-  group.add_argument('--test-apk')
-  group.add_argument('--test-jar')
-  group.add_argument('--test-apk-incremental-install-json')
-  group.add_argument('--coverage-dir')
-  group.add_argument('--android-manifest-path')
-  group.add_argument('--resource-zips')
-  group.add_argument('--robolectric-runtime-deps-dir')
-  args, test_runner_args = parser.parse_known_args(
-      build_utils.ExpandFileArgs(args))
-
-  def RelativizePathToScript(path):
-    """Returns the path relative to the output script directory."""
-    return os.path.relpath(path, os.path.dirname(args.script_output_path))
-
-  test_runner_path = args.test_runner_path or os.path.join(
-      os.path.dirname(__file__), os.path.pardir, 'test_runner.py')
-  test_runner_path = RelativizePathToScript(test_runner_path)
-
-  test_runner_path_args = []
-  if args.additional_apk_list:
-    args.additional_apks.extend(
-        build_utils.ParseGnList(args.additional_apk_list))
-  if args.additional_apks:
-    test_runner_path_args.extend(
-        ('--additional-apk', RelativizePathToScript(a))
-        for a in args.additional_apks)
-  if args.additional_apks_incremental:
-    bad_additional_apks = [a for a in args.additional_apks_incremental
-                           if a != 'None']
-    if bad_additional_apks:
-      raise Exception(_GenerateAdditionalApksErrorString(bad_additional_apks))
-  if args.apk_under_test:
-    test_runner_path_args.append(
-        ('--apk-under-test', RelativizePathToScript(args.apk_under_test)))
-  if args.apk_under_test_incremental_install_json:
-    test_runner_path_args.append(
-        ('--apk-under-test-incremental-install-json',
-         RelativizePathToScript(
-             args.apk_under_test_incremental_install_json)))
-  if args.executable_dist_dir:
-    test_runner_path_args.append(
-        ('--executable-dist-dir',
-         RelativizePathToScript(args.executable_dist_dir)))
-  if args.isolate_file_path:
-    test_runner_path_args.append(
-        ('--isolate-file-path', RelativizePathToScript(args.isolate_file_path)))
-  if args.output_directory:
-    test_runner_path_args.append(
-        ('--output-directory', RelativizePathToScript(args.output_directory)))
-  if args.runtime_deps_path:
-    test_runner_path_args.append(
-        ('--runtime-deps-path', RelativizePathToScript(args.runtime_deps_path)))
-  if args.test_apk:
-    test_runner_path_args.append(
-        ('--test-apk', RelativizePathToScript(args.test_apk)))
-  if args.test_jar:
-    test_runner_path_args.append(
-        ('--test-jar', RelativizePathToScript(args.test_jar)))
-  if args.test_apk_incremental_install_json:
-    test_runner_path_args.append(
-        ('--test-apk-incremental-install-json',
-         RelativizePathToScript(args.test_apk_incremental_install_json)))
-  if args.coverage_dir:
-    test_runner_path_args.append(
-        ('--coverage-dir', RelativizePathToScript(args.coverage_dir)))
-  if args.android_manifest_path:
-    test_runner_path_args.append(
-        ('--android-manifest-path',
-         RelativizePathToScript(args.android_manifest_path)))
-  if args.resource_zips:
-    test_runner_path_args.extend(
-        ('--resource-zip', RelativizePathToScript(r))
-        for r in build_utils.ParseGnList(args.resource_zips))
-  if args.robolectric_runtime_deps_dir:
-    test_runner_path_args.append(
-        ('--robolectric-runtime-deps-dir',
-         RelativizePathToScript(args.robolectric_runtime_deps_dir)))
-
-  with build_utils.AtomicOutput(args.script_output_path) as script:
-    script.write(SCRIPT_TEMPLATE.format(
-        test_runner_path=str(test_runner_path),
-        test_runner_args=str(test_runner_args),
-        test_runner_path_args=str(test_runner_path_args)))
-
-  os.chmod(args.script_output_path, 0750)
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
diff --git a/build/android/gyp/create_test_runner_script.pydeps b/build/android/gyp/create_test_runner_script.pydeps
deleted file mode 100644
index 4b8876b..0000000
--- a/build/android/gyp/create_test_runner_script.pydeps
+++ /dev/null
@@ -1,7 +0,0 @@
-# Generated by running:
-#   build/print_python_deps.py --root build/android/gyp --output build/android/gyp/create_test_runner_script.pydeps build/android/gyp/create_test_runner_script.py
-../../gn_helpers.py
-create_test_runner_script.py
-util/__init__.py
-util/build_utils.py
-util/md5_check.py
diff --git a/build/android/gyp/generate_android_wrapper.py b/build/android/gyp/generate_android_wrapper.py
new file mode 100755
index 0000000..f8e1815
--- /dev/null
+++ b/build/android/gyp/generate_android_wrapper.py
@@ -0,0 +1,42 @@
+#!/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.
+
+import os
+import re
+import sys
+
+from util import build_utils
+
+sys.path.append(
+    os.path.abspath(
+        os.path.join(os.path.dirname(__file__), '..', '..', 'util')))
+
+import generate_wrapper
+
+_WRAPPED_PATH_LIST_RE = re.compile(r'@WrappedPathList\(([^,]+), ([^)]+)\)')
+
+
+def ExpandWrappedPathLists(args):
+  expanded_args = []
+  for arg in args:
+    m = _WRAPPED_PATH_LIST_RE.match(arg)
+    if m:
+      for p in build_utils.ParseGnList(m.group(2)):
+        expanded_args.extend([m.group(1), '@WrappedPath(%s)' % p])
+    else:
+      expanded_args.append(arg)
+  return expanded_args
+
+
+def main(raw_args):
+  parser = generate_wrapper.CreateArgumentParser()
+  expanded_raw_args = build_utils.ExpandFileArgs(raw_args)
+  expanded_raw_args = ExpandWrappedPathLists(expanded_raw_args)
+  args = parser.parse_args(expanded_raw_args)
+  return generate_wrapper.Wrap(args)
+
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv[1:]))
diff --git a/build/android/gyp/util/build_utils.py b/build/android/gyp/util/build_utils.py
index ad26224..a0ef2ee 100644
--- a/build/android/gyp/util/build_utils.py
+++ b/build/android/gyp/util/build_utils.py
@@ -573,9 +573,6 @@
     if not match:
       continue
 
-    if match.end() != len(arg):
-      raise Exception('Unexpected characters after FileArg: ' + arg)
-
     lookup_path = match.group(1).split(':')
     file_path = lookup_path[0]
     if not file_path in file_jsons:
@@ -589,9 +586,10 @@
     # This should match ParseGnList. The output is either a GN-formatted list
     # or a literal (with no quotes).
     if isinstance(expansion, list):
-      new_args[i] = arg[:match.start()] + gn_helpers.ToGNString(expansion)
+      new_args[i] = (arg[:match.start()] + gn_helpers.ToGNString(expansion) +
+                     arg[match.end():])
     else:
-      new_args[i] = arg[:match.start()] + str(expansion)
+      new_args[i] = arg[:match.start()] + str(expansion) + arg[match.end():]
 
   return new_args
 
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 9891d2e..b26bade 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env vpython
 #
 # Copyright 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index ed85335..afb8ca9a 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -8,6 +8,7 @@
 import("//build/config/dcheck_always_on.gni")
 import("//build/config/python.gni")
 import("//build/config/sanitizers/sanitizers.gni")
+import("//build/util/generate_wrapper.gni")
 import("//build_overrides/build.gni")
 assert(is_android)
 
@@ -570,6 +571,18 @@
   }
 }
 
+template("generate_android_wrapper") {
+  generate_wrapper(target_name) {
+    forward_variables_from(invoker, "*")
+    generator_script = "//build/android/gyp/generate_android_wrapper.py"
+    sources = [
+      "//build/android/gyp/util/build_utils.py",
+      "//build/gn_helpers.py",
+      "//build/util/generate_wrapper.py",
+    ]
+  }
+}
+
 # Generates a script in the build bin directory which runs the test
 # target using the test runner script in build/android/test_runner.py.
 template("test_runner_script") {
@@ -607,7 +620,7 @@
     }
   }
 
-  action_with_pydeps(target_name) {
+  generate_android_wrapper(target_name) {
     forward_variables_from(invoker,
                            [
                              "data_deps",
@@ -616,11 +629,17 @@
     if (!defined(deps)) {
       deps = []
     }
+
     if (!defined(data_deps)) {
       data_deps = []
     }
 
-    script = "//build/android/gyp/create_test_runner_script.py"
+    if (defined(android_test_runner_script)) {
+      executable = android_test_runner_script
+    } else {
+      executable = "//build/android/test_runner.py"
+    }
+    testonly = true
 
     data_deps += [
       "//build/android:test_runner_py",
@@ -629,18 +648,20 @@
 
     data = []
 
-    test_runner_args = [
+    executable_args = [
       _test_type,
       "--output-directory",
-      rebase_path(root_build_dir, root_build_dir),
+      "@WrappedPath(.)",
     ]
 
     if (_runtime_deps) {
       deps += [ ":$_runtime_deps_target" ]
       data += [ _runtime_deps_file ]
-      test_runner_args += [
+      _rebased_runtime_deps_file =
+          rebase_path(_runtime_deps_file, root_build_dir)
+      executable_args += [
         "--runtime-deps-path",
-        rebase_path(_runtime_deps_file, root_build_dir),
+        "@WrappedPath(${_rebased_runtime_deps_file})",
       ]
     }
 
@@ -658,28 +679,32 @@
       assert(
           defined(invoker.executable_dist_dir),
           "Must define either apk_target or executable_dist_dir for test_runner_script()")
-      test_runner_args += [
+      _rebased_executable_dist_dir =
+          rebase_path(invoker.executable_dist_dir, root_build_dir)
+      executable_args += [
         "--executable-dist-dir",
-        rebase_path(invoker.executable_dist_dir, root_build_dir),
+        "@WrappedPath(${_rebased_executable_dist_dir})",
       ]
     }
 
     _device_test = true
     if (_test_type == "gtest") {
       assert(defined(invoker.test_suite))
-      test_runner_args += [
+      executable_args += [
         "--suite",
         invoker.test_suite,
       ]
     } else if (_test_type == "instrumentation") {
-      _test_apk = "@FileArg($_rebased_apk_build_config:deps_info:apk_path)"
+      _test_apk = "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:apk_path))"
       if (_incremental_install) {
-        _test_apk = "@FileArg($_rebased_apk_build_config:deps_info:incremental_apk_path)"
+        _test_apk = "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:incremental_apk_path))"
       }
-      test_runner_args += [
-        "--test-apk=$_test_apk",
+      _rebased_test_jar = rebase_path(invoker.test_jar, root_build_dir)
+      executable_args += [
+        "--test-apk",
+        _test_apk,
         "--test-jar",
-        rebase_path(invoker.test_jar, root_build_dir),
+        "@WrappedPath(${_rebased_test_jar})",
       ]
       if (defined(invoker.apk_under_test)) {
         deps += [ "${invoker.apk_under_test}$build_config_target_suffix" ]
@@ -688,38 +713,44 @@
             get_label_info(invoker.apk_under_test, "name") + ".build_config"
         _rebased_apk_under_test_build_config =
             rebase_path(_apk_under_test_build_config, root_build_dir)
-        _apk_under_test =
-            "@FileArg($_rebased_apk_under_test_build_config:deps_info:apk_path)"
+        _apk_under_test = "@WrappedPath(@FileArg($_rebased_apk_under_test_build_config:deps_info:apk_path))"
         if (_incremental_install) {
-          _apk_under_test = "@FileArg($_rebased_apk_under_test_build_config:deps_info:incremental_apk_path)"
+          _apk_under_test = "@WrappedPath(@FileArg($_rebased_apk_under_test_build_config:deps_info:incremental_apk_path))"
         }
-        test_runner_args += [ "--apk-under-test=$_apk_under_test" ]
+        executable_args += [
+          "--apk-under-test",
+          _apk_under_test,
+        ]
       }
       if (defined(invoker.proguard_enabled) && invoker.proguard_enabled) {
-        test_runner_args += [ "--enable-java-deobfuscation" ]
+        executable_args += [ "--enable-java-deobfuscation" ]
       }
       if (emma_coverage) {
         # Set a default coverage output directory (can be overridden by user
         # passing the same flag).
-        test_runner_args += [
+        _rebased_coverage_dir =
+            rebase_path("$root_out_dir/coverage", root_build_dir)
+        executable_args += [
           "--coverage-dir",
-          rebase_path("$root_out_dir/coverage", root_build_dir),
+          "@WrappedPath(${_rebased_coverage_dir})",
         ]
       }
     } else if (_test_type == "junit") {
       assert(defined(invoker.test_suite))
       _device_test = false
-      test_runner_args += [
+      executable_args += [
         "--test-suite",
         invoker.test_suite,
       ]
       if (defined(invoker.android_manifest_path)) {
-        test_runner_args += [
+        _rebased_android_manifest_path =
+            rebase_path(invoker.android_manifest_path, root_build_dir)
+        executable_args += [
           "--android-manifest-path",
-          rebase_path(invoker.android_manifest_path, root_build_dir),
+          "@WrappedPath(${_rebased_android_manifest_path})",
         ]
       } else if (defined(invoker.package_name)) {
-        test_runner_args += [
+        executable_args += [
           "--package-name",
           invoker.package_name,
         ]
@@ -732,20 +763,19 @@
           "${target_gen_dir}/${invoker.test_suite}.build_config"
       _rebased_build_config =
           rebase_path("$_junit_binary_build_config", root_build_dir)
-      test_runner_args += [
-        "--resource-zips",
-        "@FileArg($_rebased_build_config:resources:dependency_zips)",
-      ]
+      executable_args += [ "@WrappedPathList(--resource-zip, @FileArg($_rebased_build_config:resources:dependency_zips))" ]
 
-      test_runner_args += [
+      _rebased_robolectric_runtime_deps_dir =
+          rebase_path("$root_build_dir/lib.java/third_party/robolectric",
+                      root_build_dir)
+      executable_args += [
         "--robolectric-runtime-deps-dir",
-        rebase_path("$root_build_dir/lib.java/third_party/robolectric",
-                    root_build_dir),
+        "@WrappedPath(${_rebased_robolectric_runtime_deps_dir})",
       ]
     } else if (_test_type == "linker") {
-      test_runner_args += [
+      executable_args += [
         "--test-apk",
-        "@FileArg($_rebased_apk_build_config:deps_info:apk_path)",
+        "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:apk_path))",
       ]
     } else {
       assert(false, "Invalid test type: $_test_type.")
@@ -757,57 +787,40 @@
         _build_config = get_label_info(additional_apk, "target_gen_dir") + "/" +
                         get_label_info(additional_apk, "name") + ".build_config"
         _rebased_build_config = rebase_path(_build_config, root_build_dir)
-        test_runner_args += [
+        executable_args += [
           "--additional-apk",
-          "@FileArg($_rebased_build_config:deps_info:apk_path)",
+          "@WrappedPath(@FileArg($_rebased_build_config:deps_info:apk_path))",
           "--additional-apk-incremental",
-          "@FileArg($_rebased_build_config:deps_info:incremental_apk_path)",
+          "@WrappedPath(@FileArg($_rebased_build_config:deps_info:incremental_apk_path))",
         ]
       }
     }
     if (defined(invoker.shard_timeout)) {
-      test_runner_args += [ "--shard-timeout=${invoker.shard_timeout}" ]
+      executable_args += [ "--shard-timeout=${invoker.shard_timeout}" ]
     }
     if (_incremental_install) {
-      test_runner_args += [
+      executable_args += [
         "--test-apk-incremental-install-json",
-        "@FileArg($_rebased_apk_build_config:deps_info:incremental_install_json_path)",
+        "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:incremental_install_json_path))",
       ]
       if (defined(invoker.apk_under_test)) {
-        test_runner_args += [
+        executable_args += [
           "--apk-under-test-incremental-install-json",
-          "@FileArg($_rebased_apk_under_test_build_config:deps_info:incremental_install_json_path)",
+          "@WrappedPath(@FileArg($_rebased_apk_under_test_build_config:deps_info:incremental_install_json_path))",
         ]
       }
-      test_runner_args += [ "--fast-local-dev" ]
+      executable_args += [ "--fast-local-dev" ]
     }
     if (_device_test && is_asan) {
-      test_runner_args += [ "--tool=asan" ]
+      executable_args += [ "--tool=asan" ]
     }
 
     if (defined(invoker.generated_script)) {
       assert(_test_name != "" || true)  # Mark _test_name as used.
-      generated_script = invoker.generated_script
+      wrapper_script = invoker.generated_script
     } else {
-      generated_script = "$root_build_dir/bin/run_${_test_name}"
+      wrapper_script = "$root_build_dir/bin/run_${_test_name}"
     }
-    outputs = [
-      generated_script,
-    ]
-    data += [ generated_script ]
-
-    args = [
-      "--script-output-path",
-      rebase_path(generated_script, root_build_dir),
-    ]
-    if (defined(android_test_runner_script)) {
-      args += [
-        "--test-runner-path",
-        android_test_runner_script,
-      ]
-    }
-
-    args += test_runner_args
   }
 }
 
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 54d2624..7138f702 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8917075804817341376
\ No newline at end of file
+8917022403152524912
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 8c9335d..668df67 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8917084311105425712
\ No newline at end of file
+8917018730856666704
\ No newline at end of file
diff --git a/build/util/generate_wrapper.gni b/build/util/generate_wrapper.gni
index 51658e2..74d9433 100644
--- a/build/util/generate_wrapper.gni
+++ b/build/util/generate_wrapper.gni
@@ -62,6 +62,7 @@
                              "data",
                              "data_deps",
                              "deps",
+                             "sources",
                              "testonly",
                            ])
     script = _generator_script
diff --git a/build/util/generate_wrapper.py b/build/util/generate_wrapper.py
index b89f9fe0..5373e1e 100755
--- a/build/util/generate_wrapper.py
+++ b/build/util/generate_wrapper.py
@@ -11,21 +11,26 @@
 import textwrap
 
 
+# The bash template passes the python script into vpython via stdin.
+# The interpreter doesn't know about the script, so we have bash
+# inject the script location.
 BASH_TEMPLATE = textwrap.dedent(
     """\
-    #!/usr/bin/env bash
-    python - <<END $@
-    _SCRIPT_LOCATION = "${{BASH_SOURCE[0]}}"
+    #!/usr/bin/env vpython
+    _SCRIPT_LOCATION = __file__
     {script}
-    END
     """)
 
 
+# The batch template reruns the batch script with vpython, with the -x
+# flag instructing the interpreter to ignore the first line. The interpreter
+# knows about the (batch) script in this case, so it can get the file location
+# directly.
 BATCH_TEMPLATE = textwrap.dedent(
     """\
     @SETLOCAL ENABLEDELAYEDEXPANSION \
-        & python -x "%~f0" %* \
-        & EXIT /B !ERRORLEVEL!
+      & vpython.bat -x "%~f0" %* \
+      & EXIT /B !ERRORLEVEL!
     _SCRIPT_LOCATION = __file__
     {script}
     """)
diff --git a/cc/paint/paint_worklet_input.h b/cc/paint/paint_worklet_input.h
index 16572914..94e2cfda 100644
--- a/cc/paint/paint_worklet_input.h
+++ b/cc/paint/paint_worklet_input.h
@@ -15,6 +15,7 @@
     : public base::RefCountedThreadSafe<PaintWorkletInput> {
  public:
   virtual gfx::SizeF GetSize() const = 0;
+  virtual int WorkletId() const = 0;
 
  protected:
   friend class base::RefCountedThreadSafe<PaintWorkletInput>;
diff --git a/cc/test/test_paint_worklet_input.cc b/cc/test/test_paint_worklet_input.cc
index 85a8671..2916057 100644
--- a/cc/test/test_paint_worklet_input.cc
+++ b/cc/test/test_paint_worklet_input.cc
@@ -10,4 +10,8 @@
   return container_size_;
 }
 
+int TestPaintWorkletInput::WorkletId() const {
+  return 1u;
+}
+
 }  // namespace cc
diff --git a/cc/test/test_paint_worklet_input.h b/cc/test/test_paint_worklet_input.h
index 1f1510f..8113137c 100644
--- a/cc/test/test_paint_worklet_input.h
+++ b/cc/test/test_paint_worklet_input.h
@@ -13,7 +13,9 @@
  public:
   explicit TestPaintWorkletInput(const gfx::SizeF& size)
       : container_size_(size) {}
+
   gfx::SizeF GetSize() const override;
+  int WorkletId() const override;
 
  protected:
   ~TestPaintWorkletInput() override = default;
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 29f21e2..059e3f0 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -35,6 +35,7 @@
     "//content/public/android:content_java",
     "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:com_android_support_design_java",
+    "//third_party/android_deps:com_android_support_gridlayout_v7_java",
     "//third_party/android_deps:com_android_support_recyclerview_v7_java",
     "//third_party/android_deps:com_android_support_support_compat_java",
     "//third_party/android_deps:com_android_support_support_core_ui_java",
@@ -82,6 +83,7 @@
     "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDrawable.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayEventFilter.java",
+    "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java",
     "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestModel.java",
diff --git a/chrome/android/features/autofill_assistant/java/res/drawable/ic_autofill_assistant_add_circle_24dp.xml b/chrome/android/features/autofill_assistant/java/res/drawable/ic_autofill_assistant_add_circle_24dp.xml
new file mode 100644
index 0000000..fef6777
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/res/drawable/ic_autofill_assistant_add_circle_24dp.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    tools:targetApi="21">
+    <path
+        android:fillColor="@color/default_icon_color_blue"
+        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"/>
+</vector>
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_payment_request.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_payment_request.xml
index 31ee93c..05288a9 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_payment_request.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_payment_request.xml
@@ -4,6 +4,8 @@
      found in the LICENSE file. -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/autofill_assistant_payment_request"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
@@ -17,30 +19,27 @@
         android:orientation="vertical" />
 
     <!-- 3rd party terms & conditions. -->
-    <RadioGroup
+    <Space android:layout_width="0dp" android:layout_height="8dp"/>
+    <org.chromium.chrome.browser.autofill_assistant.payment.AssistantChoiceList
+        android:id="@+id/third_party_terms_list"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:orientation="vertical">
-
-        <!-- Set paddingStart to add padding between radio-button and text. -->
-        <Space android:layout_width="0dp" android:layout_height="9dp"/>
-        <android.support.v7.widget.AppCompatRadioButton
+        app:column_spacing="8dp"
+        app:row_spacing="8dp">
+        <TextView
             android:id="@+id/terms_checkbox_agree"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingStart="9dp"
             android:text="@string/autofill_assistant_3rd_party_terms_accept"
             android:textAppearance="@style/TextAppearance.BlackCaption"/>
-        <Space android:layout_width="0dp" android:layout_height="5dp"/>
-        <android.support.v7.widget.AppCompatRadioButton
+        <TextView
             android:id="@+id/terms_checkbox_review"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingStart="9dp"
             android:text="@string/autofill_assistant_3rd_party_terms_review"
             android:textAppearance="@style/TextAppearance.BlackCaption"/>
-    </RadioGroup>
-    <Space android:layout_width="0dp" android:layout_height="12dp"/>
+    </org.chromium.chrome.browser.autofill_assistant.payment.AssistantChoiceList>
+    <Space android:layout_width="0dp" android:layout_height="10dp"/>
     <TextView
         android:id="@+id/payment_request_3rd_party_privacy_notice"
         android:layout_width="match_parent"
diff --git a/chrome/android/features/autofill_assistant/java/res/values-v17/attrs.xml b/chrome/android/features/autofill_assistant/java/res/values-v17/attrs.xml
new file mode 100644
index 0000000..bb4204c
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/res/values-v17/attrs.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<resources>
+    <declare-styleable name="AssistantChoiceList">
+        <attr name="add_button_text" format="string"/>
+        <attr name="row_spacing" format="dimension"/>
+        <attr name="column_spacing" format="dimension"/>
+
+        <attr name="layout_edit_button_text" format="string"/>
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java
new file mode 100644
index 0000000..7297a69b6
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantChoiceList.java
@@ -0,0 +1,339 @@
+// 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.autofill_assistant.payment;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.GridLayout;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.widget.RadioButton;
+import android.widget.Space;
+import android.widget.TextView;
+
+import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.Callback;
+import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.ui.widget.ChromeImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A widget that displays a list of choices in a regular grid. It is similar to a RadioGroup, but
+ * much more customizable, because it allows arbitrary content views rather than only TextViews.
+ *
+ * The layout is as follows:
+ * [radio-button] |    content   | [edit-button]
+ * [radio-button] |    content   | [edit-button]
+ * ...
+ * [add-icon]     | [add-button] |
+ *
+ * A number of custom view attributes control the layout and look of this widget.
+ *  - Edit-buttons are optional on an item-by-item basis.
+ *  - The add-button at the end of the list is optional.
+ *  - Spacing between rows and columns can be customized.
+ *  - The text for the `add' and `edit' buttons can be customized.
+ */
+public class AssistantChoiceList extends GridLayout {
+    /**
+     * Represents a single choice with a radio button, customizable content and an edit button.
+     */
+    private class Item {
+        final RadioButton mRadioButton;
+        final View mContent;
+        final View mEditButton;
+
+        Item(@Nullable RadioButton radioButton, View content, @Nullable View editButton) {
+            this.mRadioButton = radioButton;
+            this.mContent = content;
+            this.mEditButton = editButton;
+        }
+    }
+
+    /**
+     * |mCanAddItems| is true if the custom view parameter |add_button_text| was specified.
+     * If true, the list will have an additional 'add' button at the end.
+     */
+    private final boolean mCanAddItems;
+    /**
+     * |mAddButton| and |mAddButtonLabel| are guaranteed to be non-null if |mCanAddItems| is true.
+     */
+    private final ChromeImageView mAddButton;
+    private final TextView mAddButtonLabel;
+    private final int mRowSpacing;
+    private final int mColumnSpacing;
+    private final List<Item> mItems = new ArrayList<>();
+
+    private Runnable mAddButtonListener;
+    private Callback<View> mItemSelectedListener;
+    private Callback<View> mEditButtonListener;
+
+    public AssistantChoiceList(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.getTheme().obtainStyledAttributes(
+                attrs, R.styleable.AssistantChoiceList, 0, 0);
+        mCanAddItems = a.hasValue(R.styleable.AssistantChoiceList_add_button_text);
+        String addButtonText =
+                mCanAddItems ? a.getString(R.styleable.AssistantChoiceList_add_button_text) : null;
+        mRowSpacing = a.getDimensionPixelSize(R.styleable.AssistantChoiceList_row_spacing, 0);
+        mColumnSpacing = a.getDimensionPixelSize(R.styleable.AssistantChoiceList_column_spacing, 0);
+        a.recycle();
+
+        // One column for the radio buttons, one for the content, one for the edit buttons.
+        setColumnCount(3);
+
+        if (mCanAddItems) {
+            mAddButton = createAddButtonIcon();
+            mAddButtonLabel = createAddButtonLabel(addButtonText);
+
+            addViewInternal(mAddButton, -1, createRadioButtonLayoutParams());
+            GridLayout.LayoutParams lp = createContentLayoutParams();
+            lp.columnSpec = GridLayout.spec(UNDEFINED, 2);
+            addViewInternal(mAddButtonLabel, -1, lp);
+
+            // Set margin to 0 because list is currently empty.
+            updateAddButtonMargins(0);
+        } else {
+            mAddButton = null;
+            mAddButtonLabel = null;
+        }
+    }
+
+    /**
+     * Children of this container are automatically added as selectable items to the list.
+     *
+     * This method is automatically called by layout inflaters and xml files. In code, you usually
+     * want to call |addItem| directly.
+     */
+    @Override
+    public void addView(View view, int index, ViewGroup.LayoutParams lp) {
+        assert index != -1;
+        String editText = null;
+        if (lp instanceof AssistantChoiceList.LayoutParams) {
+            editText = ((LayoutParams) lp).mEditText;
+        }
+
+        addItem(view, editText);
+    }
+
+    /**
+     * Adds an item to the list. Additional widgets to select and edit the item are created as
+     * necessary.
+     * @param view The view to add to the list.
+     * @param editButtonText The text of the edit button to display next to |view|. Can be null to
+     * indicate that no edit button should be provided.
+     */
+    public void addItem(View view, @Nullable String editButtonText) {
+        RadioButton radioButton = new RadioButton(getContext());
+        // Insert at end, before the `add' button (if any).
+        int index = mCanAddItems ? indexOfChild(mAddButton) : getChildCount();
+        addViewInternal(radioButton, index++, createRadioButtonLayoutParams());
+        addViewInternal(view, index++, createContentLayoutParams());
+
+        TextView editButton = null;
+        if (editButtonText != null) {
+            editButton = (TextView) LayoutInflater.from(getContext())
+                                 .inflate(R.layout.autofill_assistant_button_hairline,
+                                         /*parent = */ null);
+            editButton.setText(editButtonText);
+            editButton.setOnClickListener(unusedView -> {
+                if (mEditButtonListener != null) {
+                    mEditButtonListener.onResult(view);
+                }
+            });
+            addViewInternal(editButton, index++, createEditButtonLayoutParams());
+        } else {
+            View spacer = new Space(getContext());
+            addViewInternal(spacer, index++, createEditButtonLayoutParams());
+        }
+
+        Item item = new Item(radioButton, view, editButton);
+        radioButton.setOnClickListener(unusedView -> setCheckedItem(item));
+        // TODO(crbug.com/806868): Forward event to radiobutton to re-trigger animation.
+        view.setOnClickListener(unusedView -> setCheckedItem(item));
+        mItems.add(item);
+
+        // Need to adjust button margins after first item was inserted.
+        if (mItems.size() == 1) {
+            updateAddButtonMargins(mRowSpacing);
+        }
+    }
+
+    public void setOnAddButtonClickedListener(Runnable listener) {
+        mAddButtonListener = listener;
+    }
+
+    public void setOnEditButtonClickedListener(Callback<View> listener) {
+        mEditButtonListener = listener;
+    }
+
+    public void setOnItemSelectedListener(Callback<View> listener) {
+        mItemSelectedListener = listener;
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new AssistantChoiceList.LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        return new AssistantChoiceList.LayoutParams();
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+        if (lp instanceof LayoutParams) {
+            return new LayoutParams((LayoutParams) lp);
+        } else if (lp instanceof GridLayout.LayoutParams) {
+            return new LayoutParams((GridLayout.LayoutParams) lp);
+        }
+        return new LayoutParams(lp);
+    }
+
+    // Override to allow type-checking of LayoutParams.
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof AssistantChoiceList.LayoutParams;
+    }
+
+    /**
+     * Adds a view to the underlying gridlayout.
+     *
+     * This method is used internally to add a view to the actual layout. A single call to |addView|
+     * will result in multiple calls to |addViewInternal|, because additional widgets are
+     * automatically generated (e.g., radio-buttons and edit-buttons).
+     * @param view The view to add to the layout.
+     * @param index The index at which to insert the view into the layout. Note that this - along
+     * with the column width specified in |lp| - will determine the column in which the view will
+     * end up in.
+     * @param lp Additional layout parameters, see |GridLayout.LayoutParams|.
+     */
+    private void addViewInternal(View view, int index, ViewGroup.LayoutParams lp) {
+        super.addView(view, index, lp);
+    }
+
+    private ChromeImageView createAddButtonIcon() {
+        ChromeImageView addButtonIcon = new ChromeImageView(getContext());
+        addButtonIcon.setImageResource(R.drawable.ic_autofill_assistant_add_circle_24dp);
+        addButtonIcon.setOnClickListener(unusedView -> {
+            if (mAddButtonListener != null) {
+                mAddButtonListener.run();
+            }
+        });
+        return addButtonIcon;
+    }
+
+    private TextView createAddButtonLabel(String addButtonText) {
+        TextView addButtonLabel = new TextView(getContext());
+        ApiCompatibilityUtils.setTextAppearance(
+                addButtonLabel, R.style.TextAppearance_BlueButtonText2);
+        addButtonLabel.setText(addButtonText);
+        addButtonLabel.setOnClickListener(unusedView -> {
+            if (mAddButtonListener != null) {
+                mAddButtonListener.run();
+            }
+        });
+        return addButtonLabel;
+    }
+
+    private GridLayout.LayoutParams createContentLayoutParams() {
+        // Set layout params to let content grow to maximum size.
+        GridLayout.LayoutParams lp =
+                new GridLayout.LayoutParams(GridLayout.spec(UNDEFINED), GridLayout.spec(1, 1));
+        lp.setGravity(Gravity.FILL_HORIZONTAL | Gravity.CENTER_VERTICAL);
+        lp.width = 0;
+        lp.topMargin = mItems.isEmpty() ? 0 : mRowSpacing;
+        return lp;
+    }
+
+    private GridLayout.LayoutParams createRadioButtonLayoutParams() {
+        GridLayout.LayoutParams lp =
+                new GridLayout.LayoutParams(GridLayout.spec(UNDEFINED), GridLayout.spec(0, 1));
+        lp.setGravity(Gravity.CENTER);
+        lp.setMarginEnd(mColumnSpacing);
+        lp.topMargin = mItems.isEmpty() ? 0 : mRowSpacing;
+        return lp;
+    }
+
+    private GridLayout.LayoutParams createEditButtonLayoutParams() {
+        GridLayout.LayoutParams lp =
+                new GridLayout.LayoutParams(GridLayout.spec(UNDEFINED), GridLayout.spec(2, 1));
+        lp.setGravity(Gravity.CENTER_VERTICAL);
+        lp.setMarginStart(mColumnSpacing);
+        lp.topMargin = mItems.isEmpty() ? 0 : mRowSpacing;
+        return lp;
+    }
+
+    /**
+     * Adjusts the margins of the 'add' button.
+     *
+     * For empty lists, the margins should be 0. For non-empty lists, the margins should be equal
+     * to |mRowSpacing|.
+     */
+    private void updateAddButtonMargins(int marginTop) {
+        if (!mCanAddItems) {
+            return;
+        }
+
+        LayoutParams lp = (LayoutParams) mAddButton.getLayoutParams();
+        lp.setMargins(lp.leftMargin, marginTop, lp.rightMargin, lp.bottomMargin);
+        mAddButton.setLayoutParams(lp);
+
+        lp = (LayoutParams) mAddButtonLabel.getLayoutParams();
+        lp.setMargins(lp.leftMargin, marginTop, lp.rightMargin, lp.bottomMargin);
+        mAddButtonLabel.setLayoutParams(lp);
+    }
+
+    private void setCheckedItem(Item item) {
+        boolean changed = false;
+        for (int i = 0; i < mItems.size(); i++) {
+            RadioButton radioButton = mItems.get(i).mRadioButton;
+            boolean isItem = mItems.get(i) == item;
+            changed |= isItem && !radioButton.isChecked();
+            radioButton.setChecked(isItem);
+        }
+
+        if (changed && mItemSelectedListener != null) {
+            mItemSelectedListener.onResult(item.mContent);
+        }
+    }
+
+    /**
+     * Per-child layout information associated with AssistantChoiceList.
+     */
+    public static class LayoutParams extends GridLayout.LayoutParams {
+        /**
+         * Indicates whether an 'edit' button should be added for this item.
+         */
+        @ViewDebug.ExportedProperty(category = "layout")
+        public String mEditText;
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.AssistantChoiceList);
+            mEditText = a.getString(R.styleable.AssistantChoiceList_layout_edit_button_text);
+            a.recycle();
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams p) {
+            super(p);
+        }
+
+        public LayoutParams(GridLayout.LayoutParams p) {
+            super(p);
+        }
+
+        private LayoutParams() {
+            super();
+        }
+    }
+}
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestUI.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestUI.java
index 1d8d9905..39966aca 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestUI.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestUI.java
@@ -15,7 +15,6 @@
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.widget.LinearLayout;
-import android.widget.RadioButton;
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
@@ -120,8 +119,6 @@
     private FadingEdgeScrollView mRequestViewContainer;
     private ViewGroup mRequestView;
     private LinearLayout mPaymentContainerLayout;
-    private RadioButton mAcceptThirdPartyConditions;
-    private RadioButton mReviewThirdPartyConditions;
 
     private boolean mRequestShipping;
     private boolean mRequestContactDetails;
@@ -200,17 +197,26 @@
 
     private void prepareRequestView(String origin) {
         // Set terms & conditions text.
-        mAcceptThirdPartyConditions = mRequestView.findViewById(R.id.terms_checkbox_agree);
-        mReviewThirdPartyConditions = mRequestView.findViewById(R.id.terms_checkbox_review);
+        AssistantChoiceList thirdPartyTermsList =
+                mRequestView.findViewById(R.id.third_party_terms_list);
+        TextView acceptThirdPartyConditions =
+                thirdPartyTermsList.findViewById(R.id.terms_checkbox_agree);
+        TextView reviewThirdPartyConditions =
+                thirdPartyTermsList.findViewById(R.id.terms_checkbox_review);
         StyleSpan boldSpan = new StyleSpan(android.graphics.Typeface.BOLD);
-        mAcceptThirdPartyConditions.setText(SpanApplier.applySpans(
+        acceptThirdPartyConditions.setText(SpanApplier.applySpans(
                 mActivity.getString(R.string.autofill_assistant_3rd_party_terms_accept, origin),
                 new SpanApplier.SpanInfo("<b>", "</b>", boldSpan)));
-        mReviewThirdPartyConditions.setText(SpanApplier.applySpans(
+        reviewThirdPartyConditions.setText(SpanApplier.applySpans(
                 mActivity.getString(R.string.autofill_assistant_3rd_party_terms_review, origin),
                 new SpanApplier.SpanInfo("<b>", "</b>", boldSpan)));
-        mAcceptThirdPartyConditions.setOnClickListener(this);
-        mReviewThirdPartyConditions.setOnClickListener(this);
+        thirdPartyTermsList.setOnItemSelectedListener((view) -> {
+            if (view == acceptThirdPartyConditions) {
+                mClient.onCheckAcceptTermsAndConditions(true);
+            } else if (view == reviewThirdPartyConditions) {
+                mClient.onCheckReviewTermsAndConditions(true);
+            }
+        });
 
         // Set 3rd party privacy notice text.
         TextView thirdPartyPrivacyNotice =
@@ -430,10 +436,6 @@
             expand(mContactDetailsSection);
         } else if (v == mPaymentMethodSection) {
             expand(mPaymentMethodSection);
-        } else if (v == mAcceptThirdPartyConditions) {
-            mClient.onCheckAcceptTermsAndConditions(mAcceptThirdPartyConditions.isChecked());
-        } else if (v == mReviewThirdPartyConditions) {
-            mClient.onCheckReviewTermsAndConditions(mReviewThirdPartyConditions.isChecked());
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigator.java
index 12a04095..0edd9e6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigator.java
@@ -6,9 +6,13 @@
 
 import android.support.annotation.Nullable;
 
+import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController;
+import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationHistory;
+import org.chromium.content_public.browser.WebContents;
 
 import javax.inject.Inject;
 
@@ -26,9 +30,15 @@
 @ActivityScope
 public class CloseButtonNavigator {
     @Nullable private PageCriteria mLandingPageCriteria;
+    private final CustomTabActivityTabController mTabController;
+    private final CustomTabActivityTabProvider mTabProvider;
 
     @Inject
-    public CloseButtonNavigator() {}
+    public CloseButtonNavigator(CustomTabActivityTabController tabController,
+            CustomTabActivityTabProvider tabProvider) {
+        mTabController = tabController;
+        mTabProvider = tabProvider;
+    }
 
     // TODO(peconn): Replace with Predicate<T> when we can use Java 8 libraries.
     /** An interface that allows specifying if a URL matches some criteria. */
@@ -44,17 +54,48 @@
         mLandingPageCriteria = criteria;
     }
 
+    private boolean isLandingPage(String url) {
+        return mLandingPageCriteria != null && mLandingPageCriteria.matches(url);
+    }
+
     /**
-     * Navigates to the most recent landing page. Returns {@code false} if no criteria for what is
-     * a landing page has been given or no such page can be found.
+     * Handles navigation and Tab closures that should occur when the close button is pressed. It
+     * searches for a landing page in the history of the current Tab and then closes it if none are
+     * found. This continues until a landing page is found or all Tabs are closed.
      */
-    public boolean navigateOnClose(@Nullable NavigationController controller) {
+    public void navigateOnClose() {
+        while (mTabProvider.getTab() != null) {
+            // See if there's a close button navigation in our current Tab.
+            NavigationController navigationController = getNavigationController();
+            if (navigationController != null && navigateSingleTab(getNavigationController())) {
+                return;
+            }
+
+            mTabController.closeTab();
+
+            // Check whether the close button navigation would have stopped on the newly revealed
+            // Tab. We don't check this at the start of the loop (or make navigateSingleTab
+            // consider the current Tab) because in that case if the user started on a landing page,
+            // we would not navigate at all. (Admittedly though this case would never happen at the
+            // time of writing since landing pages don't show the close button).
+            Tab nextTab = mTabProvider.getTab();
+            if (nextTab != null && isLandingPage(mTabProvider.getTab().getUrl())) {
+                return;
+            }
+        }
+    }
+
+    /**
+     * Navigates to the most recent landing page on the current Tab. Returns {@code false} if no
+     * criteria for what is a landing page has been given or no such page can be found.
+     */
+    private boolean navigateSingleTab(@Nullable NavigationController controller) {
         if (mLandingPageCriteria == null || controller == null) return false;
 
         NavigationHistory history = controller.getNavigationHistory();
         for (int i = history.getCurrentEntryIndex() - 1; i >= 0; i--) {
             String url = history.getEntryAtIndex(i).getUrl();
-            if (!mLandingPageCriteria.matches(url)) continue;
+            if (!isLandingPage(url)) continue;
 
             controller.goToNavigationIndex(i);
             return true;
@@ -62,4 +103,13 @@
 
         return false;
     }
+
+    @Nullable
+    private NavigationController getNavigationController() {
+        Tab tab = mTabProvider.getTab();
+        if (tab == null) return null;
+        WebContents webContents = tab.getWebContents();
+        if (webContents == null) return null;
+        return webContents.getNavigationController();
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
index 0258966..f06e69a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
@@ -38,9 +38,7 @@
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.content_public.browser.LoadUrlParams;
-import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
-import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.base.PageTransition;
 
 import java.lang.annotation.Retention;
@@ -100,14 +98,14 @@
 
     private boolean mIsFinishing;
 
-    private boolean mIsClosingTabOnBack;
+    private boolean mIsHandlingUserNavigation;
 
     private final CustomTabActivityTabProvider.Observer mTabObserver =
             new CustomTabActivityTabProvider.Observer() {
 
         @Override
         public void onAllTabsClosed() {
-            finish(mIsClosingTabOnBack ? USER_NAVIGATION : OTHER);
+            finish(mIsHandlingUserNavigation ? USER_NAVIGATION : OTHER);
         }
     };
 
@@ -205,32 +203,21 @@
     private void executeDefaultBackHandling() {
         if (mToolbarManager.get().back()) return;
 
-        // mTabController.closeTab may result in either closing the only tab, or swapping to the
-        // previous tab. In the first case we need finish to be called with USER_NAVIGATION reason.
-        mIsClosingTabOnBack = true;
+        // mTabController.closeTab may result in either closing the only tab (through the back
+        // button or the close button), or swapping to the previous tab. In the first case we need
+        // finish to be called with USER_NAVIGATION reason.
+        mIsHandlingUserNavigation = true;
         mTabController.closeTab();
-        mIsClosingTabOnBack = false;
+        mIsHandlingUserNavigation = false;
     }
 
     /**
      * Handles close button navigation.
      */
     public void navigateOnClose() {
-        NavigationController navigationController = getNavigationController();
-        if (navigationController != null
-                && mCloseButtonNavigator.navigateOnClose(navigationController)) {
-            return;
-        }
-        finish(USER_NAVIGATION);
-    }
-
-    @Nullable
-    private NavigationController getNavigationController() {
-        Tab tab = mTabProvider.getTab();
-        if (tab == null) return null;
-        WebContents webContents = tab.getWebContents();
-        if (webContents == null) return null;
-        return webContents.getNavigationController();
+        mIsHandlingUserNavigation = true;
+        mCloseButtonNavigator.navigateOnClose();
+        mIsHandlingUserNavigation = false;
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
index 05f9087..f2dd7f4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityTabController.java
@@ -162,7 +162,7 @@
      * case links with target="_blank" were followed. See the comment to
      * {@link CustomTabActivityTabProvider.Observer#onAllTabsClosed}.
      */
-    void closeTab() {
+    public void closeTab() {
         mTabFactory.getTabModelSelector().getCurrentModel().closeTab(mTabProvider.getTab(),
                 false, false, false);
     }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index cb5e99e1..d86e131 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -8,6 +8,8 @@
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 
 import static org.chromium.base.test.util.Restriction.RESTRICTION_TYPE_NON_LOW_END_DEVICE;
 import static org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule.LONG_TIMEOUT_MS;
@@ -119,6 +121,7 @@
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.WebContentsObserver;
+import org.chromium.content_public.browser.test.util.ClickUtils;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.DOMUtils;
@@ -156,6 +159,8 @@
             "/chrome/test/data/geolocation/geolocation_on_load.html";
     private static final String SELECT_POPUP_PAGE = "/chrome/test/data/android/select.html";
     private static final String FRAGMENT_TEST_PAGE = "/chrome/test/data/android/fragment.html";
+    private static final String TARGET_BLANK_TEST_PAGE =
+            "/chrome/test/data/android/cct_target_blank.html";
     private static final String TEST_MENU_TITLE = "testMenuTitle";
     private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "chrome";
     private static final String WEBLITE_PREFIX = "http://googleweblight.com/i?u=";
@@ -399,7 +404,7 @@
         final int expectedMenuSize = 12;
         Menu menu = ContextMenuUtils.openContextMenu(
                 mCustomTabActivityTestRule.getActivity().getActivityTab(), "logo");
-        Assert.assertEquals(expectedMenuSize, menu.size());
+        assertEquals(expectedMenuSize, menu.size());
 
         Assert.assertNotNull(menu.findItem(R.id.contextmenu_copy_link_address));
         Assert.assertNotNull(menu.findItem(R.id.contextmenu_call));
@@ -442,7 +447,7 @@
         final int expectedMenuSize = 12;
         Menu menu = ContextMenuUtils.openContextMenu(
                 mCustomTabActivityTestRule.getActivity().getActivityTab(), "aboutLink");
-        Assert.assertEquals(expectedMenuSize, menu.size());
+        assertEquals(expectedMenuSize, menu.size());
 
         Assert.assertNotNull(menu.findItem(R.id.contextmenu_copy_link_address));
         Assert.assertNotNull(menu.findItem(R.id.contextmenu_call));
@@ -484,7 +489,7 @@
         final int expectedMenuSize = 12;
         Menu menu = ContextMenuUtils.openContextMenu(
                 mCustomTabActivityTestRule.getActivity().getActivityTab(), "email");
-        Assert.assertEquals(expectedMenuSize, menu.size());
+        assertEquals(expectedMenuSize, menu.size());
 
         Assert.assertNotNull(menu.findItem(R.id.contextmenu_copy_link_address));
         Assert.assertNotNull(menu.findItem(R.id.contextmenu_call));
@@ -526,7 +531,7 @@
         final int expectedMenuSize = 12;
         Menu menu = ContextMenuUtils.openContextMenu(
                 mCustomTabActivityTestRule.getActivity().getActivityTab(), "tel");
-        Assert.assertEquals(expectedMenuSize, menu.size());
+        assertEquals(expectedMenuSize, menu.size());
 
         Assert.assertNotNull(menu.findItem(R.id.contextmenu_copy_link_address));
         Assert.assertNotNull(menu.findItem(R.id.contextmenu_call));
@@ -572,8 +577,8 @@
         final int expectedMenuSize = numMenuEntries + NUM_CHROME_MENU_ITEMS;
 
         Assert.assertNotNull("App menu is not initialized: ", menu);
-        Assert.assertEquals(expectedMenuSize, getActualMenuSize(menu));
-        Assert.assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
+        assertEquals(expectedMenuSize, getActualMenuSize(menu));
+        assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
         Assert.assertNotNull(menu.findItem(R.id.forward_menu_id));
         Assert.assertNotNull(menu.findItem(R.id.bookmark_this_page_id));
         Assert.assertNotNull(menu.findItem(R.id.offline_page_id));
@@ -605,8 +610,8 @@
         final int expectedMenuSize = 0;
 
         Assert.assertNotNull("App menu is not initialized: ", menu);
-        Assert.assertEquals(expectedMenuSize, getActualMenuSize(menu));
-        Assert.assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
+        assertEquals(expectedMenuSize, getActualMenuSize(menu));
+        assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
     }
 
     /**
@@ -627,8 +632,8 @@
         final int expectedMenuSize = 2;
 
         Assert.assertNotNull("App menu is not initialized: ", menu);
-        Assert.assertEquals(expectedMenuSize, getActualMenuSize(menu));
-        Assert.assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
+        assertEquals(expectedMenuSize, getActualMenuSize(menu));
+        assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
         Assert.assertTrue(menu.findItem(R.id.find_in_page_id).isVisible());
         Assert.assertTrue(menu.findItem(R.id.reader_mode_prefs_id).isVisible());
     }
@@ -651,8 +656,8 @@
         final int expectedMenuSize = 3;
 
         Assert.assertNotNull("App menu is not initialized: ", menu);
-        Assert.assertEquals(expectedMenuSize, getActualMenuSize(menu));
-        Assert.assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
+        assertEquals(expectedMenuSize, getActualMenuSize(menu));
+        assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
         Assert.assertTrue(menu.findItem(R.id.find_in_page_id).isVisible());
         Assert.assertNotNull(menu.findItem(R.id.request_desktop_site_row_menu_id));
 
@@ -661,7 +666,7 @@
         Assert.assertNotNull(icon_row.hasSubMenu());
         SubMenu icon_row_menu = icon_row.getSubMenu();
         final int expectedIconMenuSize = 4;
-        Assert.assertEquals(expectedIconMenuSize, getVisibleMenuSize(icon_row_menu));
+        assertEquals(expectedIconMenuSize, getVisibleMenuSize(icon_row_menu));
         Assert.assertNotNull(icon_row_menu.findItem(R.id.forward_menu_id));
         Assert.assertNotNull(icon_row_menu.findItem(R.id.bookmark_this_page_id));
         Assert.assertNotNull(icon_row_menu.findItem(R.id.info_menu_id));
@@ -703,8 +708,8 @@
         Menu menu = mCustomTabActivityTestRule.getMenu();
         final int expectedMenuSize = MAX_MENU_CUSTOM_ITEMS + NUM_CHROME_MENU_ITEMS;
         Assert.assertNotNull("App menu is not initialized: ", menu);
-        Assert.assertEquals(expectedMenuSize, getActualMenuSize(menu));
-        Assert.assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
+        assertEquals(expectedMenuSize, getActualMenuSize(menu));
+        assertEquals(expectedMenuSize, getVisibleMenuSize(menu));
     }
 
     /**
@@ -882,7 +887,7 @@
         Assert.assertTrue(
                 "A custom tab toolbar is never shown", toolbarView instanceof CustomTabToolbar);
         CustomTabToolbar toolbar = (CustomTabToolbar) toolbarView;
-        Assert.assertEquals(expectedColor, toolbar.getBackground().getColor());
+        assertEquals(expectedColor, toolbar.getBackground().getColor());
         Assert.assertFalse(mCustomTabActivityTestRule.getActivity()
                                    .getToolbarManager()
                                    .getLocationBarModelForTesting()
@@ -890,10 +895,10 @@
         // TODO(https://crbug.com/871805): Use helper class to determine whether dark status icons
         // are supported.
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-            Assert.assertEquals(expectedColor,
+            assertEquals(expectedColor,
                     mCustomTabActivityTestRule.getActivity().getWindow().getStatusBarColor());
         } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
-            Assert.assertEquals(ColorUtils.getDarkenedColorForStatusBar(expectedColor),
+            assertEquals(ColorUtils.getDarkenedColorForStatusBar(expectedColor),
                     mCustomTabActivityTestRule.getActivity().getWindow().getStatusBarColor());
         }
     }
@@ -1008,7 +1013,7 @@
         CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
         Assert.assertTrue(connection.updateVisuals(token, updateVisualsBundle));
 
-        Assert.assertEquals("Bestest testest", actionButton.getContentDescription());
+        assertEquals("Bestest testest", actionButton.getContentDescription());
     }
 
     /**
@@ -1041,7 +1046,7 @@
         CustomTabToolbar toolbar = (CustomTabToolbar) toolbarView;
         final ImageButton actionButton = toolbar.getCustomActionButtonForTest(0);
         Assert.assertNotNull("Action button not found", actionButton);
-        Assert.assertEquals("Shown", actionButton.getContentDescription());
+        assertEquals("Shown", actionButton.getContentDescription());
 
         Assert.assertNull(toolbar.getCustomActionButtonForTest(1));
     }
@@ -1095,9 +1100,9 @@
         Assert.assertTrue("Bottom Bar wrapper is not visible.",
                 bottomBar.getVisibility() == View.VISIBLE && bottomBar.getHeight() > 0
                         && bottomBar.getWidth() > 0);
-        Assert.assertEquals("Bottom Bar showing incorrect number of buttons.", numItems,
+        assertEquals("Bottom Bar showing incorrect number of buttons.", numItems,
                 bottomBar.getChildCount());
-        Assert.assertEquals("Bottom bar not showing correct color", barColor,
+        assertEquals("Bottom bar not showing correct color", barColor,
                 ((ColorDrawable) bottomBar.getBackground()).getColor());
         for (int i = 0; i < numItems; i++) {
             ImageButton button = (ImageButton) bottomBar.getChildAt(i);
@@ -1106,7 +1111,7 @@
             Assert.assertTrue("Bottom Bar button is not visible.",
                     button.getVisibility() == View.VISIBLE && button.getHeight() > 0
                             && button.getWidth() > 0);
-            Assert.assertEquals("Bottom Bar button does not have correct content description",
+            assertEquals("Bottom Bar button does not have correct content description",
                     Integer.toString(i + 1), button.getContentDescription());
         }
     }
@@ -1143,7 +1148,7 @@
     @RetryOnFailure
     public void testLaunchWithSession() throws Exception {
         CustomTabsSessionToken session = warmUpAndLaunchUrlWithSession();
-        Assert.assertEquals(getActivity().getIntentDataProvider().getSession(), session);
+        assertEquals(getActivity().getIntentDataProvider().getSession(), session);
     }
 
     @Test
@@ -1154,7 +1159,7 @@
         final Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
         CustomTabsSessionToken session = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
         warmUpAndLaunchUrlWithSession(intent);
-        Assert.assertEquals(getActivity().getIntentDataProvider().getSession(), session);
+        assertEquals(getActivity().getIntentDataProvider().getSession(), session);
         Assert.assertFalse("CustomTabContentHandler handled intent with wrong session",
                 TestThreadUtils.runOnUiThreadBlockingNoException(() -> {
                     return BrowserSessionContentUtils.handleBrowserServicesIntent(
@@ -1204,7 +1209,7 @@
         DOMUtils.clickNode(mCustomTabActivityTestRule.getWebContents(), "new_window");
 
         openTabHelper.waitForCallback(0, 1);
-        Assert.assertEquals(
+        assertEquals(
                 "A new tab should have been created.", 2, tabSelector.getModel(false).getCount());
     }
 
@@ -1217,19 +1222,19 @@
                                         .getApplicationContext();
         final Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage2);
         final CustomTabsSessionToken session = warmUpAndLaunchUrlWithSession(intent);
-        Assert.assertEquals(getActivity().getIntentDataProvider().getSession(), session);
+        assertEquals(getActivity().getIntentDataProvider().getSession(), session);
         CustomTabsConnection connection = CustomTabsConnection.getInstance();
         String packageName = context.getPackageName();
         final String referrer =
                 IntentHandler.constructValidReferrerForAuthority(packageName).getUrl();
-        Assert.assertEquals(referrer, connection.getReferrerForSession(session).getUrl());
+        assertEquals(referrer, connection.getReferrerForSession(session).getUrl());
 
         final Tab tab = getActivity().getActivityTab();
         final CallbackHelper pageLoadFinishedHelper = new CallbackHelper();
         tab.addObserver(new EmptyTabObserver() {
             @Override
             public void onLoadUrl(Tab tab, LoadUrlParams params, int loadType) {
-                Assert.assertEquals(referrer, params.getReferrer().getUrl());
+                assertEquals(referrer, params.getReferrer().getUrl());
             }
 
             @Override
@@ -1262,14 +1267,14 @@
                         CustomTabsService.RELATION_USE_AS_ORIGIN));
 
         final CustomTabsSessionToken session = warmUpAndLaunchUrlWithSession(intent);
-        Assert.assertEquals(getActivity().getIntentDataProvider().getSession(), session);
+        assertEquals(getActivity().getIntentDataProvider().getSession(), session);
 
         final Tab tab = getActivity().getActivityTab();
         final CallbackHelper pageLoadFinishedHelper = new CallbackHelper();
         tab.addObserver(new EmptyTabObserver() {
             @Override
             public void onLoadUrl(Tab tab, LoadUrlParams params, int loadType) {
-                Assert.assertEquals(referrer, params.getReferrer().getUrl());
+                assertEquals(referrer, params.getReferrer().getUrl());
             }
 
             @Override
@@ -1338,7 +1343,7 @@
 
     private static void assertSuffixedHistogramTotalCount(long expected, String histogramPrefix) {
         for (String suffix : new String[] {".ZoomedIn", ".ZoomedOut"}) {
-            Assert.assertEquals(expected,
+            assertEquals(expected,
                     RecordHistogram.getHistogramTotalCountForTesting(histogramPrefix + suffix));
         }
     }
@@ -1748,7 +1753,7 @@
             messageChannelHelper.waitForCallback(0);
             String currentMessage = "Prerendering ";
             // Initial title update during prerender.
-            Assert.assertEquals(
+            assertEquals(
                     CustomTabsService.RESULT_SUCCESS, session.postMessage(currentMessage, null));
             titleString = currentMessage;
         }
@@ -1771,7 +1776,7 @@
 
         String currentMessage = "and loading ";
         // Update title again and verify both updates went through with the channel still intact.
-        Assert.assertEquals(
+        assertEquals(
                 CustomTabsService.RESULT_SUCCESS, session.postMessage(currentMessage, null));
         titleString += currentMessage;
 
@@ -1781,7 +1786,7 @@
 
         String newMessage = "and refreshing";
         // Update title again and verify both updates went through with the channel still intact.
-        Assert.assertEquals(
+        assertEquals(
                 CustomTabsService.RESULT_SUCCESS, session.postMessage(newMessage, null));
         titleString += newMessage;
 
@@ -1819,8 +1824,8 @@
                 mCustomTabActivityTestRule.getActivity().getActivityTab().canGoForward());
 
         List<HistoryItem> history = getHistory();
-        Assert.assertEquals(1, history.size());
-        Assert.assertEquals(mTestPage, history.get(0).getUrl());
+        assertEquals(1, history.size());
+        assertEquals(mTestPage, history.get(0).getUrl());
     }
 
     /** Tests that calling warmup() is optional without prerendering. */
@@ -1960,7 +1965,7 @@
         Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
         String content = JavaScriptUtils.executeJavaScriptAndWaitForResult(
                 tab.getWebContents(), "document.body.textContent");
-        Assert.assertEquals("\"acookie\"", content);
+        assertEquals("\"acookie\"", content);
     }
 
     /**
@@ -2047,7 +2052,7 @@
 
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(
                 CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage));
-        Assert.assertEquals(Uri.parse(mTestPage).getHost() + ":" + Uri.parse(mTestPage).getPort(),
+        assertEquals(Uri.parse(mTestPage).getHost() + ":" + Uri.parse(mTestPage).getPort(),
                 ((EditText) getActivity().findViewById(R.id.url_bar)).getText().toString());
     }
 
@@ -2375,7 +2380,7 @@
                 CustomTabsTestUtils.createMinimalCustomTabIntent(
                         InstrumentationRegistry.getTargetContext(), testUrl));
         Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-        Assert.assertEquals(mTestPage, tab.getUrl());
+        assertEquals(mTestPage, tab.getUrl());
     }
 
     /**
@@ -2393,7 +2398,7 @@
                 CustomTabsTestUtils.createMinimalCustomTabIntent(
                         InstrumentationRegistry.getTargetContext(), testUrl));
         Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-        Assert.assertEquals(testUrl, tab.getUrl());
+        assertEquals(testUrl, tab.getUrl());
     }
 
     /**
@@ -2414,7 +2419,7 @@
                 CustomTabsTestUtils.createMinimalCustomTabIntent(
                         InstrumentationRegistry.getTargetContext(), testUrl));
         Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-        Assert.assertEquals(testUrl, tab.getUrl());
+        assertEquals(testUrl, tab.getUrl());
     }
 
     /**
@@ -2433,7 +2438,7 @@
                 CustomTabsTestUtils.createMinimalCustomTabIntent(
                         InstrumentationRegistry.getTargetContext(), testUrl));
         Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-        Assert.assertEquals(testUrl, tab.getUrl());
+        assertEquals(testUrl, tab.getUrl());
     }
 
     /**
@@ -2451,7 +2456,7 @@
                 CustomTabsTestUtils.createMinimalCustomTabIntent(
                         InstrumentationRegistry.getTargetContext(), testUrl));
         Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-        Assert.assertEquals(testUrl, tab.getUrl());
+        assertEquals(testUrl, tab.getUrl());
     }
 
     /** Maybe prerenders a URL with a referrer, then launch it with another one. */
@@ -2504,9 +2509,9 @@
         Assert.assertFalse(tab.canGoForward());
 
         List<HistoryItem> history = getHistory();
-        Assert.assertEquals(2, history.size());
-        Assert.assertEquals(mTestPage2, history.get(0).getUrl());
-        Assert.assertEquals(mTestPage, history.get(1).getUrl());
+        assertEquals(2, history.size());
+        assertEquals(mTestPage2, history.get(0).getUrl());
+        assertEquals(mTestPage, history.get(1).getUrl());
     }
 
     /**
@@ -2527,6 +2532,57 @@
         verifyHistoryAfterHiddenTab(false);
     }
 
+    @Test
+    @SmallTest
+    public void closeButton_navigatesToLandingPage() throws InterruptedException, TimeoutException {
+        Context context = InstrumentationRegistry.getInstrumentation()
+                .getTargetContext()
+                .getApplicationContext();
+        Intent intent = CustomTabsTestUtils
+                .createMinimalCustomTabIntent(context, mTestServer.getURL(TARGET_BLANK_TEST_PAGE));
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+
+        CustomTabActivity activity = mCustomTabActivityTestRule.getActivity();
+        Assert.assertEquals(activity.getCurrentTabModel().getCount(), 1);
+
+        // The link we click will take us to another page. Set the initial page as the landing page.
+        activity.getComponent().resolveNavigationController().setLandingPageOnCloseCriterion(
+                url -> url.contains(TARGET_BLANK_TEST_PAGE));
+
+        DOMUtils.clickNode(activity.getActivityTab().getWebContents(), "target_blank_link");
+        CriteriaHelper.pollUiThread(Criteria.equals(2,
+                () -> activity.getCurrentTabModel().getCount()));
+
+        ClickUtils.clickButton(activity.findViewById(R.id.close_button));
+
+        CriteriaHelper.pollUiThread(Criteria.equals(1,
+                () -> activity.getCurrentTabModel().getCount()));
+        assertFalse(activity.isFinishing());
+    }
+
+    @Test
+    @SmallTest
+    public void closeButton_closesActivityIfNoLandingPage()
+            throws InterruptedException, TimeoutException {
+        Context context = InstrumentationRegistry.getInstrumentation()
+                .getTargetContext()
+                .getApplicationContext();
+        Intent intent = CustomTabsTestUtils
+                .createMinimalCustomTabIntent(context, mTestServer.getURL(TARGET_BLANK_TEST_PAGE));
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+
+        CustomTabActivity activity = mCustomTabActivityTestRule.getActivity();
+        Assert.assertEquals(activity.getCurrentTabModel().getCount(), 1);
+
+        DOMUtils.clickNode(activity.getActivityTab().getWebContents(), "target_blank_link");
+        CriteriaHelper.pollUiThread(Criteria.equals(2,
+                () -> activity.getCurrentTabModel().getCount()));
+
+        ClickUtils.clickButton(activity.findViewById(R.id.close_button));
+
+        CriteriaHelper.pollUiThread(activity::isFinishing);
+    }
+
     private void verifyHistoryAfterHiddenTab(boolean speculationWasAHit) throws Exception {
         String speculationUrl = mTestPage;
         String navigationUrl = speculationWasAHit ? mTestPage : mTestPage2;
@@ -2548,8 +2604,8 @@
         Assert.assertFalse(tab.canGoForward());
 
         List<HistoryItem> history = getHistory();
-        Assert.assertEquals(1, history.size());
-        Assert.assertEquals(navigationUrl, history.get(0).getUrl());
+        assertEquals(1, history.size());
+        assertEquals(navigationUrl, history.get(0).getUrl());
     }
 
     private void mayLaunchUrlWithoutWarmup(boolean useHiddenTab) throws InterruptedException {
@@ -2565,7 +2621,7 @@
         mCustomTabActivityTestRule.startCustomTabActivityWithIntent(
                 CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage));
         Tab tab = mCustomTabActivityTestRule.getActivity().getActivityTab();
-        Assert.assertEquals(mTestPage, tab.getUrl());
+        assertEquals(mTestPage, tab.getUrl());
     }
 
     private ChromeActivity reparentAndVerifyTab() throws InterruptedException {
@@ -2601,12 +2657,12 @@
                         && newActivity.getActivityTab().equals(tabToBeReparented);
             }
         }));
-        Assert.assertEquals(newActivity.getWindowAndroid(), tabToBeReparented.getWindowAndroid());
-        Assert.assertEquals(newActivity.getWindowAndroid(),
+        assertEquals(newActivity.getWindowAndroid(), tabToBeReparented.getWindowAndroid());
+        assertEquals(newActivity.getWindowAndroid(),
                 tabToBeReparented.getWebContents().getTopLevelNativeWindow());
         Assert.assertFalse(
                 tabToBeReparented.getDelegateFactory() instanceof CustomTabDelegateFactory);
-        Assert.assertEquals("The tab should never be hidden during the reparenting process", 0,
+        assertEquals("The tab should never be hidden during the reparenting process", 0,
                 tabHiddenHelper.getCallCount());
         Assert.assertFalse(tabToBeReparented.isCurrentlyACustomTab());
         tabToBeReparented.removeObserver(observer);
@@ -2629,7 +2685,7 @@
             public void extraCallback(String callbackName, Bundle args) {
                 if (callbackName.equals(CustomTabsConnection.ON_WARMUP_COMPLETED)) return;
 
-                Assert.assertEquals(CustomTabsConnection.PAGE_LOAD_METRICS_CALLBACK, callbackName);
+                assertEquals(CustomTabsConnection.PAGE_LOAD_METRICS_CALLBACK, callbackName);
                 if (-1 != args.getLong(PageLoadMetrics.EFFECTIVE_CONNECTION_TYPE, -1)) {
                     sawNetworkQualityEstimates.set(true);
                 }
@@ -2684,7 +2740,7 @@
             } catch (AssertionError e) {
                 // Expected.
             }
-            Assert.assertEquals(-1L, (long) firstContentfulPaintMs.get());
+            assertEquals(-1L, (long) firstContentfulPaintMs.get());
         }
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java
index 8329563..c37585a9 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CloseButtonNavigatorTest.java
@@ -4,8 +4,13 @@
 
 package org.chromium.chrome.browser.customtabs;
 
+import static junit.framework.Assert.assertEquals;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -15,36 +20,76 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.BlockJUnit4ClassRunner;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabController;
+import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.content_public.browser.NavigationController;
 import org.chromium.content_public.browser.NavigationEntry;
 import org.chromium.content_public.browser.NavigationHistory;
+import org.chromium.content_public.browser.WebContents;
+
+import java.util.Stack;
 
 /**
  * Tests for {@link CloseButtonNavigator}.
  */
 @RunWith(BlockJUnit4ClassRunner.class)
 public class CloseButtonNavigatorTest {
-    @Mock public NavigationController mNavigationController;
-    private final NavigationHistory mNavigationHistory = new NavigationHistory();
+    @Mock public CustomTabActivityTabController mTabController;
+    @Mock public CustomTabActivityTabProvider mTabProvider;
 
-    private final CloseButtonNavigator mCloseButtonNavigator = new CloseButtonNavigator();
+    // private final List<Tab> mTabs = new ArrayList<>();
+    private final Stack<Tab> mTabs = new Stack<>();
+    private CloseButtonNavigator mCloseButtonNavigator;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        when(mNavigationController.getNavigationHistory()).thenReturn(mNavigationHistory);
+        mCloseButtonNavigator = new CloseButtonNavigator(mTabController, mTabProvider);
+
+        // Set up our mTabs to act as the mock tab model:
+        // - mTabController.closeTab removes the top tab.
+        // - mTabProvider.getTab returns the top tab.
+        Mockito.doAnswer((invocation) -> {
+            mTabs.pop();
+            return null;  // Annoyingly we have to return something.
+        }).when(mTabController).closeTab();
+        when(mTabProvider.getTab()).thenAnswer(invocation -> {
+            if (mTabs.empty()) return null;
+            return mTabs.peek();
+        });
     }
 
-    private void addSitesToHistory(String... urls) {
+    private Tab createTabWithNavigationHistory(String... urls) {
+        NavigationHistory history = new NavigationHistory();
+
         for (String url : urls) {
-            mNavigationHistory.addEntry(new NavigationEntry(0, url, "", "", "", "", null, 0, 0));
+            history.addEntry(new NavigationEntry(0, url, "", "", "", "", null, 0, 0));
         }
 
         // Point to the most recent entry in history.
-        mNavigationHistory.setCurrentEntryIndex(mNavigationHistory.getEntryCount() - 1);
+        history.setCurrentEntryIndex(history.getEntryCount() - 1);
+
+        Tab tab = mock(Tab.class);
+        WebContents webContents = mock(WebContents.class);
+        NavigationController navigationController = mock(NavigationController.class);
+
+        when(tab.getUrl()).thenAnswer(invocation ->
+            history.getEntryAtIndex(history.getCurrentEntryIndex()).getUrl());
+        when(tab.getWebContents()).thenReturn(webContents);
+        when(webContents.getNavigationController()).thenReturn(navigationController);
+        when(navigationController.getNavigationHistory()).thenReturn(history);
+
+        return tab;
+    }
+
+    private NavigationController currentTabsNavigationController() {
+        // The navigation controller will be a mock object created in the above method.
+        return mTabs.peek().getWebContents().getNavigationController();
     }
 
     /** Example criteria. */
@@ -53,63 +98,142 @@
     }
 
     @Test
-    public void noCriteria() {
-        addSitesToHistory(
+    public void noCriteria_singleTab() {
+        mTabs.push(createTabWithNavigationHistory(
                 "www.blue.com/page1",
                 "www.blue.com/page2"
-        );
+        ));
 
-        mCloseButtonNavigator.navigateOnClose(mNavigationController);
+        mCloseButtonNavigator.navigateOnClose();
 
-        verify(mNavigationController, never()).goToNavigationIndex(anyInt());
+        assertTrue(mTabs.empty());
     }
 
     @Test
-    public void matchingUrl() {
-        addSitesToHistory(
+    public void noCriteria_multipleTabs() {
+        mTabs.push(createTabWithNavigationHistory( "www.blue.com/page1"));
+        mTabs.push(createTabWithNavigationHistory( "www.blue.com/page2"));
+
+        mCloseButtonNavigator.navigateOnClose();
+
+        assertTrue(mTabs.empty());
+    }
+
+    @Test
+    public void noMatchingUrl_singleTab() {
+        mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
+        mTabs.push(createTabWithNavigationHistory(
+                "www.blue.com/page1",
+                "www.blue.com/page2"
+        ));
+
+        mCloseButtonNavigator.navigateOnClose();
+
+        assertTrue(mTabs.empty());
+    }
+
+    @Test
+    public void noMatchingUrl_multipleTabs() {
+        mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
+        mTabs.push(createTabWithNavigationHistory( "www.blue.com/page1"));
+        mTabs.push(createTabWithNavigationHistory( "www.blue.com/page2"));
+
+        mCloseButtonNavigator.navigateOnClose();
+
+        assertTrue(mTabs.empty());
+    }
+
+    @Test
+    public void matchingUrl_singleTab() {
+        mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
+        mTabs.push(createTabWithNavigationHistory(
                 "www.red.com/page1",
                 "www.red.com/page2",
                 "www.blue.com/page1",
                 "www.blue.com/page2"
-        );
+        ));
 
-        mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
-        mCloseButtonNavigator.navigateOnClose(mNavigationController);
+        mCloseButtonNavigator.navigateOnClose();
 
-        verify(mNavigationController).goToNavigationIndex(eq(1));  // www.red.com/page2
-        // Verify that it wasn't called with any other index.
-        verify(mNavigationController).goToNavigationIndex(anyInt());
+        assertFalse(mTabs.isEmpty());
+        verify(currentTabsNavigationController()).goToNavigationIndex(eq(1));
+        // Ensure it was only called with that value.
+        verify(currentTabsNavigationController()).goToNavigationIndex(anyInt());
     }
 
     @Test
-    public void noMatchingUrl() {
-        addSitesToHistory(
+    public void matchingUrl_startOfNextTab() {
+        mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
+        mTabs.push(createTabWithNavigationHistory(
+                "www.red.com/page1",
+                "www.red.com/page2"
+        ));
+        mTabs.push(createTabWithNavigationHistory(
                 "www.blue.com/page1",
                 "www.blue.com/page2"
-        );
+        ));
 
-        mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
-        mCloseButtonNavigator.navigateOnClose(mNavigationController);
+        mCloseButtonNavigator.navigateOnClose();
 
-        verify(mNavigationController, never()).goToNavigationIndex(anyInt());
+        assertEquals(1, mTabs.size());
+        verify(currentTabsNavigationController(), never()).goToNavigationIndex(anyInt());
     }
 
     @Test
-    public void inMiddleOfHistory() {
-        addSitesToHistory(
+    public void matchingUrl_middleOfNextTab() {
+        mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
+        mTabs.push(createTabWithNavigationHistory(
+                "www.red.com/page1",
+                "www.blue.com/page1"
+        ));
+        mTabs.push(createTabWithNavigationHistory(
+                "www.blue.com/page2",
+                "www.blue.com/page3"
+        ));
+
+        mCloseButtonNavigator.navigateOnClose();
+
+        assertEquals(1, mTabs.size());
+        verify(currentTabsNavigationController()).goToNavigationIndex(eq(0));
+        verify(currentTabsNavigationController()).goToNavigationIndex(anyInt());
+    }
+
+    @Test
+    public void middleOfHistory() {
+        mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
+        mTabs.push(createTabWithNavigationHistory(
                 "www.red.com/page1",
                 "www.red.com/page2",
                 "www.blue.com/page1",
                 "www.blue.com/page2",
                 "www.red.com/page3"
-        );
-        mNavigationHistory.setCurrentEntryIndex(3);  // www.blue.com/page2
+        ));
 
+        mTabs.peek().getWebContents().getNavigationController().getNavigationHistory()
+                .setCurrentEntryIndex(3);
+
+        mCloseButtonNavigator.navigateOnClose();
+
+        assertEquals(1, mTabs.size());
+        verify(currentTabsNavigationController()).goToNavigationIndex(eq(1));
+        verify(currentTabsNavigationController()).goToNavigationIndex(anyInt());
+    }
+
+    @Test
+    public void navigateFromLandingPage() {
         mCloseButtonNavigator.setLandingPageCriteria(CloseButtonNavigatorTest::isRed);
-        mCloseButtonNavigator.navigateOnClose(mNavigationController);
+        mTabs.push(createTabWithNavigationHistory(
+                "www.red.com/page1",
+                "www.red.com/page2",
+                "www.blue.com/page1",
+                "www.blue.com/page2",
+                "www.red.com/page3"
+        ));
 
-        verify(mNavigationController).goToNavigationIndex(eq(1));  // www.red.com/page2
-        // Verify that it wasn't called with any other index.
-        verify(mNavigationController).goToNavigationIndex(anyInt());
+        mCloseButtonNavigator.navigateOnClose();
+
+        assertEquals(1, mTabs.size());
+        verify(currentTabsNavigationController()).goToNavigationIndex(eq(1));
+        verify(currentTabsNavigationController()).goToNavigationIndex(anyInt());
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationControllerTest.java
index 272af9ed..4b698d1 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationControllerTest.java
@@ -64,20 +64,6 @@
     }
 
     @Test
-    public void doesntFinish_IfCloseButtonNavigatorHandlesClose() {
-        when(env.closeButtonNavigator.navigateOnClose(any())).thenReturn(true);
-        mNavigationController.navigateOnClose();
-        verify(mFinishHandler, never()).onFinish(anyInt());
-    }
-
-    @Test
-    public void closesTab_IfCloseButtonNavigatorDoesntHandleClose() {
-        when(env.closeButtonNavigator.navigateOnClose(any())).thenReturn(false);
-        mNavigationController.navigateOnClose();
-        verify(mFinishHandler).onFinish(eq(FinishReason.USER_NAVIGATION));
-    }
-
-    @Test
     public void handlesBackNavigation_IfExternalBackHandlerRejectsSynchronously() {
         mNavigationController.setBackHandler(notHandledRunnable -> false);
         mNavigationController.navigateOnBack();
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index cc99f630..e6b4539 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-75.0.3756.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-75.0.3757.0_rc-r1-merged.afdo.bz2
\ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 9fbda13..d260a8b 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -4037,6 +4037,10 @@
      FEATURE_VALUE_TYPE(features::kInSessionPasswordChange)},
 #endif  // OS_CHROMEOS
 
+    {"enable-portals", flag_descriptions::kEnablePortalsName,
+     flag_descriptions::kEnablePortalsDescription, kOsAll,
+     FEATURE_VALUE_TYPE(blink::features::kPortals)},
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index f979d52..d7ef52a 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -2232,7 +2232,13 @@
 // This test ensures that closing app window on 'loadcommit' does not crash.
 // The test launches an app with guest and closes the window on loadcommit. It
 // then launches the app window again. The process is repeated 3 times.
-IN_PROC_BROWSER_TEST_F(WebViewTest, CloseOnLoadcommit) {
+// TODO(crbug.com/949923): The test is flaky (crash) on ChromeOS debug and ASan/LSan
+#if defined(OS_CHROMEOS) && (!defined(NDEBUG) || defined(ADDRESS_SANITIZER))
+#define MAYBE_CloseOnLoadcommit DEFINE_CloseOnLoadcommit
+#else
+#define MAYBE_CloseOnLoadcommit CloseOnLoadcommit
+#endif
+IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_CloseOnLoadcommit) {
   LoadAndLaunchPlatformApp("web_view/close_on_loadcommit",
                            "done-close-on-loadcommit");
 }
diff --git a/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_unittest.cc b/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_unittest.cc
index a389cc6..7506c06 100644
--- a/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/event_based_status_reporting_service_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/child_accounts/consumer_status_reporting_service.h"
 #include "chrome/browser/chromeos/child_accounts/consumer_status_reporting_service_factory.h"
@@ -17,6 +18,7 @@
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/supervised_user/supervised_user_constants.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
 #include "chromeos/dbus/system_clock/system_clock_client.h"
@@ -89,6 +91,11 @@
   void SetUp() override {
     PowerManagerClient::InitializeFake();
     SystemClockClient::InitializeFake();
+
+    // TODO(agawronska): To enable this we need LoginScreenClient, but it causes
+    // test crashes on network connection change.
+    scoped_feature_list_.InitAndDisableFeature(features::kParentAccessCode);
+
     profile_ = std::make_unique<TestingProfile>();
     profile_.get()->SetSupervisedUserId(supervised_users::kChildAccountSUID);
     arc_test_.SetUp(profile());
@@ -169,6 +176,7 @@
       test_consumer_status_reporting_service_;
   TestingScreenTimeController* test_screen_time_controller_;
   session_manager::SessionManager session_manager_;
+  base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<EventBasedStatusReportingService> service_;
 
   DISALLOW_COPY_AND_ASSIGN(EventBasedStatusReportingServiceTest);
diff --git a/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_unittest.cc b/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_unittest.cc
index fa0ddc4..8cf7e18 100644
--- a/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/parent_access_code/parent_access_service_unittest.cc
@@ -115,7 +115,7 @@
     EXPECT_EQ(failure_count, validation_results_.failure_count);
   }
 
-  // ParentAccessService depends on LoginScreenClient and the1refore requires
+  // ParentAccessService depends on LoginScreenClient and therefore requires
   // objects in the following block to be initialized early (order matters).
   content::TestBrowserThreadBundle thread_bundle_;
   content::TestServiceManagerContext context_;
diff --git a/chrome/browser/chromeos/input_method/input_method_engine.cc b/chrome/browser/chromeos/input_method/input_method_engine.cc
index 38b3fdc..f084489 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine.cc
@@ -133,11 +133,10 @@
 
  private:
   void OnConnectionLost() {
-    // After the connection to |ImeEngineFactoryRegistry| is broken, break the
-    // connection to the client, so that the client can reconnect through Window
-    // Service.
-    engine_binding_.Close();
-    engine_client_.reset();
+    // After the connection to |ImeEngineFactoryRegistry| is broken, notifies
+    // the client to reconnect through Window Service.
+    if (engine_client_)
+      engine_client_->Reconnect();
   }
 
   InputMethodEngine* engine_;
@@ -370,7 +369,8 @@
     uint32_t cursor_pos,
     bool is_visible) {
   if (mojo_helper_->IsConnected()) {
-    NOTIMPLEMENTED_LOG_ONCE();
+    mojo_helper_->engine_client()->UpdateCompositionText(
+        composition_text, cursor_pos, is_visible);
   } else {
     ui::IMEInputContextHandlerInterface* input_context =
         ui::IMEBridge::Get()->GetInputContextHandler();
@@ -384,7 +384,7 @@
                                                  const std::string& text) {
   bool committed = false;
   if (mojo_helper_->IsConnected()) {
-    NOTIMPLEMENTED_LOG_ONCE();
+    mojo_helper_->engine_client()->CommitText(text);
   } else {
     ui::IMEInputContextHandlerInterface* input_context =
         ui::IMEBridge::Get()->GetInputContextHandler();
@@ -407,7 +407,8 @@
     int offset,
     size_t number_of_chars) {
   if (mojo_helper_->IsConnected()) {
-    NOTIMPLEMENTED_LOG_ONCE();
+    mojo_helper_->engine_client()->DeleteSurroundingText(offset,
+                                                         number_of_chars);
   } else {
     ui::IMEInputContextHandlerInterface* input_context =
         ui::IMEBridge::Get()->GetInputContextHandler();
@@ -429,7 +430,7 @@
 
   bool sent = false;
   if (mojo_helper_->IsConnected()) {
-    NOTIMPLEMENTED_LOG_ONCE();
+    mojo_helper_->engine_client()->SendKeyEvent(ui::Event::Clone(*event));
   } else {
     ui::IMEInputContextHandlerInterface* input_context =
         ui::IMEBridge::Get()->GetInputContextHandler();
diff --git a/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc b/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc
index f055275..93088e8 100644
--- a/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc
+++ b/chrome/browser/chromeos/input_method/input_method_engine_unittest.cc
@@ -187,8 +187,22 @@
     return ptr;
   }
 
+  bool commit_text_called() const { return commit_text_called_; }
+
  private:
+  // ime::mojom::ImeEngineClient:
+  void CommitText(const std::string& text) override {
+    commit_text_called_ = true;
+  }
+  void UpdateCompositionText(const ui::CompositionText& composition_text,
+                             uint32_t cursor_pos,
+                             bool visible) override {}
+  void DeleteSurroundingText(int32_t offset, uint32_t length) override {}
+  void SendKeyEvent(std::unique_ptr<ui::Event> key_event) override {}
+  void Reconnect() override {}
+
   mojo::Binding<ime::mojom::ImeEngineClient> binding_;
+  bool commit_text_called_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestImeEngineClient);
 };
@@ -404,6 +418,12 @@
       false));
   engine_ptr.FlushForTesting();
   EXPECT_EQ(ACTIVATE | ONFOCUS, observer_->GetCallsBitmapAndReset());
+
+  int context = engine_->GetContextIdForTesting();
+  std::string error;
+  engine_->CommitText(context, "input", &error);
+  engine_->FlushForTesting();
+  EXPECT_TRUE(client.commit_text_called());
 }
 
 }  // namespace input_method
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
index 5fbf0588..4b02da0b 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_local_policy_server_browsertest.cc
@@ -18,7 +18,11 @@
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/policy/test/local_policy_test_server.h"
+#include "chromeos/attestation/mock_attestation_flow.h"
 #include "chromeos/constants/chromeos_switches.h"
+#include "chromeos/cryptohome/async_method_caller.h"
+#include "chromeos/dbus/cryptohome/fake_cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
 #include "components/policy/core/common/policy_switches.h"
 
 namespace chromeos {
@@ -100,6 +104,17 @@
                                     "5");
   }
 
+  void SetFakeAttestationFlow() {
+    g_browser_process->platform_part()
+        ->browser_policy_connector_chromeos()
+        ->GetDeviceCloudPolicyInitializer()
+        ->SetAttestationFlowForTesting(
+            std::make_unique<chromeos::attestation::AttestationFlow>(
+                cryptohome::AsyncMethodCaller::GetInstance(),
+                chromeos::FakeCryptohomeClient::Get(),
+                std::make_unique<chromeos::attestation::FakeServerProxy>()));
+  }
+
   policy::ServerBackedStateKeysBroker* state_keys_broker() {
     return g_browser_process->platform_part()
         ->browser_policy_connector_chromeos()
@@ -119,11 +134,7 @@
   enrollment_screen()->OnLoginDone(FakeGaiaMixin::kFakeUserEmail,
                                    FakeGaiaMixin::kFakeAuthCode);
 
-  test::OobeJS()
-      .CreateWaiter(
-          "document.getElementsByClassName('oauth-enroll-state-attribute-"
-          "prompt').length > 0")
-      ->Wait();
+  OobeBaseTest::WaitForEnrollmentSuccess();
   // TODO(rsorokin): Interact with attribute prompt step.
   EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
 }
@@ -184,6 +195,18 @@
   OobeScreenWaiter(OobeScreen::SCREEN_DEVICE_DISABLED).Wait();
 }
 
-// TODO(rsorokin): Add test for RESTORE_MODE_REENROLLMENT_ZERO_TOUCH.
+// Attestation enrollment.
+IN_PROC_BROWSER_TEST_F(AutoEnrollmentLocalPolicyServer, Attestation) {
+  SetFakeAttestationFlow();
+  EXPECT_TRUE(local_policy_mixin_.SetDeviceStateRetrievalResponse(
+      state_keys_broker(),
+      enterprise_management::DeviceStateRetrievalResponse::
+          RESTORE_MODE_REENROLLMENT_ZERO_TOUCH,
+      kTestDomain));
+
+  host()->StartWizard(OobeScreen::SCREEN_AUTO_ENROLLMENT_CHECK);
+  OobeBaseTest::WaitForEnrollmentSuccess();
+  EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
+}
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
index 1adf162..481199be 100644
--- a/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
+++ b/chrome/browser/chromeos/login/test/local_policy_test_server_mixin.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/guid.h"
+#include "chrome/browser/chromeos/login/test/fake_gaia_mixin.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/policy_builder.h"
 #include "components/policy/core/common/policy_switches.h"
@@ -22,6 +23,9 @@
   managed_users.GetList().emplace_back("*");
   config.SetKey("managed_users", std::move(managed_users));
 
+  config.SetKey("robot_api_auth_code",
+                base::Value(FakeGaiaMixin::kFakeAuthCode));
+
   return config;
 }
 
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_initializer.cc b/chrome/browser/chromeos/policy/device_cloud_policy_initializer.cc
index 635e07d..e6ce8211 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_initializer.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_initializer.cc
@@ -77,6 +77,11 @@
   system_url_loader_factory_for_testing_ = system_url_loader_factory;
 }
 
+void DeviceCloudPolicyInitializer::SetAttestationFlowForTesting(
+    std::unique_ptr<chromeos::attestation::AttestationFlow> attestation_flow) {
+  attestation_flow_ = std::move(attestation_flow);
+}
+
 DeviceCloudPolicyInitializer::~DeviceCloudPolicyInitializer() {
   DCHECK(!is_initialized_);
 }
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_initializer.h b/chrome/browser/chromeos/policy/device_cloud_policy_initializer.h
index c719b14..cdffabbc 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_initializer.h
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_initializer.h
@@ -125,6 +125,8 @@
       std::unique_ptr<policy::SigningService> signing_service);
   void SetSystemURLLoaderFactoryForTesting(
       scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory);
+  void SetAttestationFlowForTesting(
+      std::unique_ptr<chromeos::attestation::AttestationFlow> attestation_flow);
 
  private:
   // Signing class implementing the policy::SigningService interface to
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/adapter.cc b/chrome/browser/chromeos/power/auto_screen_brightness/adapter.cc
index 1c15b51..c6abbfc0 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/adapter.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/adapter.cc
@@ -47,7 +47,12 @@
 
 }  // namespace
 
-Adapter::Params::Params() {}
+Adapter::Params::Params() = default;
+
+Adapter::AdapterDecision::AdapterDecision() = default;
+
+Adapter::AdapterDecision::AdapterDecision(const AdapterDecision& decision) =
+    default;
 
 Adapter::Adapter(Profile* profile,
                  AlsReader* als_reader,
@@ -80,7 +85,16 @@
 
   log_als_values_->SaveToBuffer({ConvertToLog(lux), now});
 
-  MaybeAdjustBrightness(now);
+  const AdapterDecision& decision = CanAdjustBrightness(now);
+
+  if (decision.no_brightness_change_cause)
+    return;
+
+  DCHECK(decision.brightness_change_cause);
+  DCHECK(decision.log_als_avg_stddev);
+
+  AdjustBrightness(*decision.brightness_change_cause,
+                   decision.log_als_avg_stddev->avg);
 }
 
 void Adapter::OnAlsReaderInitialized(AlsReader::AlsInitStatus status) {
@@ -421,11 +435,22 @@
   adapter_status_ = Status::kSuccess;
 }
 
-base::Optional<Adapter::BrightnessChangeCause> Adapter::CanAdjustBrightness(
-    const AlsAvgStdDev& log_als_avg_stddev) const {
-  if (adapter_status_ != Status::kSuccess ||
-      adapter_disabled_by_user_adjustment_) {
-    return base::nullopt;
+Adapter::AdapterDecision Adapter::CanAdjustBrightness(base::TimeTicks now) {
+  DCHECK_EQ(adapter_status_, Status::kSuccess);
+  DCHECK(log_als_values_);
+  DCHECK(!als_init_time_.is_null());
+
+  AdapterDecision decision;
+  const base::Optional<AlsAvgStdDev> log_als_avg_stddev =
+      log_als_values_->AverageAmbientWithStdDev(now);
+  decision.log_als_avg_stddev = log_als_avg_stddev;
+
+  // User has previously manually changed brightness and it (at least
+  // temporarily) stopped the adapter from operating.
+  if (adapter_disabled_by_user_adjustment_) {
+    decision.no_brightness_change_cause =
+        NoBrightnessChangeCause::kDisabledByUser;
+    return decision;
   }
 
   // Do not change brightness if it's set by the policy, but do not completely
@@ -434,7 +459,38 @@
           ash::prefs::kPowerAcScreenBrightnessPercent) >= 0 ||
       profile_->GetPrefs()->GetInteger(
           ash::prefs::kPowerBatteryScreenBrightnessPercent) >= 0) {
-    return base::nullopt;
+    decision.no_brightness_change_cause =
+        NoBrightnessChangeCause::kBrightnessSetByPolicy;
+    return decision;
+  }
+
+  if (params_.model_curve == ModelCurve::kPersonal && !personal_curve_) {
+    decision.no_brightness_change_cause =
+        NoBrightnessChangeCause::kMissingPersonalCurve;
+    return decision;
+  }
+
+  // Wait until we've had enough ALS data to calc avg.
+  if (now - als_init_time_ < params_.auto_brightness_als_horizon) {
+    decision.no_brightness_change_cause =
+        NoBrightnessChangeCause::kWaitingForInitialAls;
+    return decision;
+  }
+
+  // Check if we've waited long enough from previous brightness change (either
+  // by user or by model).
+  if (!latest_brightness_change_time_.is_null() &&
+      now - latest_brightness_change_time_ <
+          params_.auto_brightness_als_horizon) {
+    decision.no_brightness_change_cause =
+        NoBrightnessChangeCause::kWaitingForAvgHorizon;
+    return decision;
+  }
+
+  if (!log_als_avg_stddev) {
+    decision.no_brightness_change_cause =
+        NoBrightnessChangeCause::kMissingAlsData;
+    return decision;
   }
 
   if (!average_log_ambient_lux_) {
@@ -443,7 +499,9 @@
     // 2. brightness was changed by the user but there wasn't any ALS data. This
     //    case should be rare.
     // In either case, we change brightness as soon as we have brightness.
-    return BrightnessChangeCause::kInitialAlsReceived;
+    decision.brightness_change_cause =
+        BrightnessChangeCause::kInitialAlsReceived;
+    return decision;
   }
 
   // The following thresholds should have been set last time when brightness was
@@ -451,57 +509,41 @@
   DCHECK(brightening_threshold_);
   DCHECK(darkening_threshold_);
 
-  if (log_als_avg_stddev.avg > *brightening_threshold_ &&
-      log_als_avg_stddev.stddev <= params_.brightening_log_lux_threshold *
-                                       params_.stabilization_threshold) {
-    return BrightnessChangeCause::kBrightneningThresholdExceeded;
+  if (log_als_avg_stddev->avg > *brightening_threshold_) {
+    if (log_als_avg_stddev->stddev <= params_.brightening_log_lux_threshold *
+                                          params_.stabilization_threshold) {
+      decision.brightness_change_cause =
+          BrightnessChangeCause::kBrightneningThresholdExceeded;
+      return decision;
+    }
+    decision.no_brightness_change_cause =
+        NoBrightnessChangeCause::kFluctuatingAlsIncrease;
+    return decision;
   }
 
-  if (log_als_avg_stddev.avg < *darkening_threshold_ &&
-      log_als_avg_stddev.stddev <= params_.darkening_log_lux_threshold *
-                                       params_.stabilization_threshold) {
-    return BrightnessChangeCause::kDarkeningThresholdExceeded;
+  if (log_als_avg_stddev->avg < *darkening_threshold_) {
+    if (log_als_avg_stddev->stddev <=
+        params_.darkening_log_lux_threshold * params_.stabilization_threshold) {
+      decision.brightness_change_cause =
+          BrightnessChangeCause::kDarkeningThresholdExceeded;
+      return decision;
+    }
+    decision.no_brightness_change_cause =
+        NoBrightnessChangeCause::kFluctuatingAlsDecrease;
+    return decision;
   }
 
-  return base::nullopt;
+  decision.no_brightness_change_cause =
+      NoBrightnessChangeCause::kMinimalAlsChange;
+  return decision;
 }
 
-void Adapter::MaybeAdjustBrightness(base::TimeTicks now) {
-  DCHECK_EQ(adapter_status_, Status::kSuccess);
-  DCHECK(log_als_values_);
-  DCHECK(!als_init_time_.is_null());
-  // Wait until we've had enough ALS data to calc avg.
-  if (now - als_init_time_ < params_.auto_brightness_als_horizon)
-    return;
-
-  // Check if we've waited long enough from previous brightness change (either
-  // by user or by model).
-  if (!latest_brightness_change_time_.is_null() &&
-      now - latest_brightness_change_time_ <
-          params_.auto_brightness_als_horizon)
-    return;
-
-  const base::Optional<AlsAvgStdDev> log_als_avg_stddev =
-      log_als_values_->AverageAmbientWithStdDev(now);
-  if (!log_als_avg_stddev)
-    return;
-
-  const base::Optional<BrightnessChangeCause> brightness_change_cause =
-      CanAdjustBrightness(*log_als_avg_stddev);
-
-  if (!brightness_change_cause.has_value())
-    return;
-
-  const base::Optional<double> brightness =
-      GetBrightnessBasedOnAmbientLogLux(log_als_avg_stddev->avg);
-
-  // This could occur if curve isn't set up (e.g. when we want to use
-  // personal only that's not yet available).
-  if (!brightness)
-    return;
+void Adapter::AdjustBrightness(BrightnessChangeCause cause,
+                               double log_als_avg) {
+  const double brightness = GetBrightnessBasedOnAmbientLogLux(log_als_avg);
 
   power_manager::SetBacklightBrightnessRequest request;
-  request.set_percent(*brightness);
+  request.set_percent(brightness);
   request.set_transition(
       power_manager::SetBacklightBrightnessRequest_Transition_GRADUAL);
   request.set_cause(power_manager::SetBacklightBrightnessRequest_Cause_MODEL);
@@ -515,27 +557,24 @@
   }
   latest_model_brightness_change_time_ = brightness_change_time;
 
-  const BrightnessChangeCause cause = *brightness_change_cause;
   UMA_HISTOGRAM_ENUMERATION("AutoScreenBrightness.BrightnessChange.Cause",
                             cause);
 
-  WriteLogMessages(log_als_avg_stddev->avg, *brightness, cause);
+  WriteLogMessages(log_als_avg, brightness, cause);
   model_brightness_change_counter_++;
 
-  OnBrightnessChanged(brightness_change_time, *brightness,
-                      log_als_avg_stddev->avg);
+  OnBrightnessChanged(brightness_change_time, brightness, log_als_avg);
 }
 
-base::Optional<double> Adapter::GetBrightnessBasedOnAmbientLogLux(
+double Adapter::GetBrightnessBasedOnAmbientLogLux(
     double ambient_log_lux) const {
   DCHECK_EQ(adapter_status_, Status::kSuccess);
   switch (params_.model_curve) {
     case ModelCurve::kGlobal:
       return global_curve_->Interpolate(ambient_log_lux);
     case ModelCurve::kPersonal:
-      if (personal_curve_)
-        return personal_curve_->Interpolate(ambient_log_lux);
-      return base::nullopt;  // signal brightness shouldn't be changed
+      DCHECK(personal_curve_);
+      return personal_curve_->Interpolate(ambient_log_lux);
     default:
       // We use the latest curve available.
       if (personal_curve_)
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/adapter.h b/chrome/browser/chromeos/power/auto_screen_brightness/adapter.h
index 27470a7..2bedcd42 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/adapter.h
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/adapter.h
@@ -122,6 +122,46 @@
     kMaxValue = kDarkeningThresholdExceeded
   };
 
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class NoBrightnessChangeCause {
+    kWaitingForInitialAls = 0,
+    kWaitingForAvgHorizon = 1,
+    // |log_als_values_| is empty.
+    kMissingAlsData = 2,
+    // User manually changed brightness before and it stopped adapter from
+    // changing brightness.
+    kDisabledByUser = 3,
+    kBrightnessSetByPolicy = 4,
+    // ALS increased beyond the brightening threshold, but ALS data has been
+    // fluctuating above the stabilization threshold.
+    kFluctuatingAlsIncrease = 5,
+    // ALS decreased beyond the darkening threshold, but ALS data has been
+    // fluctuating above the stabilization threshold.
+    kFluctuatingAlsDecrease = 6,
+    // ALS change is within darkening and brightening thresholds.
+    kMinimalAlsChange = 7,
+    // Adapter should only use personal curves but none is available.
+    kMissingPersonalCurve = 8,
+    kMaxValue = kMissingPersonalCurve
+  };
+
+  struct AdapterDecision {
+    AdapterDecision();
+    AdapterDecision(const AdapterDecision& decision);
+    // If |no_brightness_change_cause| is not nullopt, then brightness
+    // should not be changed.
+    // If |brightness_change_cause| is not nullopt, then brightness should be
+    // changed. In this case |log_als_avg_stddev| should not be nullopt.
+    // Exactly one of |no_brightness_change_cause| and
+    // |brightness_change_cause| should be non-nullopt.
+    // |log_als_avg_stddev| may be set even when brightness should not be
+    // changed. It is only nullopt if there is no ALS data in the data cache.
+    base::Optional<NoBrightnessChangeCause> no_brightness_change_cause;
+    base::Optional<BrightnessChangeCause> brightness_change_cause;
+    base::Optional<AlsAvgStdDev> log_als_avg_stddev;
+  };
+
   Adapter(Profile* profile,
           AlsReader* als_reader,
           BrightnessMonitor* brightness_monitor,
@@ -203,30 +243,23 @@
   // |InitParams|.
   void UpdateStatus();
 
-  // Returns a BrightnessChangeCause if the adapter can change the brightness.
+  // Checks whether brightness should be changed.
   // This is generally the case when the brightness hasn't been manually
   // set, we've received enough initial ambient light readings, and
-  // the ambient light has changed beyond thresholds and has stabilized.
-  // Returns nullopt if it shouldn't change the brightness.
-  base::Optional<BrightnessChangeCause> CanAdjustBrightness(
-      const AlsAvgStdDev& log_als_avg_stddev) const;
+  // the ambient light has changed beyond thresholds and has stabilized, and
+  // also if personal curve exists (if param says we should only use personal
+  // curve).
+  AdapterDecision CanAdjustBrightness(base::TimeTicks now);
 
-  // Called when ambient light changes. It only changes screen brightness if
-  // |CanAdjustBrightness| returns true and a required curve is set up:
-  // if the required curve is personal but no personal curve is available, then
-  // brightness won't be changed.
-  // It will call |OnBrightnessChanged| if brightness is actually changed.
-  // |now| should be the timestamp when ALS reading comes in, i.e. when
-  // |OnAmbientLightUpdated| is called. |OnAmbientLightUpdated| is the event
-  // that triggers the call of |MaybeAdjustBrightness|.
-  void MaybeAdjustBrightness(base::TimeTicks now);
+  // Changes the brightness. In addition to asking powerd to
+  // change brightness, it also calls |OnBrightnessChanged| and writes to logs.
+  void AdjustBrightness(BrightnessChangeCause cause, double log_als_avg);
 
   // Calculates brightness from given |ambient_log_lux| based on either
   // |global_curve_| or |personal_curve_| (as specified by the experiment
-  // params). Returns nullopt if a personal curve should be used but it's not
-  // available.
-  base::Optional<double> GetBrightnessBasedOnAmbientLogLux(
-      double ambient_log_lux) const;
+  // params). It's only safe to call this method when |CanAdjustBrightness|
+  // returns a |BrightnessChangeCause| in its decision.
+  double GetBrightnessBasedOnAmbientLogLux(double ambient_log_lux) const;
 
   // Called when brightness is changed by the model or user. This function
   // updates |latest_brightness_change_time_|, |current_brightness_|. If
@@ -239,7 +272,7 @@
                            double new_brightness_percent,
                            base::Optional<double> new_log_als);
 
-  // Called by |MaybeAdjustBrightness| when brightness should be changed.
+  // Called by |AdjustBrightness| when brightness should be changed.
   void WriteLogMessages(double new_log_als,
                         double new_brightness,
                         BrightnessChangeCause cause) const;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index ca7f4d93..8baab65 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1522,6 +1522,11 @@
     "expiry_milestone": 78
   },
   {
+    "name": "enable-portals",
+    "owners": [ "adithyas", "jbroman", "lfg" ],
+    "expiry_milestone": 80
+  },
+  {
     "name": "enable-previews-android-omnibox-ui",
     "owners": [ "//components/data_reduction_proxy/OWNERS" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4c26b02..156682f 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -747,6 +747,12 @@
 const char kEnableNewDownloadBackendDescription[] =
     "Enables the new download backend that uses offline content provider";
 
+const char kEnablePortalsName[] = "Enable Portals.";
+const char kEnablePortalsDescription[] =
+    "Portals are an experimental web platform feature that allows embedding"
+    " and seamless transitions between pages."
+    " See https://github.com/WICG/portals and https://wicg.github.io/portals/";
+
 const char kEnableNotificationScrollBarName[] =
     "Enable notification list scroll bar";
 const char kEnableNotificationScrollBarDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 2c26f27..38e8643 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -512,6 +512,9 @@
 extern const char kEnableNewDownloadBackendName[];
 extern const char kEnableNewDownloadBackendDescription[];
 
+extern const char kEnablePortalsName[];
+extern const char kEnablePortalsDescription[];
+
 extern const char kEnablePictureInPictureName[];
 extern const char kEnablePictureInPictureDescription[];
 
diff --git a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
index cc91963..041cb6d1 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.cc
@@ -67,6 +67,14 @@
           "list. See https://www.chromestatus.com/feature/5706745674465280 for "
           "more details.");
       return;
+    case WebFeature::kDownloadInAdFrameWithoutUserGesture:
+      rfh->AddMessageToConsole(
+          blink::mojom::ConsoleMessageLevel::kWarning,
+          "[Deprecation] Download in ad frame without user activation is "
+          "deprecated and will be removed in M76, around July 2019. See "
+          "https://www.chromestatus.com/feature/6311883621531648 for more "
+          "details.");
+      return;
 
     default:
       return;
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index f1392f5f..1dff807 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -1154,7 +1154,8 @@
   EXPECT_FALSE(prompt_observer->IsSavePromptShownAutomatically());
 }
 
-IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DeleteFrameBeforeSubmit) {
+// TODO(crbug.com/949908) The test is flaky (crashing) on all platforms.
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, DISABLED_DeleteFrameBeforeSubmit) {
   NavigateToFile("/password/multi_frames.html");
 
   NavigationObserver observer(WebContents());
diff --git a/chrome/browser/performance_manager/performance_manager.cc b/chrome/browser/performance_manager/performance_manager.cc
index 246c16f..dae724cc 100644
--- a/chrome/browser/performance_manager/performance_manager.cc
+++ b/chrome/browser/performance_manager/performance_manager.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/feature_list.h"
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/task/post_task.h"
@@ -71,12 +72,12 @@
   instance->task_runner_->DeleteSoon(FROM_HERE, instance.release());
 }
 
-void PerformanceManager::DistributeMeasurementBatch(
-    std::unique_ptr<ProcessResourceMeasurementBatch> batch) {
+void PerformanceManager::CallOnGraph(const base::Location& from_here,
+                                     GraphCallback callback) {
+  DCHECK(!callback.is_null());
   task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&PerformanceManager::DistributeMeasurementBatchImpl,
-                     base::Unretained(this), std::move(batch)));
+      FROM_HERE, base::BindOnce(&PerformanceManager::CallOnGraphImpl,
+                                base::Unretained(this), std::move(callback)));
 }
 
 std::unique_ptr<FrameNodeImpl> PerformanceManager::CreateFrameNode(
@@ -186,6 +187,10 @@
           connection ? connection->GetConnector()->Clone() : nullptr));
 }
 
+void PerformanceManager::CallOnGraphImpl(GraphCallback graph_callback) {
+  std::move(graph_callback).Run(&graph_);
+}
+
 void PerformanceManager::OnStartImpl(
     std::unique_ptr<service_manager::Connector> connector) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -226,14 +231,6 @@
                                     service_manager::BindSourceInfo());
 }
 
-void PerformanceManager::DistributeMeasurementBatchImpl(
-    std::unique_ptr<ProcessResourceMeasurementBatch> batch) {
-  SystemNodeImpl* system_node = graph_.FindOrCreateSystemNode();
-  DCHECK(system_node);
-
-  system_node->DistributeMeasurementBatch(std::move(batch));
-}
-
 void PerformanceManager::BindWebUIGraphDump(
     resource_coordinator::mojom::WebUIGraphDumpRequest request,
     const service_manager::BindSourceInfo& source_info) {
diff --git a/chrome/browser/performance_manager/performance_manager.h b/chrome/browser/performance_manager/performance_manager.h
index f2bc634..67ba160 100644
--- a/chrome/browser/performance_manager/performance_manager.h
+++ b/chrome/browser/performance_manager/performance_manager.h
@@ -10,6 +10,8 @@
 #include <utility>
 #include <vector>
 
+#include "base/callback.h"
+#include "base/location.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "chrome/browser/performance_manager/graph/graph.h"
@@ -28,7 +30,6 @@
 namespace performance_manager {
 
 class PageNodeImpl;
-struct ProcessResourceMeasurementBatch;
 
 // The performance manager is a rendezvous point for binding to performance
 // manager interfaces.
@@ -51,17 +52,16 @@
   // deletion on its sequence.
   static void Destroy(std::unique_ptr<PerformanceManager> instance);
 
+  // Invokes |graph_callback| on the performance manager's sequence, with the
+  // graph as a parameter.
+  using GraphCallback = base::OnceCallback<void(Graph*)>;
+  void CallOnGraph(const base::Location& from_here,
+                   GraphCallback graph_callback);
+
   // Forwards the binding request to the implementation class.
   template <typename Interface>
   void BindInterface(mojo::InterfaceRequest<Interface> request);
 
-  // Dispatches a measurement batch to the SystemNode on the performance
-  // sequence. This is a temporary method to support the RenderProcessProbe,
-  // which will soon go away as the performance measurement moves to the
-  // performance sequence.
-  void DistributeMeasurementBatch(
-      std::unique_ptr<ProcessResourceMeasurementBatch> batch);
-
   // Creates a new node of the requested type and adds it to the graph.
   // May be called from any sequence.
   std::unique_ptr<FrameNodeImpl> CreateFrameNode(
@@ -104,10 +104,9 @@
 
   void OnStart();
   void OnStartImpl(std::unique_ptr<service_manager::Connector> connector);
+  void CallOnGraphImpl(GraphCallback graph_callback);
   void BindInterfaceImpl(const std::string& interface_name,
                          mojo::ScopedMessagePipeHandle message_pipe);
-  void DistributeMeasurementBatchImpl(
-      std::unique_ptr<ProcessResourceMeasurementBatch> batch);
 
   void BindWebUIGraphDump(
       resource_coordinator::mojom::WebUIGraphDumpRequest request,
diff --git a/chrome/browser/performance_manager/performance_manager_unittest.cc b/chrome/browser/performance_manager/performance_manager_unittest.cc
index d96325e..b20745f 100644
--- a/chrome/browser/performance_manager/performance_manager_unittest.cc
+++ b/chrome/browser/performance_manager/performance_manager_unittest.cc
@@ -38,6 +38,8 @@
     task_environment_.RunUntilIdle();
   }
 
+  void RunUntilIdle() { task_environment_.RunUntilIdle(); }
+
  protected:
   PerformanceManager* performance_manager() {
     return performance_manager_.get();
@@ -103,8 +105,21 @@
   performance_manager()->BatchDeleteNodes(std::move(nodes));
 }
 
+TEST_F(PerformanceManagerTest, CallOnGraph) {
+  // Create a page node for something to target.
+  std::unique_ptr<PageNodeImpl> page_node =
+      performance_manager()->CreatePageNode();
+
+  PerformanceManager::GraphCallback graph_callback = base::BindLambdaForTesting(
+      [&page_node](Graph* graph) { EXPECT_EQ(page_node->graph(), graph); });
+
+  performance_manager()->CallOnGraph(FROM_HERE, std::move(graph_callback));
+  RunUntilIdle();
+
+  performance_manager()->DeleteNode(std::move(page_node));
+}
+
 // TODO(siggi): More tests!
 // - Test the WebUI interface.
-// - Test the graph introspector interface.
 
 }  // namespace performance_manager
diff --git a/chrome/browser/policy/test/policy_testserver.py b/chrome/browser/policy/test/policy_testserver.py
index 3b86cd0..5a16814f 100644
--- a/chrome/browser/policy/test/policy_testserver.py
+++ b/chrome/browser/policy/test/policy_testserver.py
@@ -308,7 +308,8 @@
     if request_type == 'register':
       response = self.ProcessRegister(rmsg.register_request)
     elif request_type == 'certificate_based_register':
-      response = self.ProcessCertBasedRegister(rmsg.register_request)
+      response = self.ProcessCertBasedRegister(
+          rmsg.certificate_based_register_request)
     elif request_type == 'api_authorization':
       response = self.ProcessApiAuthorization(rmsg.service_api_access_request)
     elif request_type == 'unregister':
@@ -450,8 +451,9 @@
     """
     # Unwrap the request
     try:
-      req = self.UnwrapCertificateBasedDeviceRegistrationData(signed_msg)
-    except (Error):
+      req = self.UnwrapCertificateBasedDeviceRegistrationData(
+          signed_msg.signed_request)
+    except (IOError):
       return(400, 'Invalid request')
 
     # TODO(drcrash): Check the certificate itself.
@@ -505,6 +507,12 @@
     """Verifies the signature of |msg| and if it is valid, return the
     certificate based device registration data. If not, throws an
     exception.
+
+    Args:
+      msg: SignedData received from the client.
+
+    Returns:
+      CertificateBasedDeviceRegistrationData
     """
     # TODO(drcrash): Verify signature.
     rdata = dm.CertificateBasedDeviceRegistrationData()
diff --git a/chrome/browser/resource_coordinator/render_process_probe.cc b/chrome/browser/resource_coordinator/render_process_probe.cc
index 8d3eea04b..76a9b21 100644
--- a/chrome/browser/resource_coordinator/render_process_probe.cc
+++ b/chrome/browser/resource_coordinator/render_process_probe.cc
@@ -10,6 +10,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
+#include "chrome/browser/performance_manager/graph/graph.h"
 #include "chrome/browser/performance_manager/graph/system_node_impl.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -261,7 +262,22 @@
       performance_manager::PerformanceManager::GetInstance();
 
   if (performance_manager && !batch->measurements.empty())
-    performance_manager->DistributeMeasurementBatch(std::move(batch));
+    performance_manager->CallOnGraph(
+        FROM_HERE,
+        base::BindOnce(
+            &RenderProcessProbeImpl::DistributeMeasurementBatchToSystemNode,
+            std::move(batch)));
+}
+
+// static
+void RenderProcessProbeImpl::DistributeMeasurementBatchToSystemNode(
+    std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch> batch,
+    performance_manager::Graph* graph) {
+  performance_manager::SystemNodeImpl* system_node =
+      graph->FindOrCreateSystemNode();
+  DCHECK(system_node);
+
+  system_node->DistributeMeasurementBatch(std::move(batch));
 }
 
 }  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/render_process_probe.h b/chrome/browser/resource_coordinator/render_process_probe.h
index 64a6114..36eadd2 100644
--- a/chrome/browser/resource_coordinator/render_process_probe.h
+++ b/chrome/browser/resource_coordinator/render_process_probe.h
@@ -19,6 +19,7 @@
 
 namespace performance_manager {
 struct ProcessResourceMeasurementBatch;
+class Graph;
 }  // namespace performance_manager
 
 namespace resource_coordinator {
@@ -105,6 +106,11 @@
       std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch>
           batch);
 
+  static void DistributeMeasurementBatchToSystemNode(
+      std::unique_ptr<performance_manager::ProcessResourceMeasurementBatch>
+          batch,
+      performance_manager::Graph* graph);
+
   // A map of currently running render process host IDs to process.
   // This map is accessed alternatively from the UI thread and the IO thread,
   // but only one of the two at a time.
diff --git a/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc b/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
index 5a61622..595b74c 100644
--- a/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_metrics_logger_unittest.cc
@@ -155,7 +155,13 @@
 };
 
 // Tests has_form_entry.
-TEST_F(TabMetricsLoggerTest, GetHasFormEntry) {
+// TODO(crbug.com/949288): The test is flaky on ChromeOS.
+#if defined(OS_CHROMEOS)
+#define MAYBE_GetHasFormEntry DISABLED_GetHasFormEntry
+#else
+#define MAYBE_GetHasFormEntry GetHasFormEntry
+#endif
+TEST_F(TabMetricsLoggerTest, MAYBE_GetHasFormEntry) {
   EXPECT_FALSE(CurrentTabFeatures().has_form_entry);
   content::PageImportanceSignals signal;
   signal.had_form_interaction = true;
diff --git a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
index 83f5ab6..2180914 100644
--- a/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
+++ b/chrome/browser/resources/settings/printing_page/cups_edit_printer_dialog.js
@@ -204,11 +204,7 @@
    * @private
    */
   canSavePrinter_: function() {
-    return this.printerInfoChanged_ &&
-        (settings.printing.isNameAndAddressValid(this.activePrinter) &&
-         settings.printing.isPPDInfoValid(
-             this.activePrinter.ppdManufacturer, this.activePrinter.ppdModel,
-             this.activePrinter.printerPPDPath));
+    return this.printerInfoChanged_ && this.isPrinterValid();
   },
 
   /**
@@ -285,4 +281,15 @@
     }
     this.userPPD_ = settings.printing.getBaseName(path);
   },
+
+  /*
+   * Returns true if the printer has valid name, address, and PPD.
+   * @return {boolean}
+   */
+  isPrinterValid: function() {
+    return settings.printing.isNameAndAddressValid(this.activePrinter) &&
+        settings.printing.isPPDInfoValid(
+            this.activePrinter.ppdManufacturer, this.activePrinter.ppdModel,
+            this.activePrinter.printerPPDPath);
+  },
 });
diff --git a/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc b/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
index 86b0345..f919b0a2 100644
--- a/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/file_analyzer_unittest.cc
@@ -729,7 +729,8 @@
   EXPECT_FALSE(result_.archived_binaries.Get(0).digests().sha256().empty());
 }
 
-TEST_F(FileAnalyzerTest, LargeRarSkipsContentInspection) {
+// TODO(crbug.com/949399): The test is flaky (fail, timeout) on all platforms.
+TEST_F(FileAnalyzerTest, DISABLED_LargeRarSkipsContentInspection) {
   scoped_refptr<MockBinaryFeatureExtractor> extractor =
       new testing::StrictMock<MockBinaryFeatureExtractor>();
   FileAnalyzer analyzer(extractor);
diff --git a/chrome/browser/search/tools/generate_integrity_header.py b/chrome/browser/search/tools/generate_integrity_header.py
index 94ead9d..228aa13 100755
--- a/chrome/browser/search/tools/generate_integrity_header.py
+++ b/chrome/browser/search/tools/generate_integrity_header.py
@@ -13,7 +13,7 @@
 
 def ComputeIntegrity(input_path):
   hasher = hashlib.sha256()
-  with open(input_path, 'r') as f:
+  with open(input_path, 'rb') as f:
     hasher.update(f.read())
   return base64.b64encode(hasher.digest())
 
@@ -32,7 +32,7 @@
       define_name = re.sub('\W', '_', input_filename.upper())
       define_name = define_name + '_INTEGRITY'
 
-      f.write('#define ' + define_name + ' "' + integrity + '"\n')
+      f.write('#define ' + define_name + ' "' + integrity.decode() + '"\n')
 
       f.write('\n')
 
diff --git a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
index ba706e5..d3cf2502 100644
--- a/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_wallet_sync_test.cc
@@ -241,16 +241,8 @@
   EXPECT_EQ(kLaterTime, server_addresses[0]->use_date());
 }
 
-// Disabled due to flakiness: https://crbug.com/947692.
-#if defined(THREAD_SANITIZER)
-#define MAYBE_UpdateServerAddressMetadataWhileNotSyncing \
-  DISABLED_UpdateServerAddressMetadataWhileNotSyncing
-#else
-#define MAYBE_UpdateServerAddressMetadataWhileNotSyncing \
-  UpdateServerAddressMetadataWhileNotSyncing
-#endif
 IN_PROC_BROWSER_TEST_P(TwoClientWalletSyncTest,
-                       MAYBE_UpdateServerAddressMetadataWhileNotSyncing) {
+                       UpdateServerAddressMetadataWhileNotSyncing) {
   GetFakeServer()->SetWalletData(
       {CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
        CreateDefaultSyncPaymentsCustomerData()});
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 8114750..769eaeb4 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine.cc
@@ -702,13 +702,6 @@
   }
 }
 
-void SyncEngine::OnPrimaryAccountSigninFailed(
-    const GoogleServiceAuthError& error) {
-  Reset();
-  UpdateServiceState(REMOTE_SERVICE_AUTHENTICATION_REQUIRED,
-                     "Failed to sign in.");
-}
-
 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 d20e692..2cb3d67 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_engine.h
+++ b/chrome/browser/sync_file_system/drive_backend/sync_engine.h
@@ -151,8 +151,6 @@
       const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const CoreAccountInfo& previous_primary_account_info) override;
-  void OnPrimaryAccountSigninFailed(
-      const GoogleServiceAuthError& error) override;
 
  private:
   class WorkerObserver;
diff --git a/chrome/browser/ui/cocoa/task_manager_mac.mm b/chrome/browser/ui/cocoa/task_manager_mac.mm
index 78740ae..95ba3072 100644
--- a/chrome/browser/ui/cocoa/task_manager_mac.mm
+++ b/chrome/browser/ui/cocoa/task_manager_mac.mm
@@ -291,6 +291,7 @@
   base::scoped_nsobject<NSTableView> tableView(
       [[NSTableView alloc] initWithFrame:NSMakeRect(0, 0, 400, 200)]);
   [tableView setAllowsColumnReordering:NO];
+  [tableView setAllowsMultipleSelection:YES];
   [tableView setAutosaveTableColumns:NO];
   [tableView
       setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle];
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc
index 4a9ceaa..35d5852e 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view_interactive_uitest.cc
@@ -70,8 +70,14 @@
 
 #if defined(USE_AURA)
 // Verify that the hover card is not visible when any key is pressed.
+// TODO(crbug.com/947668): The test is flaky on Win10.
+#if defined(OS_WIN)
+#define MAYBE_HoverCardHidesOnAnyKeyPressInSameWindow DISABLED_HoverCardHidesOnAnyKeyPressInSameWindow
+#else
+#define MAYBE_HoverCardHidesOnAnyKeyPressInSameWindow HoverCardHidesOnAnyKeyPressInSameWindow
+#endif
 IN_PROC_BROWSER_TEST_F(TabHoverCardBubbleViewInteractiveUiTest,
-                       HoverCardHidesOnAnyKeyPressInSameWindow) {
+                       MAYBE_HoverCardHidesOnAnyKeyPressInSameWindow) {
   TabStrip* tab_strip =
       BrowserView::GetBrowserViewForBrowser(browser())->tabstrip();
   Tab* tab = tab_strip->tab_at(0);
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 71a622f..513391509 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -54,7 +54,7 @@
   }
 
   // Blocks and waits until the user signs in. Wait() does not block if a
-  // GoogleSigninSucceeded or a GoogleSigninFailed has already occurred.
+  // GoogleSigninSucceeded has already occurred.
   void Wait() {
     if (seen_)
       return;
@@ -65,12 +65,6 @@
     EXPECT_TRUE(seen_);
   }
 
-  void OnPrimaryAccountSigninFailed(
-      const GoogleServiceAuthError& error) override {
-    DVLOG(1) << "Google signin failed.";
-    QuitLoopRunner();
-  }
-
   void OnPrimaryAccountSet(
       const CoreAccountInfo& primary_account_info) override {
     DVLOG(1) << "Google signin succeeded.";
diff --git a/chrome/chrome_cleaner/BUILD.gn b/chrome/chrome_cleaner/BUILD.gn
index 1b6e3ec..163f6f0 100644
--- a/chrome/chrome_cleaner/BUILD.gn
+++ b/chrome/chrome_cleaner/BUILD.gn
@@ -30,8 +30,13 @@
   }
 }
 
-# This library should only be included in executable targets.
+# This library should only be included in executable targets.  It contains
+# definitions used by the unit tests and misc helper binaries. The
+# software_reporter_tool and chrome_cleanup_tool binaries will depend on
+# specific reporter-only and cleaner-only versions of these definitions.
 static_library("other_executable_definitions") {
+  testonly = true
+
   sources = [
     "//chrome/chrome_cleaner/logging/other_logging_definitions.cc",
     "//chrome/chrome_cleaner/settings/other_settings_definitions.cc",
@@ -100,4 +105,12 @@
   if (is_internal_chrome_cleaner_build) {
     deps += [ "//chrome_cleaner_internal:unittest_sources" ]
   }
+
+  # TODO(crbug.com/949669): Add an integration test of the top-level
+  # executables. For now, just add a dependency on them to make sure they
+  # build.
+  data_deps = [
+    "//chrome/chrome_cleaner/executables:chrome_cleanup_tool",
+    "//chrome/chrome_cleaner/executables:software_reporter_tool",
+  ]
 }
diff --git a/chrome/chrome_cleaner/components/BUILD.gn b/chrome/chrome_cleaner/components/BUILD.gn
index 7c2d40e..c3acdda 100644
--- a/chrome/chrome_cleaner/components/BUILD.gn
+++ b/chrome/chrome_cleaner/components/BUILD.gn
@@ -63,6 +63,7 @@
     "//chrome/chrome_cleaner/constants:uws_id",
     "//chrome/chrome_cleaner/http:mock_http_agent_factory",
     "//chrome/chrome_cleaner/logging:cleaner_logging",
+    "//chrome/chrome_cleaner/logging:common",
     "//chrome/chrome_cleaner/logging/proto:chrome_cleaner_report_proto",
     "//chrome/chrome_cleaner/os:cleaner_os",
     "//chrome/chrome_cleaner/os:common_os",
diff --git a/chrome/chrome_cleaner/components/recovery_component.cc b/chrome/chrome_cleaner/components/recovery_component.cc
index c263223..d08987f 100644
--- a/chrome/chrome_cleaner/components/recovery_component.cc
+++ b/chrome/chrome_cleaner/components/recovery_component.cc
@@ -45,7 +45,8 @@
                              0x19, 0x7a, 0x71, 0x4b, 0x0a, 0x7c, 0x80, 0x1c,
                              0xf6, 0x29, 0x7c, 0x0a, 0x5f, 0xea, 0x67, 0xb7};
 
-// Name of the executable file as well as the command line arg to use for Foil.
+// Name of the executable file as well as the command line arg to use when run
+// from the Chrome Cleanup tool.
 const wchar_t kChromeRecoveryExe[] = L"ChromeRecovery.exe";
 const char kChromeRecoveryArg[] = "/installsource swreporter";
 
diff --git a/chrome/chrome_cleaner/constants/BUILD.gn b/chrome/chrome_cleaner/constants/BUILD.gn
index 811ac40b..1fa6b1c 100644
--- a/chrome/chrome_cleaner/constants/BUILD.gn
+++ b/chrome/chrome_cleaner/constants/BUILD.gn
@@ -35,6 +35,30 @@
   output = "$target_gen_dir/software_reporter_tool_branding.h"
 }
 
+process_version("chrome_cleaner_version_resource") {
+  template_file = "chrome_cleaner.rc.version"
+
+  sources = [
+    "chrome_cleanup_tool_exe.ver",
+    cleaner_branding_path,
+    version_path,
+  ]
+
+  output = "$target_gen_dir/chrome_cleaner.rc"
+}
+
+process_version("reporter_version_resource") {
+  template_file = "chrome_cleaner.rc.version"
+
+  sources = [
+    "software_reporter_tool_exe.ver",
+    reporter_branding_path,
+    version_path,
+  ]
+
+  output = "$target_gen_dir/chrome_reporter.rc"
+}
+
 source_set("common_strings") {
   sources = [
     "chrome_cleaner_switches.cc",
diff --git a/chrome/chrome_cleaner/constants/chrome_cleaner.rc.version b/chrome/chrome_cleaner/constants/chrome_cleaner.rc.version
new file mode 100644
index 0000000..322d86d
--- /dev/null
+++ b/chrome/chrome_cleaner/constants/chrome_cleaner.rc.version
@@ -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.
+
+#include <verrsrc.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @MAJOR@,@MINOR@,@BUILD@
+ PRODUCTVERSION @MAJOR@,@MINOR@,@BUILD@
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "@COMPANY_FULLNAME@"
+            VALUE "FileDescription", "@PRODUCT_FULLNAME@"
+            VALUE "FileVersion", "@MAJOR@.@MINOR@.@BUILD@"
+            VALUE "InternalName", "@INTERNAL_NAME@"
+            VALUE "LegalCopyright", "@COPYRIGHT@"
+            VALUE "OriginalFilename", "@ORIGINAL_FILENAME@"
+            VALUE "ProductName", "@PRODUCT_FULLNAME@"
+            VALUE "ProductVersion", "@MAJOR@.@MINOR@.@BUILD@"
+            VALUE "CompanyShortName", "@COMPANY_SHORTNAME@"
+            VALUE "ProductShortName", "@PRODUCT_SHORTNAME@"
+            VALUE "Official Build", "@OFFICIAL_BUILD@"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
diff --git a/chrome/chrome_cleaner/constants/chrome_cleanup_tool_exe.ver b/chrome/chrome_cleaner/constants/chrome_cleanup_tool_exe.ver
new file mode 100644
index 0000000..747882bb
--- /dev/null
+++ b/chrome/chrome_cleaner/constants/chrome_cleanup_tool_exe.ver
@@ -0,0 +1,2 @@
+INTERNAL_NAME=chrome_cleanup_tool_exe
+ORIGINAL_FILENAME=chrome_cleanup.exe
diff --git a/chrome/chrome_cleaner/constants/software_reporter_tool_exe.ver b/chrome/chrome_cleaner/constants/software_reporter_tool_exe.ver
new file mode 100644
index 0000000..0d3600f5
--- /dev/null
+++ b/chrome/chrome_cleaner/constants/software_reporter_tool_exe.ver
@@ -0,0 +1,2 @@
+INTERNAL_NAME=software_reporter_tool_exe
+ORIGINAL_FILENAME=software_reporter_tool.exe
diff --git a/chrome/chrome_cleaner/engines/DEPS b/chrome/chrome_cleaner/engines/DEPS
new file mode 100644
index 0000000..ef8ad28
--- /dev/null
+++ b/chrome/chrome_cleaner/engines/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+mojo/public",
+]
diff --git a/chrome/chrome_cleaner/engines/controllers/BUILD.gn b/chrome/chrome_cleaner/engines/controllers/BUILD.gn
index 871823a6..3b3d031 100644
--- a/chrome/chrome_cleaner/engines/controllers/BUILD.gn
+++ b/chrome/chrome_cleaner/engines/controllers/BUILD.gn
@@ -46,6 +46,7 @@
 
   deps = [
     ":engine_cleaner_lib",
+    ":facade_interface_header",
     "//base:base",
     "//chrome/chrome_cleaner/cleaner:cleaner_headers",
     "//chrome/chrome_cleaner/components:components",
@@ -89,6 +90,18 @@
   ]
 }
 
+source_set("facade_interface_header") {
+  sources = [
+    "engine_facade_interface.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chrome/chrome_cleaner/cleaner:cleaner_headers",
+    "//chrome/chrome_cleaner/scanner:scanner_api",
+  ]
+}
+
 source_set("engine_cleaner_lib") {
   sources = [
     "elevating_facade.cc",
@@ -97,13 +110,13 @@
     "engine_cleaner.h",
     "engine_facade.cc",
     "engine_facade.h",
-    "engine_facade_interface.h",
     "uwe_engine_cleaner_wrapper.cc",
     "uwe_engine_cleaner_wrapper.h",
   ]
 
   deps = [
     ":common",
+    ":facade_interface_header",
     "//base",
     "//chrome/chrome_cleaner/chrome_utils:extensions_util_lib",
     "//chrome/chrome_cleaner/chrome_utils:force_installed_extension",
@@ -172,6 +185,7 @@
     "//chrome/chrome_cleaner/logging/proto:removal_status_proto",
     "//chrome/chrome_cleaner/os:cleaner_os",
     "//chrome/chrome_cleaner/os:common_os",
+    "//chrome/chrome_cleaner/os:file_remover_api",
     "//chrome/chrome_cleaner/parsers/broker:parser_sandbox_broker",
     "//chrome/chrome_cleaner/parsers/json_parser:json_parser",
     "//chrome/chrome_cleaner/parsers/shortcut_parser/broker:fake_shortcut_parser",
diff --git a/chrome/chrome_cleaner/executables/BUILD.gn b/chrome/chrome_cleaner/executables/BUILD.gn
new file mode 100644
index 0000000..9400ae2
--- /dev/null
+++ b/chrome/chrome_cleaner/executables/BUILD.gn
@@ -0,0 +1,152 @@
+# 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("//chrome/chrome_cleaner/chrome_cleaner_args.gni")
+
+group("engine_definitions") {
+  if (is_internal_chrome_cleaner_build) {
+    deps = [
+      "//chrome/chrome_cleaner_internal:engine_definitions",
+    ]
+  } else {
+    deps = [
+      "//chrome/chrome_cleaner/engines/common:dummy_engine_resources",
+      "//chrome/chrome_cleaner/settings:dummy_engine_settings",
+    ]
+  }
+}
+
+group("engine_target_factory") {
+  if (is_internal_chrome_cleaner_build) {
+    deps = [
+      "//chrome/chrome_cleaner_internal:engine_target_factory",
+    ]
+  } else {
+    deps = [
+      "//chrome/chrome_cleaner/engines/target:dummy_engine_delegate_factory",
+    ]
+  }
+}
+
+source_set("shutdown_sequence") {
+  sources = [
+    "shutdown_sequence.cc",
+    "shutdown_sequence.h",
+  ]
+
+  deps = [
+    "//base:base",
+    "//chrome/chrome_cleaner/engines/broker:common",
+    "//chrome/chrome_cleaner/engines/controllers:facade_interface_header",
+    "//chrome/chrome_cleaner/ipc:mojo_task_runner",
+  ]
+}
+
+# The Chrome Cleanup tool is distributed as two executables:
+# software_reporter_tool only scans the user's system and returns a status code
+# to Chrome, while chrome_cleanup_tool is invoked to remove UwS. This template
+# holds the definitions they share.
+template("chrome_cleaner_executable") {
+  executable(target_name) {
+    sources = invoker.sources
+
+    # Default entrypoint is main, console app. Change it to wWinMain, windowed.
+    configs -= [ "//build/config/win:console" ]
+    configs += [ "//build/config/win:windowed" ]
+    if (defined(invoker.configs)) {
+      configs += invoker.configs
+    }
+
+    libs = [
+      "crypt32.lib",
+      "mstask.lib",
+      "ntdll.lib",
+      "taskschd.lib",
+      "wintrust.lib",
+    ]
+    if (defined(invoker.libs)) {
+      libs += invoker.libs
+    }
+
+    deps = [
+      ":engine_definitions",
+      ":engine_target_factory",
+      ":shutdown_sequence",
+      "//base:base",
+      "//build/win:default_exe_manifest",
+      "//chrome/chrome_cleaner/constants:common_strings",
+      "//chrome/chrome_cleaner/constants:version_header",
+      "//chrome/chrome_cleaner/crash:crashpad_lib",
+      "//chrome/chrome_cleaner/engines/broker:common",
+      "//chrome/chrome_cleaner/engines/broker:interface_log_service",
+      "//chrome/chrome_cleaner/engines/common:resources_header",
+      "//chrome/chrome_cleaner/engines/target:common",
+      "//chrome/chrome_cleaner/engines/target:engine_delegate_factory_header",
+      "//chrome/chrome_cleaner/ipc:mojo_task_runner",
+      "//chrome/chrome_cleaner/ipc:sandbox",
+      "//chrome/chrome_cleaner/logging:common",
+      "//chrome/chrome_cleaner/logging:logging_definitions",
+      "//chrome/chrome_cleaner/os:common_os",
+      "//chrome/chrome_cleaner/parsers/broker:parser_sandbox_broker",
+      "//chrome/chrome_cleaner/parsers/shortcut_parser/broker:sandboxed_shortcut_parser",
+      "//chrome/chrome_cleaner/parsers/target:parser_sandbox_target",
+      "//chrome/chrome_cleaner/settings:default_matching_options",
+      "//chrome/chrome_cleaner/settings:engine_settings_header",
+      "//chrome/chrome_cleaner/settings:matching_options",
+      "//chrome/chrome_cleaner/settings:settings",
+      "//chrome/chrome_cleaner/settings:settings_types",
+      "//sandbox/win:sandbox",
+    ]
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+    }
+  }
+}
+
+chrome_cleaner_executable("chrome_cleanup_tool") {
+  sources = [
+    "chrome_cleaner_main.cc",
+  ]
+
+  libs = [ "comctl32.lib" ]
+
+  deps = [
+    "//chrome/chrome_cleaner/cleaner:cleaner_headers",
+    "//chrome/chrome_cleaner/components",
+    "//chrome/chrome_cleaner/constants:chrome_cleaner_version_resource",
+    "//chrome/chrome_cleaner/constants:chrome_cleanup_tool_branding_header",
+    "//chrome/chrome_cleaner/engines/controllers:engine_cleaner_lib",
+    "//chrome/chrome_cleaner/engines/controllers:facade_interface_header",
+    "//chrome/chrome_cleaner/engines/controllers:main_controller",
+    "//chrome/chrome_cleaner/ipc:chrome_prompt_ipc",
+    "//chrome/chrome_cleaner/logging:cleaner_logging",
+    "//chrome/chrome_cleaner/logging:cleaner_logging_definitions",
+    "//chrome/chrome_cleaner/os:cleaner_os",
+    "//chrome/chrome_cleaner/parsers/json_parser",
+    "//chrome/chrome_cleaner/scanner:force_installed_extension_scanner",
+    "//chrome/chrome_cleaner/scanner:force_installed_extension_scanner_api",
+    "//chrome/chrome_cleaner/settings:cleaner_settings_definitions",
+    "//chrome/chrome_cleaner/zip_archiver/target:common",
+  ]
+}
+
+chrome_cleaner_executable("software_reporter_tool") {
+  sources = [
+    "chrome_reporter_main.cc",
+  ]
+
+  deps = [
+    "//chrome/chrome_cleaner/constants:reporter_version_resource",
+    "//chrome/chrome_cleaner/constants:software_reporter_tool_branding_header",
+    "//chrome/chrome_cleaner/engines/broker:disabled_cleaner_sandbox_interface",
+    "//chrome/chrome_cleaner/engines/controllers:scanner_controller",
+    "//chrome/chrome_cleaner/logging:noop_logging",
+    "//chrome/chrome_cleaner/logging:reporter_logging",
+    "//chrome/chrome_cleaner/logging:reporter_logging_definitions",
+    "//chrome/chrome_cleaner/logging/proto:reporter_logs_proto",
+    "//chrome/chrome_cleaner/parsers/shortcut_parser/broker:shortcut_parser_api",
+    "//chrome/chrome_cleaner/scanner:reporter_scanner",
+    "//chrome/chrome_cleaner/settings:reporter_settings_definitions",
+  ]
+}
diff --git a/chrome/chrome_cleaner/executables/chrome_cleaner_main.cc b/chrome/chrome_cleaner/executables/chrome_cleaner_main.cc
new file mode 100644
index 0000000..178ec89
--- /dev/null
+++ b/chrome/chrome_cleaner/executables/chrome_cleaner_main.cc
@@ -0,0 +1,628 @@
+// 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 <windows.h>
+
+#include <commctrl.h>
+#include <psapi.h>
+
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/task_scheduler/task_scheduler.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/version.h"
+#include "base/win/scoped_com_initializer.h"
+#include "chrome/chrome_cleaner/components/recovery_component.h"
+#include "chrome/chrome_cleaner/components/system_report_component.h"
+#include "chrome/chrome_cleaner/components/system_restore_point_component.h"
+#include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
+#include "chrome/chrome_cleaner/constants/chrome_cleanup_tool_branding.h"
+#include "chrome/chrome_cleaner/constants/version.h"
+#include "chrome/chrome_cleaner/crash/crash_client.h"
+#include "chrome/chrome_cleaner/crash/crash_reporter.h"
+#include "chrome/chrome_cleaner/engines/broker/interface_log_service.h"
+#include "chrome/chrome_cleaner/engines/broker/sandbox_setup.h"
+#include "chrome/chrome_cleaner/engines/common/engine_resources.h"
+#include "chrome/chrome_cleaner/engines/controllers/elevating_facade.h"
+#include "chrome/chrome_cleaner/engines/controllers/engine_facade.h"
+#include "chrome/chrome_cleaner/engines/controllers/engine_facade_interface.h"
+#include "chrome/chrome_cleaner/engines/controllers/main_controller.h"
+#include "chrome/chrome_cleaner/engines/target/engine_delegate.h"
+#include "chrome/chrome_cleaner/engines/target/engine_delegate_factory.h"
+#include "chrome/chrome_cleaner/engines/target/sandbox_setup.h"
+#include "chrome/chrome_cleaner/executables/shutdown_sequence.h"
+#include "chrome/chrome_cleaner/ipc/chrome_prompt_ipc.h"
+#include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+#include "chrome/chrome_cleaner/ipc/sandbox.h"
+#include "chrome/chrome_cleaner/logging/logging_service_api.h"
+#include "chrome/chrome_cleaner/logging/pending_logs_service.h"
+#include "chrome/chrome_cleaner/logging/registry_logger.h"
+#include "chrome/chrome_cleaner/logging/scoped_logging.h"
+#include "chrome/chrome_cleaner/os/disk_util.h"
+#include "chrome/chrome_cleaner/os/early_exit.h"
+#include "chrome/chrome_cleaner/os/file_path_sanitization.h"
+#include "chrome/chrome_cleaner/os/initializer.h"
+#include "chrome/chrome_cleaner/os/post_reboot_registration.h"
+#include "chrome/chrome_cleaner/os/pre_fetched_paths.h"
+#include "chrome/chrome_cleaner/os/rebooter.h"
+#include "chrome/chrome_cleaner/os/secure_dll_loading.h"
+#include "chrome/chrome_cleaner/os/system_util.h"
+#include "chrome/chrome_cleaner/os/system_util_cleaner.h"
+#include "chrome/chrome_cleaner/os/task_scheduler.h"
+#include "chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.h"
+#include "chrome/chrome_cleaner/parsers/json_parser/json_parser_api.h"
+#include "chrome/chrome_cleaner/parsers/json_parser/sandboxed_json_parser.h"
+#include "chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.h"
+#include "chrome/chrome_cleaner/parsers/target/sandbox_setup.h"
+#include "chrome/chrome_cleaner/scanner/force_installed_extension_scanner_impl.h"
+#include "chrome/chrome_cleaner/settings/engine_settings.h"
+#include "chrome/chrome_cleaner/settings/matching_options.h"
+#include "chrome/chrome_cleaner/settings/settings.h"
+#include "chrome/chrome_cleaner/settings/settings_types.h"
+#include "chrome/chrome_cleaner/zip_archiver/target/sandbox_setup.h"
+#include "components/chrome_cleaner/public/constants/constants.h"
+#include "components/chrome_cleaner/public/constants/result_codes.h"
+#include "sandbox/win/src/sandbox_factory.h"
+
+namespace {
+
+using chrome_cleaner::ExecutionMode;
+
+// The number of milliseconds to sleep to delay the self-deletion.
+const uint32_t kSelfDeleteDelayMs = 1000;
+
+const wchar_t kElevatedLogFileSuffix[] = L"-elevated";
+
+// A callback for the logs service to call us back when it's done with logs
+// upload. |success| is the result of the upload, and |succeeded|, when not
+// null, is set with the |success| value.
+void LogsUploadCallback(bool* succeeded,
+                        base::OnceClosure quit_closure,
+                        bool success) {
+  if (succeeded)
+    *succeeded = success;
+  // Use a task instead of a direct call to QuitWhenIdle, in case we are called
+  // synchronously because of an upload error, and the message loop is not
+  // running yet.
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                std::move(quit_closure));
+}
+
+void AddComponents(chrome_cleaner::MainController* main_controller,
+                   base::CommandLine* command_line,
+                   chrome_cleaner::JsonParserAPI* json_parser,
+                   chrome_cleaner::SandboxedShortcutParser* shortcut_parser) {
+#if defined(CHROME_CLEANER_OFFICIAL_BUILD)
+  // Ensure that the system restore point component runs first.
+  main_controller->AddComponent(
+      std::make_unique<chrome_cleaner::SystemRestorePointComponent>(
+          PRODUCT_FULLNAME_STRING));
+#endif
+
+  if (chrome_cleaner::RecoveryComponent::IsAvailable())
+    main_controller->AddComponent(
+        std::make_unique<chrome_cleaner::RecoveryComponent>());
+
+  main_controller->AddComponent(
+      std::make_unique<chrome_cleaner::SystemReportComponent>(json_parser,
+                                                              shortcut_parser));
+}
+
+void SendLogsToSafeBrowsing(chrome_cleaner::ResultCode exit_code,
+                            chrome_cleaner::RegistryLogger* registry_logger) {
+  chrome_cleaner::LoggingServiceAPI* logging_service =
+      chrome_cleaner::LoggingServiceAPI::GetInstance();
+  logging_service->SetExitCode(exit_code);
+  base::RunLoop run_loop;
+  logging_service->SendLogsToSafeBrowsing(
+      base::BindRepeating(&LogsUploadCallback, nullptr,
+                          run_loop.QuitWhenIdleClosure()),
+      registry_logger);
+  run_loop.Run();
+}
+
+chrome_cleaner::ResultCode RelaunchElevated(
+    chrome_cleaner::RegistryLogger* registry_logger) {
+  // If this is being done after we tried to relaunch elevated, there is a
+  // problem. We unfortunately can't report it since the user didn't get a
+  // chance to opt out of logs upload.
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(chrome_cleaner::kElevatedSwitch)) {
+    LOG(ERROR) << "Failed to restart elevated.";
+    return chrome_cleaner::RESULT_CODE_FAILED_TO_ELEVATE;
+  }
+
+  command_line->AppendSwitch(chrome_cleaner::kElevatedSwitch);
+  base::Process elevated_process =
+      chrome_cleaner::LaunchElevatedProcessWithAssociatedWindow(
+          *command_line,
+          /*hwnd=*/nullptr);
+  if (elevated_process.IsValid()) {
+    LOG(INFO) << "Successfully re-launched elevated.";
+  } else {
+    LOG(ERROR) << "Failed to re-launch elevated.";
+    chrome_cleaner::Settings* settings =
+        chrome_cleaner::Settings::GetInstance();
+    if (chrome_cleaner::Rebooter::IsPostReboot()) {
+      chrome_cleaner::ResultCode exit_code =
+          chrome_cleaner::RESULT_CODE_POST_REBOOT_ELEVATION_DENIED;
+      if (settings->logs_upload_allowed())
+        SendLogsToSafeBrowsing(exit_code, registry_logger);
+      return exit_code;
+    }
+    // In legacy mode, we can't upload logs to Safe Browsing if this is not
+    // a post-reboot run. We log a distinct exit code to indicate in UMA that
+    // elevation was declined or the user doesn't have admin rights.
+    return chrome_cleaner::RESULT_CODE_ELEVATION_PROMPT_DECLINED;
+  }
+  return chrome_cleaner::RESULT_CODE_SUCCESS;
+}
+
+// Run the chrome Cleaner to scan and clean.
+chrome_cleaner::ResultCode RunChromeCleaner(
+    base::CommandLine* command_line,
+    chrome_cleaner::RebooterAPI* rebooter,
+    chrome_cleaner::RegistryLogger* registry_logger,
+    chrome_cleaner::ChromePromptIPC* chrome_prompt_ipc,
+    chrome_cleaner::ShutdownSequence shutdown_sequence) {
+  if (command_line->HasSwitch(chrome_cleaner::kCrashSwitch)) {
+    int* crash_me = nullptr;
+    *crash_me = 42;
+  }
+
+  INITCOMMONCONTROLSEX common_control_info = {sizeof(INITCOMMONCONTROLSEX),
+                                              ICC_LINK_CLASS};
+  if (!InitCommonControlsEx(&common_control_info))
+    return chrome_cleaner::RESULT_CODE_FAILED;
+
+  // There is a circular dependency: MainController depends on EngineFacade;
+  // EngineFacade might instantiate a sandbox but the sandbox connection error
+  // handler invokes a method of MainController.
+  //
+  // To break this circle, create MainController first and get the error
+  // handler with MainController::GetSandboxConnectionErrorCallback. Then
+  // create the EngineFacade with the error handler, and set it on the
+  // MainController. It's important that the connection error handler is
+  // available when the EngineFacade is created, otherwise there's a race
+  // condition where a sandbox target process can be spawned without an error
+  // handler, so any disconnection before MainController is created wouldn't be
+  // handled.
+
+  chrome_cleaner::MainController main_controller(rebooter, registry_logger,
+                                                 chrome_prompt_ipc);
+  chrome_cleaner::SandboxConnectionErrorCallback connection_error_callback =
+      main_controller.GetSandboxConnectionErrorCallback();
+
+  // Initialize a null UniqueParserPtr to be set by SpawnParserSandbox.
+  chrome_cleaner::UniqueParserPtr parser_ptr(
+      nullptr, base::OnTaskRunnerDeleter(nullptr));
+  chrome_cleaner::ResultCode init_result = chrome_cleaner::SpawnParserSandbox(
+      shutdown_sequence.mojo_task_runner, connection_error_callback,
+      &parser_ptr);
+  if (init_result != chrome_cleaner::RESULT_CODE_SUCCESS) {
+    return init_result;
+  }
+  std::unique_ptr<chrome_cleaner::SandboxedJsonParser> json_parser =
+      std::make_unique<chrome_cleaner::SandboxedJsonParser>(
+          shutdown_sequence.mojo_task_runner.get(), parser_ptr.get());
+  std::unique_ptr<chrome_cleaner::SandboxedShortcutParser> shortcut_parser =
+      std::make_unique<chrome_cleaner::SandboxedShortcutParser>(
+          shutdown_sequence.mojo_task_runner.get(), parser_ptr.get());
+
+  chrome_cleaner::Settings* settings = chrome_cleaner::Settings::GetInstance();
+  if (!chrome_cleaner::IsSupportedEngine(settings->engine())) {
+    LOG(FATAL) << "Unsupported engine " << settings->engine();
+    return chrome_cleaner::RESULT_CODE_FAILED;
+  }
+
+  chrome_cleaner::InitializePUPDataWithCatalog(settings->engine());
+
+  std::unique_ptr<chrome_cleaner::InterfaceLogService> interface_log_service =
+      chrome_cleaner::InterfaceLogService::Create(
+          command_line->GetSwitchValueNative(
+              chrome_cleaner::kLogInterfaceCallsToSwitch),
+          CHROME_CLEANER_VERSION_STRING);
+
+  chrome_cleaner::ResultCode engine_result;
+  std::tie(engine_result, shutdown_sequence.engine_client) = SpawnEngineSandbox(
+      settings->engine(), registry_logger, shutdown_sequence.mojo_task_runner,
+      connection_error_callback, std::move(interface_log_service));
+  if (engine_result != chrome_cleaner::RESULT_CODE_SUCCESS)
+    return engine_result;
+
+  shutdown_sequence
+      .engine_facade = std::make_unique<chrome_cleaner::EngineFacade>(
+      shutdown_sequence.engine_client, json_parser.get(),
+      main_controller.main_dialog(),
+      std::make_unique<chrome_cleaner::ForceInstalledExtensionScannerImpl>(),
+      chrome_prompt_ipc);
+
+  if (settings->execution_mode() == ExecutionMode::kScanning) {
+    shutdown_sequence.engine_facade =
+        std::make_unique<chrome_cleaner::ElevatingFacade>(
+            std::move(shutdown_sequence.engine_facade));
+  }
+  main_controller.SetEngineFacade(shutdown_sequence.engine_facade.get());
+
+  // Ensure profile reset, recovery and other components run only once by
+  // running them only in the cleaning mode.
+  if (settings->execution_mode() != ExecutionMode::kScanning) {
+    AddComponents(&main_controller, command_line, json_parser.get(),
+                  shortcut_parser.get());
+  }
+
+  command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(chrome_cleaner::kLoadEmptyDLLSwitch)) {
+    chrome_cleaner::testing::LoadEmptyDLL();
+  }
+  chrome_cleaner::NotifyInitializationDoneForTesting();
+
+  if (chrome_cleaner::Rebooter::IsPostReboot()) {
+    // When running post reboot, confirm whether the job was done successfully
+    // or not.
+    return main_controller.ValidateCleanup();
+  }
+
+  return main_controller.ScanAndClean();
+}
+
+// Return false when a self delete should NOT not be attempted.
+bool CanSelfDelete(chrome_cleaner::ResultCode exit_code) {
+  if (exit_code == chrome_cleaner::RESULT_CODE_PENDING_REBOOT ||
+      exit_code == chrome_cleaner::RESULT_CODE_CANCELED ||
+      exit_code == chrome_cleaner::RESULT_CODE_CLEANUP_PROMPT_DENIED) {
+    return false;
+  }
+  std::unique_ptr<chrome_cleaner::TaskScheduler> task_scheduler(
+      chrome_cleaner::TaskScheduler::CreateInstance());
+  return !task_scheduler->IsTaskRegistered(
+      chrome_cleaner::PendingLogsService::LogsUploadRetryTaskName(
+          PRODUCT_SHORTNAME_STRING)
+          .c_str());
+}
+
+chrome_cleaner::ResultCode ReturnWithResultCode(
+    chrome_cleaner::ResultCode result_code,
+    const base::FilePath& exe_path,
+    chrome_cleaner::RegistryLogger* registry_logger,
+    chrome_cleaner::RebooterAPI* rebooter) {
+  DCHECK_NE(chrome_cleaner::RESULT_CODE_INVALID, result_code);
+
+  registry_logger->WriteExitCode(result_code);
+  registry_logger->WriteEndTime();
+
+  bool self_delete = CanSelfDelete(result_code);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+#if defined(CHROME_CLEANER_OFFICIAL_BUILD)
+  self_delete = self_delete &&
+                !command_line->HasSwitch(chrome_cleaner::kNoSelfDeleteSwitch);
+#else
+  self_delete = self_delete &&
+                command_line->HasSwitch(chrome_cleaner::kForceSelfDeleteSwitch);
+#endif
+
+  if (self_delete) {
+    LOG(INFO) << "Self-deleting.";
+
+    if (!chrome_cleaner::DeleteFileFromTempProcess(exe_path, kSelfDeleteDelayMs,
+                                                   nullptr)) {
+      PLOG(ERROR) << "Failed to self-DeleteFileFromTempProcess.";
+    }
+
+    // Embedded libraries may have been extracted. Try to delete them and ignore
+    // errors.
+    base::FilePath exe_dir = exe_path.DirName();
+    std::set<base::string16> embedded_libraries =
+        chrome_cleaner::GetLibrariesToLoad(
+            chrome_cleaner::Settings::GetInstance()->engine());
+    for (const auto& library : embedded_libraries) {
+      chrome_cleaner::DeleteFileFromTempProcess(exe_dir.Append(library),
+                                                kSelfDeleteDelayMs, nullptr);
+    }
+  }
+
+  if (result_code == chrome_cleaner::RESULT_CODE_SUCCESS ||
+      result_code == chrome_cleaner::RESULT_CODE_POST_REBOOT_SUCCESS) {
+    registry_logger->RecordCompletedCleanup();
+  }
+
+  LOG(INFO) << "Exiting with code: " << result_code;
+  chrome_cleaner::TaskScheduler::Terminate();
+
+  return result_code;
+}
+
+}  // namespace
+
+int APIENTRY wWinMain(HINSTANCE, HINSTANCE, wchar_t*, int) {
+  // This must be executed as soon as possible to reduce the number of dlls that
+  // the code might try to load before we can lock things down.
+  chrome_cleaner::EnableSecureDllLoading();
+
+  base::AtExitManager at_exit;
+
+  // This must be done BEFORE constructing ScopedLogging, which call InitLogging
+  // to set the name of the log file, which needs to read from the command line.
+  bool success = base::CommandLine::Init(0, nullptr);
+  DCHECK(success);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+  // The list of post-reboot switches has grown so long that RunOnce no longer
+  // works, as there is a limit of 260 characters on RunOnce values. Switches
+  // are stored in a separate registry key.
+  if (command_line->HasSwitch(
+          chrome_cleaner::kPostRebootSwitchesInOtherRegistryKeySwitch)) {
+    base::CommandLine tmp_cmd(command_line->GetProgram());
+    chrome_cleaner::PostRebootRegistration post_reboot(
+        PRODUCT_SHORTNAME_STRING);
+    if (!post_reboot.ReadRunOncePostRebootCommandLine(
+            command_line->GetSwitchValueASCII(chrome_cleaner::kCleanupIdSwitch),
+            &tmp_cmd)) {
+      LOG(DFATAL)
+          << "Could not read post-reboot switches from the registry key";
+      // This shouldn't be an attack vector by UwS: the TaskScheduler should
+      // run the cleaner if the RunOnce attempt failed.
+      return chrome_cleaner::RESULT_CODE_FAILED;
+    }
+
+    // Overwrites the current process' command line. Future calls to
+    // base::CommandLine::ForCurrentProcess will return the mutated |tmp_cmd|
+    // instead of the original command line.
+    // Note that this mutation is not thread safe.
+    *command_line = tmp_cmd;
+  }
+
+  chrome_cleaner::ShutdownSequence shutdown_sequence;
+
+  chrome_cleaner::RegistryLogger registry_logger(
+      chrome_cleaner::RegistryLogger::Mode::REMOVER);
+  if (!chrome_cleaner::InitializeOSUtils()) {
+    return ReturnWithResultCode(
+        chrome_cleaner::RESULT_CODE_INITIALIZATION_ERROR, base::FilePath(),
+        &registry_logger, nullptr);
+  }
+
+  const char* crash_reporter_switch = chrome_cleaner::kCrashHandlerSwitch;
+  if (command_line->HasSwitch(crash_reporter_switch) &&
+      !command_line->HasSwitch(chrome_cleaner::kUploadLogFileSwitch)) {
+    // If this process should run as the crash reporter, run that then return
+    // immediately, as this process is not meant to be the cleaner itself.
+    return CrashReporterMain();
+  }
+
+  // GetTargetServices() returns non-null if this is the sandbox target, and
+  // null otherwise.
+  sandbox::TargetServices* sandbox_target_services =
+      sandbox::SandboxFactory::GetTargetServices();
+  const bool is_sandbox_target = (sandbox_target_services != nullptr);
+
+  base::string16 log_suffix =
+      command_line->HasSwitch(chrome_cleaner::kElevatedSwitch)
+          ? kElevatedLogFileSuffix
+          : L"";
+  log_suffix += is_sandbox_target ? chrome_cleaner::kSandboxLogFileSuffix : L"";
+
+  // This has to be created after CrashReporterMain() above, as
+  // CrashReporterMain creates its own ScopedLogging to upload logs.
+
+  chrome_cleaner::ScopedLogging scoped_logging(log_suffix);
+
+  // Only start the crash reporter for the main process, the sandboxed process
+  // will use the same crash reporter.
+  if (is_sandbox_target) {
+    const base::string16 ipc_pipe_name = command_line->GetSwitchValueNative(
+        chrome_cleaner::kUseCrashHandlerWithIdSwitch);
+    CHECK(!ipc_pipe_name.empty());
+    UseCrashReporter(ipc_pipe_name);
+  } else {
+    StartCrashReporter(CHROME_CLEANER_VERSION_UTF8_STRING);
+  }
+
+  const chrome_cleaner::Settings* settings =
+      chrome_cleaner::Settings::GetInstance();
+
+  // Process priority modification has to be done before threads are created
+  // because they inherit process' priority.
+  if (settings->execution_mode() == ExecutionMode::kScanning) {
+    chrome_cleaner::SetBackgroundMode();
+  } else {
+    if (!SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS))
+      PLOG(ERROR) << "Can't SetPriorityClass to NORMAL_PRIORITY_CLASS";
+  }
+
+  base::TaskScheduler::CreateAndStartWithDefaultParams("chrome cleanup tool");
+
+  chrome_cleaner::SandboxType sandbox_type =
+      is_sandbox_target ? chrome_cleaner::SandboxProcessType()
+                        : chrome_cleaner::SandboxType::kNonSandboxed;
+
+  if (!chrome_cleaner::CrashClient::GetInstance()->InitializeCrashReporting(
+          chrome_cleaner::CrashClient::Mode::CLEANER, sandbox_type)) {
+    LOG(INFO) << "Crash reporting is not available.";
+  } else {
+    VLOG(1) << "Crash reporting initialized.";
+  }
+
+  if (is_sandbox_target) {
+    switch (sandbox_type) {
+      case chrome_cleaner::SandboxType::kParser:
+        return chrome_cleaner::RunParserSandboxTarget(*command_line,
+                                                      sandbox_target_services);
+      case chrome_cleaner::SandboxType::kZipArchiver:
+        return chrome_cleaner::RunZipArchiverSandboxTarget(
+            *command_line, sandbox_target_services);
+      case chrome_cleaner::SandboxType::kEngine:
+        return RunEngineSandboxTarget(
+            chrome_cleaner::CreateEngineDelegate(settings->engine()),
+            *command_line, sandbox_target_services);
+      default:
+        NOTREACHED() << "Unknown sandbox type "
+                     << static_cast<int>(sandbox_type);
+    }
+  }
+
+  // Make sure we don't run two instances of the cleaner simultaneously
+  // post-reboot.
+  if (chrome_cleaner::Rebooter::IsPostReboot() &&
+      chrome_cleaner::HasAdminRights()) {
+    // The system closes the handle automatically when the process terminates,
+    // and the event object is destroyed when its last handle has been closed.
+    HANDLE event = ::CreateEvent(nullptr, FALSE, FALSE, L"chrome_cleanup_tool");
+    if (event && ::GetLastError() == ERROR_ALREADY_EXISTS)
+      return chrome_cleaner::RESULT_CODE_ALREADY_RUNNING;
+  }
+
+  // Setup Cleaner registry values.
+  registry_logger.ClearExitCode();
+  registry_logger.ClearEndTime();
+  registry_logger.WriteVersion();
+  registry_logger.WriteStartTime();
+
+  // CoInitialize into the MTA since we desire to use the System Restore Point
+  // API which requires we be in the MTA. Also needed for the task scheduler.
+  base::win::ScopedCOMInitializer scoped_com_initializer(
+      base::win::ScopedCOMInitializer::kMTA);
+  bool succeeded = chrome_cleaner::InitializeCOMSecurity();
+  PLOG_IF(ERROR, !succeeded) << "InitializeCOMSecurity() failed";
+  DCHECK(succeeded);
+  succeeded = chrome_cleaner::TaskScheduler::Initialize();
+  LOG_IF(ERROR, !succeeded) << "TaskScheduler::Initialize() failed";
+  DCHECK(succeeded);
+
+  LOG(INFO) << "Command line arguments: "
+            << chrome_cleaner::SanitizeCommandLine(*command_line);
+
+  // Make sure that users won't be bothered again to confirm they want to run
+  // the cleaner, especially post-reboot.
+  base::FilePath executable_path =
+      chrome_cleaner::PreFetchedPaths::GetInstance()->GetExecutablePath();
+  if (chrome_cleaner::HasZoneIdentifier(executable_path) &&
+      !chrome_cleaner::OverwriteZoneIdentifier(executable_path)) {
+    LOG(ERROR) << "Failed to remove zone identifier.";
+  }
+
+  // Many pieces of code below need a message loop to have been instantiated
+  // before them.
+  base::MessageLoopForUI ui_message_loop;
+
+  // The rebooter must be at the outermost scope so it can be called to reboot
+  // before exiting, when appropriate.
+  std::unique_ptr<chrome_cleaner::RebooterAPI> rebooter;
+
+  if (command_line->HasSwitch(chrome_cleaner::kUploadLogFileSwitch)) {
+    // Bail out of logs upload if upload is disabled.
+    if (!settings->logs_upload_allowed()) {
+      // Also get rid of all pending logs upload. Use a set to stop if we see
+      // the same file name twice and make sure we don't go through some sort
+      // of circular loop. Otherwise, we could spin forever if
+      // GetNextLogFilePath returns the same file and never gets to return an
+      // empty one. This might leave some log file behind, in very rare error
+      // cases, but it's better than an infinite loop.
+      std::set<base::string16> log_files;
+      while (true) {
+        base::FilePath log_file;
+        registry_logger.GetNextLogFilePath(&log_file);
+        if (log_file.empty() || !log_files.insert(log_file.value()).second)
+          break;
+        chrome_cleaner::PendingLogsService::ClearPendingLogFile(
+            PRODUCT_SHORTNAME_STRING, log_file, &registry_logger);
+      }
+
+      return ReturnWithResultCode(
+          chrome_cleaner::RESULT_CODE_EMPTY_CLIENT_ID_UPLOAD_ATTEMPT,
+          executable_path, &registry_logger, nullptr);
+    }
+
+    succeeded = false;
+    chrome_cleaner::PendingLogsService pending_logs_service;
+    base::RunLoop run_loop;
+    pending_logs_service.RetryNextPendingLogsUpload(
+        PRODUCT_SHORTNAME_STRING,
+        base::BindOnce(&LogsUploadCallback, &succeeded,
+                       run_loop.QuitWhenIdleClosure()),
+        &registry_logger);
+    run_loop.Run();
+    registry_logger.AppendLogUploadResult(succeeded);
+
+    return ReturnWithResultCode(
+        succeeded ? chrome_cleaner::RESULT_CODE_UPLOADED_PENDING_LOGS
+                  : chrome_cleaner::RESULT_CODE_FAILED_TO_UPLOAD_LOGS,
+        executable_path, &registry_logger, nullptr);
+  }
+
+  rebooter.reset(new chrome_cleaner::Rebooter(PRODUCT_SHORTNAME_STRING));
+
+  shutdown_sequence.mojo_task_runner = chrome_cleaner::MojoTaskRunner::Create();
+
+  // Only create the IPC if both the Mojo pipe token and the parent pipe handle
+  // have been sent by Chrome. If either switch is not present, it will not be
+  // connected to the parent process.
+  chrome_cleaner::ChromePromptIPC* chrome_prompt_ipc = nullptr;
+  if (settings->execution_mode() == ExecutionMode::kScanning) {
+    // Scanning mode is only used by Chrome and all necessary mojo pipe flags
+    // must have been passed on the command line.
+    if (settings->chrome_mojo_pipe_token().empty() ||
+        !settings->has_parent_pipe_handle()) {
+      return ReturnWithResultCode(
+          chrome_cleaner::RESULT_CODE_INVALID_IPC_SWITCHES, executable_path,
+          &registry_logger, rebooter.get());
+    }
+
+    const std::string chrome_mojo_pipe_token =
+        settings->chrome_mojo_pipe_token();
+    // This pointer is leaked, in order to simplify this object's lifetime.
+    chrome_prompt_ipc = new chrome_cleaner::ChromePromptIPC(
+        chrome_mojo_pipe_token, shutdown_sequence.mojo_task_runner);
+  } else if (!settings->chrome_mojo_pipe_token().empty() ||
+             settings->has_parent_pipe_handle()) {
+    return ReturnWithResultCode(
+        chrome_cleaner::RESULT_CODE_EXPECTED_SCANNING_EXECUTION_MODE,
+        executable_path, &registry_logger, rebooter.get());
+  }
+
+  if (settings->execution_mode() == ExecutionMode::kNone) {
+    ::MessageBox(NULL,
+                 L"Manually running this program is no longer supported. "
+                 L"Please visit "
+                 L"https://support.google.com/chrome/?p=chrome_cleanup_tool "
+                 L"for more information.",
+                 L"Error", MB_OK | MB_ICONERROR | MB_TOPMOST);
+    return chrome_cleaner::RESULT_CODE_MANUAL_EXECUTION_BY_USER;
+  }
+
+  // If immediate elevation is not required, the process will restart elevated
+  // after the user accepts to run cleanup.
+  if (settings->execution_mode() != ExecutionMode::kScanning &&
+      !chrome_cleaner::HasAdminRights()) {
+    chrome_cleaner::ResultCode result_code = RelaunchElevated(&registry_logger);
+    // If we failed to launch the elevated process, this process should call
+    // ReturnWithResultCode to ensure all the properly post-run registry values
+    // are written, and that all required cleanup is executed. If the elevated
+    // process was created it will handle this.
+    if (result_code != chrome_cleaner::RESULT_CODE_SUCCESS) {
+      return ReturnWithResultCode(result_code, executable_path,
+                                  &registry_logger, rebooter.get());
+    }
+    return result_code;
+  }
+
+  return ReturnWithResultCode(
+      RunChromeCleaner(command_line, rebooter.get(), &registry_logger,
+                       chrome_prompt_ipc, std::move(shutdown_sequence)),
+      executable_path, &registry_logger, rebooter.get());
+}
diff --git a/chrome/chrome_cleaner/executables/chrome_reporter_main.cc b/chrome/chrome_cleaner/executables/chrome_reporter_main.cc
new file mode 100644
index 0000000..4c189e0
--- /dev/null
+++ b/chrome/chrome_cleaner/executables/chrome_reporter_main.cc
@@ -0,0 +1,317 @@
+// 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 <windows.h>
+
+#include <psapi.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/at_exit.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_util.h"
+#include "base/task/task_scheduler/task_scheduler.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/win/registry.h"
+#include "base/win/scoped_com_initializer.h"
+#include "base/win/windows_version.h"
+#include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
+#include "chrome/chrome_cleaner/constants/software_reporter_tool_branding.h"
+#include "chrome/chrome_cleaner/constants/version.h"
+#include "chrome/chrome_cleaner/crash/crash_client.h"
+#include "chrome/chrome_cleaner/crash/crash_reporter.h"
+#include "chrome/chrome_cleaner/engines/broker/engine_client.h"
+#include "chrome/chrome_cleaner/engines/broker/interface_log_service.h"
+#include "chrome/chrome_cleaner/engines/broker/sandbox_setup.h"
+#include "chrome/chrome_cleaner/engines/common/engine_resources.h"
+#include "chrome/chrome_cleaner/engines/controllers/scanner_controller_impl.h"
+#include "chrome/chrome_cleaner/engines/target/engine_delegate.h"
+#include "chrome/chrome_cleaner/engines/target/engine_delegate_factory.h"
+#include "chrome/chrome_cleaner/engines/target/sandbox_setup.h"
+#include "chrome/chrome_cleaner/executables/shutdown_sequence.h"
+#include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+#include "chrome/chrome_cleaner/ipc/sandbox.h"
+#include "chrome/chrome_cleaner/logging/logging_service_api.h"
+#include "chrome/chrome_cleaner/logging/registry_logger.h"
+#include "chrome/chrome_cleaner/logging/scoped_logging.h"
+#include "chrome/chrome_cleaner/os/disk_util.h"
+#include "chrome/chrome_cleaner/os/early_exit.h"
+#include "chrome/chrome_cleaner/os/file_path_sanitization.h"
+#include "chrome/chrome_cleaner/os/initializer.h"
+#include "chrome/chrome_cleaner/os/secure_dll_loading.h"
+#include "chrome/chrome_cleaner/os/system_util.h"
+#include "chrome/chrome_cleaner/os/task_scheduler.h"
+#include "chrome/chrome_cleaner/parsers/broker/sandbox_setup_hooks.h"
+#include "chrome/chrome_cleaner/parsers/shortcut_parser/broker/sandboxed_shortcut_parser.h"
+#include "chrome/chrome_cleaner/parsers/shortcut_parser/broker/shortcut_parser_api.h"
+#include "chrome/chrome_cleaner/parsers/target/sandbox_setup.h"
+#include "chrome/chrome_cleaner/scanner/scanner_controller.h"
+#include "chrome/chrome_cleaner/settings/default_matching_options.h"
+#include "chrome/chrome_cleaner/settings/engine_settings.h"
+#include "chrome/chrome_cleaner/settings/matching_options.h"
+#include "chrome/chrome_cleaner/settings/settings.h"
+#include "chrome/chrome_cleaner/settings/settings_types.h"
+#include "components/chrome_cleaner/public/constants/result_codes.h"
+#include "sandbox/win/src/sandbox_factory.h"
+
+using chrome_cleaner::MojoTaskRunner;
+
+namespace {
+
+void WriteExitMetrics(chrome_cleaner::ResultCode result_code,
+                      chrome_cleaner::RegistryLogger* registry_logger) {
+  registry_logger->WriteExitCode(result_code);
+  registry_logger->WriteEndTime();
+
+  PROCESS_MEMORY_COUNTERS pmc;
+  // TODO(joenotcharles): Log the total memory consumption instead of just the
+  // main process'.
+  if (::GetProcessMemoryInfo(::GetCurrentProcess(), &pmc, sizeof(pmc))) {
+    registry_logger->WriteMemoryUsage(pmc.PeakWorkingSetSize / 1024);
+  }
+}
+
+chrome_cleaner::ResultCode FinalizeWithResultCode(
+    chrome_cleaner::ResultCode result_code,
+    chrome_cleaner::RegistryLogger* registry_logger) {
+  chrome_cleaner::TaskScheduler::Terminate();
+  LOG(INFO) << "Exiting with code: " << result_code;
+
+  WriteExitMetrics(result_code, registry_logger);
+  return result_code;
+}
+
+void TerminateOnSandboxConnectionError(
+    const base::WeakPtr<chrome_cleaner::RegistryLogger>& registry_logger,
+    const chrome_cleaner::SandboxType sandbox_type) {
+  // If |registry_logger| has been deleted, the process is dying anyway, so no
+  // action is needed.
+  if (!registry_logger)
+    return;
+
+  chrome_cleaner::ResultCode result_code =
+      chrome_cleaner::GetResultCodeForSandboxConnectionError(sandbox_type);
+  WriteExitMetrics(result_code, registry_logger.get());
+  chrome_cleaner::EarlyExit(result_code);
+}
+
+void CallTerminateOnSandboxConnectionError(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    const base::WeakPtr<chrome_cleaner::RegistryLogger>& registry_logger,
+    const chrome_cleaner::SandboxType sandbox_type) {
+  // Weakptr has to be dereferenced on its factory thread.
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(TerminateOnSandboxConnectionError,
+                                       registry_logger, sandbox_type));
+}
+
+}  // namespace
+
+int APIENTRY wWinMain(HINSTANCE, HINSTANCE, wchar_t*, int) {
+  // This must be executed as soon as possible to reduce the number of dlls that
+  // the code might try to load before we can lock things down.
+  chrome_cleaner::EnableSecureDllLoading();
+
+  base::AtExitManager at_exit;
+
+  // This must be done BEFORE constructing ScopedLogging, which calls
+  // InitLogging to set the name of the log file, which needs to read
+  // from the command line.
+  bool success = base::CommandLine::Init(0, nullptr);
+  DCHECK(success);
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+
+  // If this process should run as the crash reporter, run that then return
+  // immediately, as this process is not meant to be the reporter itself.
+  if (command_line->HasSwitch(chrome_cleaner::kCrashHandlerSwitch))
+    return CrashReporterMain();
+
+  // GetTargetServices() returns non-null if this is the sandbox target, and
+  // null otherwise.
+  sandbox::TargetServices* sandbox_target_services =
+      sandbox::SandboxFactory::GetTargetServices();
+  const bool is_sandbox_target = (sandbox_target_services != nullptr);
+  chrome_cleaner::ScopedLogging scoped_logging(
+      is_sandbox_target ? chrome_cleaner::kSandboxLogFileSuffix : nullptr);
+
+  // If there is a command line argument to add a registry suffix, set
+  // the value for the registry_logger.
+  const std::string registry_suffix =
+      command_line->GetSwitchValueASCII(chrome_cleaner::kRegistrySuffixSwitch);
+
+  chrome_cleaner::RegistryLogger registry_logger(
+      chrome_cleaner::RegistryLogger::Mode::REPORTER, registry_suffix);
+
+  // Weak pointer for connection error callback which may outlive the main. It
+  // will be destroyed at the end of main before registry_logger.
+  base::WeakPtrFactory<chrome_cleaner::RegistryLogger>
+      registry_logger_weak_factory(&registry_logger);
+
+  chrome_cleaner::ShutdownSequence shutdown_sequence;
+
+  if (!chrome_cleaner::InitializeOSUtils()) {
+    return FinalizeWithResultCode(
+        chrome_cleaner::RESULT_CODE_INITIALIZATION_ERROR, &registry_logger);
+  }
+
+  // Start the crash handler only for the reporter process. The sandbox process
+  // will use the handler process that was started by the reporter.
+  if (command_line->HasSwitch(chrome_cleaner::kUseCrashHandlerWithIdSwitch)) {
+    DCHECK(is_sandbox_target);
+    const base::string16 ipc_pipe_name = command_line->GetSwitchValueNative(
+        chrome_cleaner::kUseCrashHandlerWithIdSwitch);
+    CHECK(!ipc_pipe_name.empty());
+    UseCrashReporter(ipc_pipe_name);
+  } else if (!is_sandbox_target) {
+    // Start the crash reporter only if this is not the sandbox target. This is
+    // the case for tests, where the |kUseCrashHandlerWithIdSwitch| switch is
+    // not passed (and we don't want a crash reporter process to be started).
+    StartCrashReporter(CHROME_CLEANER_VERSION_UTF8_STRING);
+  }
+
+  LOG(INFO) << "Command line arguments: "
+            << chrome_cleaner::SanitizeCommandLine(*command_line);
+
+  const chrome_cleaner::CrashClient::Mode crash_client_mode =
+      chrome_cleaner::CrashClient::Mode::REPORTER;
+
+  chrome_cleaner::SandboxType sandbox_type =
+      is_sandbox_target ? chrome_cleaner::SandboxProcessType()
+                        : chrome_cleaner::SandboxType::kNonSandboxed;
+
+  if (!chrome_cleaner::CrashClient::GetInstance()->InitializeCrashReporting(
+          crash_client_mode, sandbox_type)) {
+    LOG(INFO) << "Crash reporting is not available.";
+  } else {
+    LOG(INFO) << "Crash reporting initialized.";
+  }
+
+  // Make sure not to take too much of the machines's resources.
+  chrome_cleaner::SetBackgroundMode();
+
+  const chrome_cleaner::Settings* settings =
+      chrome_cleaner::Settings::GetInstance();
+
+  if (is_sandbox_target) {
+    switch (sandbox_type) {
+      case chrome_cleaner::SandboxType::kEngine:
+        return RunEngineSandboxTarget(
+            chrome_cleaner::CreateEngineDelegate(settings->engine()),
+            *command_line, sandbox_target_services);
+      case chrome_cleaner::SandboxType::kParser:
+        return chrome_cleaner::RunParserSandboxTarget(*command_line,
+                                                      sandbox_target_services);
+      default:
+        NOTREACHED() << "Unknown sandbox type "
+                     << static_cast<int>(sandbox_type);
+    }
+  }
+
+  registry_logger.ClearScanTimes();
+  registry_logger.ClearExitCode();
+  registry_logger.WriteStartTime();
+
+  if (!settings->scan_switches_correct()) {
+    return FinalizeWithResultCode(
+        chrome_cleaner::RESULT_CODE_INVALID_SCANNING_SWITCHES,
+        &registry_logger);
+  }
+
+  // Many pieces of code below need a message loop to have been instantiated
+  // before them.
+  base::TaskScheduler::CreateAndStartWithDefaultParams("software reporter");
+  base::MessageLoopForUI ui_message_loop;
+
+  shutdown_sequence.mojo_task_runner = MojoTaskRunner::Create();
+
+  if (!chrome_cleaner::IsSupportedEngine(settings->engine())) {
+    LOG(FATAL) << "Unsupported engine " << settings->engine();
+    return FinalizeWithResultCode(chrome_cleaner::RESULT_CODE_FAILED,
+                                  &registry_logger);
+  }
+
+  chrome_cleaner::InitializePUPDataWithCatalog(settings->engine());
+
+  base::string16 interface_log_file;
+  if (command_line->HasSwitch(chrome_cleaner::kLogInterfaceCallsToSwitch)) {
+    interface_log_file = command_line->GetSwitchValueNative(
+        chrome_cleaner::kLogInterfaceCallsToSwitch);
+    base::FilePath passed_name(interface_log_file);
+    std::vector<base::string16> components;
+    passed_name.GetComponents(&components);
+    if (components.size() != 1) {
+      LOG(ERROR) << "Invalid file name passed for logging!";
+      return FinalizeWithResultCode(chrome_cleaner::RESULT_CODE_FAILED,
+                                    &registry_logger);
+    }
+  }
+
+  auto sandbox_connection_error_callback =
+      base::BindRepeating(CallTerminateOnSandboxConnectionError,
+                          base::SequencedTaskRunnerHandle::Get(),
+                          registry_logger_weak_factory.GetWeakPtr());
+
+  std::unique_ptr<chrome_cleaner::InterfaceLogService> interface_log_service =
+      chrome_cleaner::InterfaceLogService::Create(
+          interface_log_file, CHROME_CLEANER_VERSION_STRING);
+
+  chrome_cleaner::ResultCode engine_result_code;
+  std::tie(engine_result_code, shutdown_sequence.engine_client) =
+      chrome_cleaner::SpawnEngineSandbox(settings->engine(), &registry_logger,
+                                         shutdown_sequence.mojo_task_runner,
+                                         sandbox_connection_error_callback,
+                                         std::move(interface_log_service));
+  if (engine_result_code != chrome_cleaner::RESULT_CODE_SUCCESS)
+    return FinalizeWithResultCode(engine_result_code, &registry_logger);
+
+  // CoInitialize into the MTA since we desire to use the task scheduler.
+  base::win::ScopedCOMInitializer scoped_com_initializer(
+      base::win::ScopedCOMInitializer::kMTA);
+  bool succeeded = chrome_cleaner::TaskScheduler::Initialize();
+  DCHECK(succeeded) << "TaskScheduler::Initialize() failed";
+
+  // Initialize the sandbox for the shortcut parser.
+  chrome_cleaner::UniqueParserPtr parser_ptr(
+      nullptr, base::OnTaskRunnerDeleter(nullptr));
+  chrome_cleaner::ResultCode parser_result_code =
+      chrome_cleaner::SpawnParserSandbox(
+          shutdown_sequence.mojo_task_runner.get(),
+          sandbox_connection_error_callback, &parser_ptr);
+  if (parser_result_code != chrome_cleaner::RESULT_CODE_SUCCESS)
+    return FinalizeWithResultCode(parser_result_code, &registry_logger);
+  std::unique_ptr<chrome_cleaner::ShortcutParserAPI> shortcut_parser =
+      std::make_unique<chrome_cleaner::SandboxedShortcutParser>(
+          shutdown_sequence.mojo_task_runner.get(), parser_ptr.get());
+
+  std::unique_ptr<chrome_cleaner::ScannerController> scanner_controller =
+      std::make_unique<chrome_cleaner::ScannerControllerImpl>(
+          shutdown_sequence.engine_client.get(), &registry_logger,
+          base::SequencedTaskRunnerHandle::Get(), shortcut_parser.get());
+
+  if (command_line->HasSwitch(chrome_cleaner::kCrashSwitch)) {
+    int* crash_me = nullptr;
+    *crash_me = 42;
+  }
+
+  if (command_line->HasSwitch(chrome_cleaner::kLoadEmptyDLLSwitch)) {
+    chrome_cleaner::testing::LoadEmptyDLL();
+  }
+  chrome_cleaner::NotifyInitializationDoneForTesting();
+
+  auto result_code =
+      static_cast<chrome_cleaner::ResultCode>(scanner_controller->ScanOnly());
+  return FinalizeWithResultCode(result_code, &registry_logger);
+}
diff --git a/chrome/chrome_cleaner/executables/shutdown_sequence.cc b/chrome/chrome_cleaner/executables/shutdown_sequence.cc
new file mode 100644
index 0000000..5d50ca29
--- /dev/null
+++ b/chrome/chrome_cleaner/executables/shutdown_sequence.cc
@@ -0,0 +1,36 @@
+// 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/chrome_cleaner/executables/shutdown_sequence.h"
+#include "base/task/task_scheduler/task_scheduler.h"
+
+#include "base/task/task_scheduler/task_scheduler.h"
+
+namespace chrome_cleaner {
+
+ShutdownSequence::ShutdownSequence() = default;
+
+ShutdownSequence::ShutdownSequence(ShutdownSequence&& other)
+    : engine_client(std::move(other.engine_client)),
+      mojo_task_runner(std::move(other.mojo_task_runner)),
+      engine_facade(std::move(other.engine_facade)) {}
+
+ShutdownSequence::~ShutdownSequence() {
+  if (!mojo_task_runner)
+    return;
+
+  auto* task_scheduler = base::TaskScheduler::GetInstance();
+  if (task_scheduler)
+    task_scheduler->Shutdown();
+
+  // Objects that post messages to themselves with base::Unretained must be
+  // destroyed after TaskScheduler::Shutdown, otherwise some tasks might be
+  // still referencing recently destroyed objects.
+
+  engine_facade.reset();
+  engine_client.reset();
+  mojo_task_runner.reset();
+}
+
+}  // namespace chrome_cleaner
diff --git a/chrome/chrome_cleaner/executables/shutdown_sequence.h b/chrome/chrome_cleaner/executables/shutdown_sequence.h
new file mode 100644
index 0000000..abd909d
--- /dev/null
+++ b/chrome/chrome_cleaner/executables/shutdown_sequence.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_CHROME_CLEANER_EXECUTABLES_SHUTDOWN_SEQUENCE_H_
+#define CHROME_CHROME_CLEANER_EXECUTABLES_SHUTDOWN_SEQUENCE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "chrome/chrome_cleaner/engines/broker/engine_client.h"
+#include "chrome/chrome_cleaner/engines/controllers/engine_facade_interface.h"
+#include "chrome/chrome_cleaner/ipc/mojo_task_runner.h"
+
+namespace chrome_cleaner {
+
+// Helper to ensure correct order of destruction of mojo-related objects during
+// the shutdown sequence. For it to work correctly, there must not be any other
+// references to the object's fields.
+struct ShutdownSequence {
+  ShutdownSequence();
+  ShutdownSequence(ShutdownSequence&& other);
+  ~ShutdownSequence();
+
+  scoped_refptr<EngineClient> engine_client;
+  scoped_refptr<MojoTaskRunner> mojo_task_runner;
+  std::unique_ptr<EngineFacadeInterface> engine_facade;
+};
+
+}  // namespace chrome_cleaner
+
+#endif  // CHROME_CHROME_CLEANER_EXECUTABLES_SHUTDOWN_SEQUENCE_H_
diff --git a/chrome/chrome_cleaner/logging/BUILD.gn b/chrome/chrome_cleaner/logging/BUILD.gn
index 454278f0..d9048ba 100644
--- a/chrome/chrome_cleaner/logging/BUILD.gn
+++ b/chrome/chrome_cleaner/logging/BUILD.gn
@@ -69,6 +69,46 @@
   ]
 }
 
+# Logging definitions that must only by linked into the cleaner executable.
+# This is separate from cleaner_logging, which is also linked with unit tests.
+source_set("cleaner_logging_definitions") {
+  sources = [
+    "cleaner_logging_definitions.cc",
+  ]
+
+  deps = [
+    ":cleaner_logging",
+    ":logging_definitions",
+  ]
+}
+
+# Logging definitions that must only by linked into the reporter executable.
+# This is separate from reporter_logging, which is also linked with unit tests.
+source_set("reporter_logging_definitions") {
+  sources = [
+    "reporter_logging_definitions.cc",
+  ]
+
+  deps = [
+    ":logging_definitions",
+    ":noop_logging",
+    ":reporter_logging",
+    "//chrome/chrome_cleaner/settings:settings",
+  ]
+}
+
+# Logging definitions that are linked into all other executables.
+source_set("other_logging_definitions") {
+  sources = [
+    "other_logging_definitions.cc",
+  ]
+
+  deps = [
+    ":logging_definitions",
+    ":noop_logging",
+  ]
+}
+
 source_set("api_keys_header") {
   sources = [
     "api_keys.h",
@@ -110,6 +150,7 @@
   deps = [
     ":api_keys",
     ":api_keys_header",
+    ":common",
     "//base",
     "//chrome/chrome_cleaner/chrome_utils:chrome_util_lib",
     "//chrome/chrome_cleaner/constants:chrome_cleanup_tool_branding_header",
@@ -125,10 +166,6 @@
     "//chrome/chrome_cleaner/strings",
     "//components/chrome_cleaner/public/constants:constants",
   ]
-
-  public_deps = [
-    ":common",
-  ]
 }
 
 static_library("reporter_logging") {
@@ -140,6 +177,7 @@
   deps = [
     ":api_keys",
     ":api_keys_header",
+    ":common",
     "//base",
     "//chrome/chrome_cleaner/chrome_utils:chrome_util_lib",
     "//chrome/chrome_cleaner/constants:common_strings",
@@ -152,10 +190,6 @@
     "//chrome/chrome_cleaner/settings:settings",
     "//components/chrome_cleaner/public/constants:constants",
   ]
-
-  public_deps = [
-    ":common",
-  ]
 }
 
 static_library("noop_logging") {
@@ -234,6 +268,7 @@
     "//chrome/chrome_cleaner/constants:version_header",
     "//chrome/chrome_cleaner/http:http",
     "//chrome/chrome_cleaner/http:mock_http_agent_factory",
+    "//chrome/chrome_cleaner/logging:common",
     "//chrome/chrome_cleaner/logging:scoped_timed_task_logger",
     "//chrome/chrome_cleaner/logging/proto:chrome_cleaner_report_proto",
     "//chrome/chrome_cleaner/logging/proto:removal_status_proto",
diff --git a/chrome/chrome_cleaner/os/BUILD.gn b/chrome/chrome_cleaner/os/BUILD.gn
index c9073ae..c3d68acc 100644
--- a/chrome/chrome_cleaner/os/BUILD.gn
+++ b/chrome/chrome_cleaner/os/BUILD.gn
@@ -101,6 +101,7 @@
 
   deps = [
     ":common_os",
+    ":file_remover_api",
     "//base:base",
     "//chrome/chrome_cleaner/constants:common_strings",
     "//chrome/chrome_cleaner/constants:quarantine_constants",
@@ -114,7 +115,6 @@
   ]
 
   public_deps = [
-    ":file_remover_api",
     "//chrome/chrome_cleaner/logging/proto:removal_status_proto",
   ]
 }
diff --git a/chrome/chrome_cleaner/scanner/BUILD.gn b/chrome/chrome_cleaner/scanner/BUILD.gn
index ca7d40f3..5337205 100644
--- a/chrome/chrome_cleaner/scanner/BUILD.gn
+++ b/chrome/chrome_cleaner/scanner/BUILD.gn
@@ -22,14 +22,11 @@
     "//base:base",
     "//chrome/chrome_cleaner/constants:common_strings",
     "//chrome/chrome_cleaner/os:common_os",
+    "//chrome/chrome_cleaner/pup_data:pup_data_base",
     "//chrome/chrome_cleaner/scanner:signature_matcher_api",
     "//chrome/chrome_cleaner/settings:settings",
     "//chrome/chrome_cleaner/strings",
   ]
-
-  public_deps = [
-    "//chrome/chrome_cleaner/pup_data:pup_data_base",
-  ]
 }
 
 source_set("signature_matcher") {
diff --git a/chrome/chrome_cleaner/scanner/force_installed_extension_scanner_impl.cc b/chrome/chrome_cleaner/scanner/force_installed_extension_scanner_impl.cc
index 0b4aa8a9..10b20c2a 100644
--- a/chrome/chrome_cleaner/scanner/force_installed_extension_scanner_impl.cc
+++ b/chrome/chrome_cleaner/scanner/force_installed_extension_scanner_impl.cc
@@ -26,6 +26,11 @@
 std::unique_ptr<UwEMatchers>
 ForceInstalledExtensionScannerImpl::CreateUwEMatchersFromResource(
     int resource_id) {
+  if (!resource_id) {
+    // Use empty matchers.
+    LOG(WARNING) << "No UwE matchers set";
+    return std::make_unique<chrome_cleaner::UwEMatchers>();
+  }
   base::StringPiece serialized_matcher_pb;
   if (!chrome_cleaner::LoadResourceOfKind(resource_id, L"TEXT",
                                           &serialized_matcher_pb)) {
@@ -33,9 +38,9 @@
                 << resource_id;
     return nullptr;
   }
-  chrome_cleaner::UwEMatchers matcher;
-  matcher.ParseFromString(serialized_matcher_pb.as_string());
-  return std::make_unique<chrome_cleaner::UwEMatchers>(matcher);
+  auto uwe_matchers = std::make_unique<chrome_cleaner::UwEMatchers>();
+  uwe_matchers->ParseFromString(serialized_matcher_pb.as_string());
+  return uwe_matchers;
 }
 
 std::vector<ForceInstalledExtension>
diff --git a/chrome/chrome_cleaner/settings/BUILD.gn b/chrome/chrome_cleaner/settings/BUILD.gn
index 7db4c30..eb21429 100644
--- a/chrome/chrome_cleaner/settings/BUILD.gn
+++ b/chrome/chrome_cleaner/settings/BUILD.gn
@@ -56,6 +56,39 @@
   ]
 }
 
+# Settings definitions that must only by linked into the cleaner executable.
+source_set("cleaner_settings_definitions") {
+  sources = [
+    "cleaner_settings_definitions.cc",
+  ]
+
+  deps = [
+    ":settings_definitions",
+  ]
+}
+
+# Settings definitions that must only by linked into the reporter executable.
+source_set("reporter_settings_definitions") {
+  sources = [
+    "reporter_settings_definitions.cc",
+  ]
+
+  deps = [
+    ":settings_definitions",
+  ]
+}
+
+# Settings definitions that are linked into all other executables.
+source_set("other_settings_definitions") {
+  sources = [
+    "other_settings_definitions.cc",
+  ]
+
+  deps = [
+    ":settings_definitions",
+  ]
+}
+
 source_set("matching_options") {
   sources = [
     "matching_options.cc",
diff --git a/chrome/chrome_cleaner/test/BUILD.gn b/chrome/chrome_cleaner/test/BUILD.gn
index cbe5f9f..d5216c5 100644
--- a/chrome/chrome_cleaner/test/BUILD.gn
+++ b/chrome/chrome_cleaner/test/BUILD.gn
@@ -183,6 +183,7 @@
     "//chrome/chrome_cleaner/constants:common_strings",
     "//chrome/chrome_cleaner/os:cleaner_os",
     "//chrome/chrome_cleaner/os:common_os",
+    "//chrome/chrome_cleaner/os:file_remover_api",
     "//chrome/chrome_cleaner/pup_data:pup_data_base",
     "//chrome/chrome_cleaner/scanner:matcher_util",
     "//chrome/chrome_cleaner/scanner:signature_matcher_api",
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 8b269df6..43b7382 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -474,7 +474,7 @@
 // Enables usage of Parent Access Code to authorize certain actions on child
 // user device.
 const base::Feature kParentAccessCode{"ParentAccessCode",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
 #endif
 
 // Delegate permissions to cross-origin iframes when the feature has been
diff --git a/chrome/test/data/android/cct_target_blank.html b/chrome/test/data/android/cct_target_blank.html
new file mode 100644
index 0000000..e5a69eb
--- /dev/null
+++ b/chrome/test/data/android/cct_target_blank.html
@@ -0,0 +1,8 @@
+<html>
+  </head><title>CCT Target Blank Test Page</title></head>
+  <body>
+    <!-- Unlike popup_on_click.html, this leads to a different page which is vital to test
+         Chrome Custom Tab's navigate on close feature. -->
+    <a id="target_blank_link" href="google.html" target="_blank">Click Here</a>
+  </body>
+</html>
diff --git a/chrome/test/data/webrtc/peerconnection_getstats.js b/chrome/test/data/webrtc/peerconnection_getstats.js
index 68bcd3a..5805968 100644
--- a/chrome/test/data/webrtc/peerconnection_getstats.js
+++ b/chrome/test/data/webrtc/peerconnection_getstats.js
@@ -9,35 +9,160 @@
  * exposed to the web) RTCStats-derived dictionaries described below.
  * @private
  */
-var gStatsWhitelist = new Map();
+let gStatsWhitelist = new Map();
 
 /**
- * RTCRTPStreamStats
+ * RTCRtpStreamStats
  * https://w3c.github.io/webrtc-stats/#streamstats-dict*
  * @private
  */
-var kRTCRTPStreamStats = new RTCStats_(null, {
+let kRTCRtpStreamStats = new RTCStats(null, {
   ssrc: 'number',
-  associateStatsId: 'string',
-  isRemote: 'boolean',
-  mediaType: 'string',
+  isRemote: 'boolean',  // Obsolete, type reveals if "remote-" or not.
   kind: 'string',
-  trackId: 'string',
+  mediaType: 'string',  // Obsolete, replaced by |kind|.
   transportId: 'string',
   codecId: 'string',
+});
+
+/**
+ * RTCReceivedRtpStreamStats
+ * https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats
+ * @private
+ */
+let kRTCReceivedRtpStreamStats = new RTCStats(kRTCRtpStreamStats, {
+  packetsReceived: 'number',
+  packetsLost: 'number',
+  jitter: 'number',
+  packetsDiscarded: 'number',
+  packetsRepaired: 'number',
+  burstPacketsLost: 'number',
+  burstPacketsDiscarded: 'number',
+  burstLossCount: 'number',
+  burstDiscardCount: 'number',
+  burstLossRate: 'number',
+  burstDiscardRate: 'number',
+  gapLossRate: 'number',
+  gapDiscardRate: 'number',
+});
+
+/*
+ * RTCInboundRTPStreamStats
+ * https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*
+ * @private
+ */
+let kRTCInboundRtpStreamStats = new RTCStats(kRTCReceivedRtpStreamStats, {
+  trackId: 'string',
+  receiverId: 'string',
+  remoteId: 'string',
+  framesDecoded: 'number',
+  qpSum: 'number',
+  lastPacketReceivedTimestamp: 'number',
+  averageRtcpInterval: 'number',
+  fecPacketsReceived: 'number',
+  fecPacketsDiscarded: 'number',
+  bytesReceived: 'number',
+  packetsFailedDecryption: 'number',
+  packetsDuplicated: 'number',
+  perDscpPacketsReceived: 'object',
+  nackCount: 'number',
   firCount: 'number',
   pliCount: 'number',
-  nackCount: 'number',
   sliCount: 'number',
-  qpSum: 'number',
+  fractionLost: 'number',  // Obsolete, moved to RTCRemoteInboundRtpStreamStats.
 });
+gStatsWhitelist.set('inbound-rtp', kRTCInboundRtpStreamStats);
+
+/*
+ * RTCRemoteInboundRtpStreamStats
+ * https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*
+ * @private
+ */
+let kRTCRemoteInboundRtpStreamStats =
+    new RTCStats(kRTCReceivedRtpStreamStats, {
+  localId: 'string',
+  roundTripTime: 'number',
+  fractionLost: 'number',
+});
+// TODO(hbos): Add this to the whitelist when it has been implemented. Adding it
+// before then will tag it as missing.
+// gStatsWhitelist.set('remote-inbound-rtp', kRTCRemoteInboundRtpStreamStats);
+
+/**
+ * RTCSentRtpStreamStats
+ * https://w3c.github.io/webrtc-stats/#dom-rtcsentrtpstreamstats
+ * @private
+ */
+let kRTCSentRtpStreamStats = new RTCStats(kRTCRtpStreamStats, {
+  packetsSent: 'number',
+  packetsDiscardedOnSend: 'number',
+  fecPacketsSent: 'number',
+  bytesSent: 'number',
+  bytesDiscardedOnSend: 'number',
+});
+
+/*
+ * RTCOutboundRtpStreamStats
+ * https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*
+ * @private
+ */
+let kRTCOutboundRtpStreamStats = new RTCStats(kRTCSentRtpStreamStats, {
+  trackId: 'string',
+  senderId: 'string',
+  remoteId: 'string',
+  lastPacketSentTimestamp: 'number',
+  retransmittedPacketsSent: 'number',
+  retransmittedBytesSent: 'number',
+  targetBitrate: 'number',
+  totalEncodedBytesTarget: 'number',
+  framesEncoded: 'number',
+  qpSum: 'number',
+  totalEncodeTime: 'number',
+  averageRtcpInterval: 'number',
+  qualityLimitationReason: 'string',
+  qualityLimitationDurations: 'object',
+  perDscpPacketsSent: 'object',
+  nackCount: 'number',
+  firCount: 'number',
+  pliCount: 'number',
+  sliCount: 'number',
+});
+gStatsWhitelist.set('outbound-rtp', kRTCOutboundRtpStreamStats);
+
+/*
+ * RTCRemoteOutboundRtpStreamStats
+ * https://w3c.github.io/webrtc-stats/#dom-rtcremoteoutboundrtpstreamstats
+ * @private
+ */
+let kRTCRemoteOutboundRtpStreamStats = new RTCStats(kRTCSentRtpStreamStats, {
+  localId: 'string',
+  remoteTimestamp: 'number',
+});
+// TODO(hbos): Add this to the whitelist when it has been implemented. Adding it
+// before then will tag it as missing.
+// gStatsWhitelist.set('remote-outbound-rtp', kRTCRemoteOutboundRtpStreamStats);
+
+/*
+ * RTCRtpContributingSourceStats
+ * https://w3c.github.io/webrtc-stats/#dom-rtcrtpcontributingsourcestats
+ * @private
+ */
+let kRTCRtpContributingSourceStats = new RTCStats(null, {
+  contributorSsrc: 'number',
+  inboundRtpStreamId: 'string',
+  packetsContributedTo: 'number',
+  audioLevel: 'number',
+});
+// TODO(hbos): Add this to the whitelist when it has been implemented. Adding it
+// before then will tag it as missing.
+// gStatsWhitelist.set('csrc', kRTCRtpContributingSourceStats);
 
 /*
  * RTCCodecStats
  * https://w3c.github.io/webrtc-stats/#codec-dict*
  * @private
  */
-var kRTCCodecStats = new RTCStats_(null, {
+let kRTCCodecStats = new RTCStats(null, {
   payloadType: 'number',
   mimeType: 'string',
   // TODO(hbos): As soon as |codec| has been renamed |mimeType| in the webrtc
@@ -51,50 +176,11 @@
 gStatsWhitelist.set('codec', kRTCCodecStats);
 
 /*
- * RTCInboundRTPStreamStats
- * https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*
- * @private
- */
-var kRTCInboundRTPStreamStats = new RTCStats_(kRTCRTPStreamStats, {
-  packetsReceived: 'number',
-  bytesReceived: 'number',
-  packetsLost: 'number',
-  jitter: 'number',
-  fractionLost: 'number',
-  packetsDiscarded: 'number',
-  packetsRepaired: 'number',
-  burstPacketsLost: 'number',
-  burstPacketsDiscarded: 'number',
-  burstLossCount: 'number',
-  burstDiscardCount: 'number',
-  burstLossRate: 'number',
-  burstDiscardRate: 'number',
-  gapLossRate: 'number',
-  gapDiscardRate: 'number',
-  framesDecoded: 'number',
-});
-gStatsWhitelist.set('inbound-rtp', kRTCInboundRTPStreamStats);
-
-/*
- * RTCOutboundRTPStreamStats
- * https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*
- * @private
- */
-var kRTCOutboundRTPStreamStats = new RTCStats_(kRTCRTPStreamStats, {
-  packetsSent: 'number',
-  bytesSent: 'number',
-  targetBitrate: 'number',
-  roundTripTime: 'number',
-  framesEncoded: 'number',
-});
-gStatsWhitelist.set('outbound-rtp', kRTCOutboundRTPStreamStats);
-
-/*
  * RTCPeerConnectionStats
  * https://w3c.github.io/webrtc-stats/#pcstats-dict*
  * @private
  */
-var kRTCPeerConnectionStats = new RTCStats_(null, {
+let kRTCPeerConnectionStats = new RTCStats(null, {
   dataChannelsOpened: 'number',
   dataChannelsClosed: 'number',
   dataChannelsRequested: 'number',
@@ -107,7 +193,7 @@
  * https://w3c.github.io/webrtc-stats/#msstats-dict*
  * @private
  */
-var kRTCMediaStreamStats = new RTCStats_(null, {
+let kRTCMediaStreamStats = new RTCStats(null, {
   streamIdentifier: 'string',
   trackIds: 'sequence_string',
 });
@@ -118,7 +204,7 @@
  * https://w3c.github.io/webrtc-stats/#mststats-dict*
  * @private
  */
-var kRTCMediaStreamTrackStats = new RTCStats_(null, {
+let kRTCMediaStreamTrackStats = new RTCStats(null, {
   trackIdentifier: 'string',
   remoteSource: 'boolean',
   ended: 'boolean',
@@ -158,7 +244,7 @@
  * https://w3c.github.io/webrtc-stats/#dcstats-dict*
  * @private
  */
-var kRTCDataChannelStats = new RTCStats_(null, {
+let kRTCDataChannelStats = new RTCStats(null, {
   label: 'string',
   protocol: 'string',
   datachannelid: 'number',
@@ -175,7 +261,7 @@
  * https://w3c.github.io/webrtc-stats/#transportstats-dict*
  * @private
  */
-var kRTCTransportStats = new RTCStats_(null, {
+let kRTCTransportStats = new RTCStats(null, {
   bytesSent: 'number',
   bytesReceived: 'number',
   rtcpTransportStatsId: 'string',
@@ -191,7 +277,7 @@
  * https://w3c.github.io/webrtc-stats/#icecandidate-dict*
  * @private
  */
-var kRTCIceCandidateStats = new RTCStats_(null, {
+let kRTCIceCandidateStats = new RTCStats(null, {
   transportId: 'string',
   isRemote: 'boolean',
   networkType: 'string',
@@ -212,7 +298,7 @@
  * https://w3c.github.io/webrtc-stats/#candidatepair-dict*
  * @private
  */
-var kRTCIceCandidatePairStats = new RTCStats_(null, {
+let kRTCIceCandidatePairStats = new RTCStats(null, {
   transportId: 'string',
   localCandidateId: 'string',
   remoteCandidateId: 'string',
@@ -245,7 +331,7 @@
  * https://w3c.github.io/webrtc-stats/#certificatestats-dict*
  * @private
  */
-var kRTCCertificateStats = new RTCStats_(null, {
+let kRTCCertificateStats = new RTCStats(null, {
   fingerprint: 'string',
   fingerprintAlgorithm: 'string',
   base64Certificate: 'string',
@@ -351,7 +437,7 @@
 // Internals.
 
 /** @private */
-function RTCStats_(parent, membersObject) {
+function RTCStats(parent, membersObject) {
   if (parent != null)
     Object.assign(this, parent);
   Object.assign(this, membersObject);
@@ -387,7 +473,8 @@
       continue;
     }
     if (!whitelistedStats.hasOwnProperty(propertyName)) {
-      throw failTest('stats.' + propertyName + ' is not a whitelisted ' +
+      throw failTest(
+          stats.type + '.' + propertyName + ' is not a whitelisted ' +
           'member: ' + stats[propertyName]);
     }
     if (whitelistedStats[propertyName] === 'any')
diff --git a/components/arc/ime/arc_ime_service.cc b/components/arc/ime/arc_ime_service.cc
index 18a58f4..54ba67f 100644
--- a/components/arc/ime/arc_ime_service.cc
+++ b/components/arc/ime/arc_ime_service.cc
@@ -340,8 +340,6 @@
 // Overridden from keyboard::KeyboardControllerObserver
 void ArcImeService::OnKeyboardAppearanceChanged(
     const keyboard::KeyboardStateDescriptor& state) {
-  if (!focused_arc_window_)
-    return;
   gfx::Rect new_bounds = state.occluded_bounds;
   // Multiply by the scale factor. To convert from DIP to physical pixels.
   // The default scale factor is always used in Android side regardless of
diff --git a/components/autofill/core/browser/address_email_form_label_formatter.cc b/components/autofill/core/browser/address_email_form_label_formatter.cc
index 9e831633..486cf7e 100644
--- a/components/autofill/core/browser/address_email_form_label_formatter.cc
+++ b/components/autofill/core/browser/address_email_form_label_formatter.cc
@@ -18,8 +18,8 @@
 AddressEmailFormLabelFormatter::~AddressEmailFormLabelFormatter() {}
 
 // Note that the order--name, address, and email--in which parts of the label
-// are added ensures that the label is formatted correctly for |group| and for
-// this kind of formatter.
+// are added ensures that the label is formatted correctly for |focused_group|
+// and for this kind of formatter.
 base::string16 AddressEmailFormLabelFormatter::GetLabelForProfile(
     const AutofillProfile& profile,
     FieldTypeGroup focused_group) const {
diff --git a/components/autofill/core/browser/address_phone_form_label_formatter.cc b/components/autofill/core/browser/address_phone_form_label_formatter.cc
index 80438e4..63b0d785 100644
--- a/components/autofill/core/browser/address_phone_form_label_formatter.cc
+++ b/components/autofill/core/browser/address_phone_form_label_formatter.cc
@@ -17,22 +17,21 @@
 
 AddressPhoneFormLabelFormatter::~AddressPhoneFormLabelFormatter() {}
 
-// Note that the order--phone, name, and address--in which parts of the label
-// are added ensures that the label is formatted correctly for the focused
-// group.
+// Note that the order--name, phone, and address--in which parts of the label
+// are added ensures that the label is formatted correctly for |focused_group|
+// and for this kind of formatter.
 base::string16 AddressPhoneFormLabelFormatter::GetLabelForProfile(
     const AutofillProfile& profile,
     FieldTypeGroup focused_group) const {
   std::vector<base::string16> label_parts;
+  if (focused_group != NAME) {
+    AddLabelPartIfNotEmpty(GetLabelName(profile, app_locale()), &label_parts);
+  }
 
   if (focused_group != PHONE_HOME) {
     AddLabelPartIfNotEmpty(GetLabelPhone(profile, app_locale()), &label_parts);
   }
 
-  if (focused_group != NAME) {
-    AddLabelPartIfNotEmpty(GetLabelName(profile, app_locale()), &label_parts);
-  }
-
   if (focused_group != ADDRESS_HOME) {
     AddLabelPartIfNotEmpty(
         GetLabelAddress(form_has_street_address_, profile, app_locale(),
diff --git a/components/autofill/core/browser/address_phone_form_label_formatter_unittest.cc b/components/autofill/core/browser/address_phone_form_label_formatter_unittest.cc
index bb5f64c..e8bfaec 100644
--- a/components/autofill/core/browser/address_phone_form_label_formatter_unittest.cc
+++ b/components/autofill/core/browser/address_phone_form_label_formatter_unittest.cc
@@ -99,7 +99,7 @@
   EXPECT_THAT(
       formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2,
                                                          &profile3, &profile4}),
-      ElementsAre(FormatExpectedLabel("(617) 730-2000", "John F Kennedy"),
+      ElementsAre(FormatExpectedLabel("John F Kennedy", "(617) 730-2000"),
                   base::ASCIIToUTF16("Jackie Kennedy"),
                   base::ASCIIToUTF16("(617) 523-2338"), base::string16()));
 }
@@ -187,8 +187,8 @@
 
   EXPECT_THAT(
       formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
-      ElementsAre(FormatExpectedLabel("(11) 2648-0254", "Tarsila do Amaral"),
-                  FormatExpectedLabel("(21) 98765-0000", "Artur Avila")));
+      ElementsAre(FormatExpectedLabel("Tarsila do Amaral", "(11) 2648-0254"),
+                  FormatExpectedLabel("Artur Avila", "(21) 98765-0000")));
 }
 
 TEST(AddressPhoneFormLabelFormatterTest,
diff --git a/components/autofill/core/browser/autofill_profile.cc b/components/autofill/core/browser/autofill_profile.cc
index 2d3c17c4..3acbc8c 100644
--- a/components/autofill/core/browser/autofill_profile.cc
+++ b/components/autofill/core/browser/autofill_profile.cc
@@ -948,7 +948,6 @@
   int validity_value = 0;
   size_t field_type_shift = 0;
   for (ServerFieldType supported_type : kSupportedTypesByClientForValidation) {
-    DCHECK(GetValidityState(supported_type, CLIENT) != UNSUPPORTED);
     validity_value |= GetValidityState(supported_type, CLIENT)
                       << field_type_shift;
     field_type_shift += kValidityBitsPerType;
diff --git a/components/autofill/core/browser/autofill_profile_validation_util.cc b/components/autofill/core/browser/autofill_profile_validation_util.cc
index 45f0530..8adf74309 100644
--- a/components/autofill/core/browser/autofill_profile_validation_util.cc
+++ b/components/autofill/core/browser/autofill_profile_validation_util.cc
@@ -41,14 +41,15 @@
 using ::i18n::addressinput::MISSING_REQUIRED_FIELD;
 using ::i18n::addressinput::UNEXPECTED_FIELD;
 using ::i18n::addressinput::UNKNOWN_VALUE;
+using ::i18n::addressinput::UNSUPPORTED_FIELD;
 
 using ::i18n::phonenumbers::PhoneNumberUtil;
 
 const AddressField kFields[] = {COUNTRY, ADMIN_AREA, LOCALITY,
                                 DEPENDENT_LOCALITY, POSTAL_CODE};
-const AddressProblem kProblems[] = {UNEXPECTED_FIELD, MISSING_REQUIRED_FIELD,
-                                    UNKNOWN_VALUE, INVALID_FORMAT,
-                                    MISMATCHING_VALUE};
+const AddressProblem kProblems[] = {UNEXPECTED_FIELD,  MISSING_REQUIRED_FIELD,
+                                    UNKNOWN_VALUE,     INVALID_FORMAT,
+                                    MISMATCHING_VALUE, UNSUPPORTED_FIELD};
 
 // If the |address_field| is valid, set the validity state of the
 // |address_field| in the |profile| to the |state| and return true.
@@ -269,9 +270,25 @@
   AddressValidator::Status status =
       address_validator->ValidateAddress(address, GetFilter(), &problems);
 
-  for (auto problem : problems)
-    SetValidityStateForAddressField(profile, problem.first,
-                                    AutofillDataModel::INVALID);
+  // The address fields for which validation is not supported by the metadata
+  // will be marked as UNSUPPORTED_FIELDs. These fields should be treated like
+  // VALID fields to stay consistent. INVALID_FORMATs, MISMATCHING_VALUEs or
+  // UNKNOWN_VALUEs are INVALID. MISSING_REQUIRED_FIELD would be marked as EMPTY
+  // along other empty fields. UNEXPECTED_FIELD would mean that there is also no
+  // metadata for validation, therefore, they are also UNSUPPORTED_FIELDs, and
+  // thus they would be treated as VALID fields.
+  for (auto problem : problems) {
+    if (problem.second == UNSUPPORTED_FIELD) {
+      SetValidityStateForAddressField(profile, problem.first,
+                                      AutofillDataModel::VALID);
+
+    } else if (problem.second == INVALID_FORMAT ||
+               problem.second == MISMATCHING_VALUE ||
+               problem.second == UNKNOWN_VALUE) {
+      SetValidityStateForAddressField(profile, problem.first,
+                                      AutofillDataModel::INVALID);
+    }
+  }
 
   SetEmptyValidityIfEmpty(profile);
 
diff --git a/components/autofill/core/browser/autofill_profile_validation_util_unittest.cc b/components/autofill/core/browser/autofill_profile_validation_util_unittest.cc
index 4b36d54..59a29b7 100644
--- a/components/autofill/core/browser/autofill_profile_validation_util_unittest.cc
+++ b/components/autofill/core/browser/autofill_profile_validation_util_unittest.cc
@@ -197,7 +197,7 @@
   // The city can't be validated, because we don't know the state, in the strict
   // validation this is considered as invalid.
   EXPECT_EQ(
-      AutofillDataModel::INVALID,
+      AutofillDataModel::VALID,
       profile.GetValidityState(ADDRESS_HOME_CITY, AutofillDataModel::CLIENT));
   EXPECT_EQ(AutofillDataModel::EMPTY,
             profile.GetValidityState(ADDRESS_HOME_DEPENDENT_LOCALITY,
@@ -221,7 +221,7 @@
   // The city can't be validated, because we don't know the state, in the strict
   // validation this is considered as invalid.
   EXPECT_EQ(
-      AutofillDataModel::INVALID,
+      AutofillDataModel::VALID,
       profile.GetValidityState(ADDRESS_HOME_CITY, AutofillDataModel::CLIENT));
   EXPECT_EQ(AutofillDataModel::EMPTY,
             profile.GetValidityState(ADDRESS_HOME_DEPENDENT_LOCALITY,
@@ -903,6 +903,12 @@
       AutofillDataModel::VALID,
       profile.GetValidityState(ADDRESS_HOME_STATE, AutofillDataModel::CLIENT));
   EXPECT_EQ(
+      AutofillDataModel::VALID,
+      profile.GetValidityState(ADDRESS_HOME_CITY, AutofillDataModel::CLIENT));
+  EXPECT_EQ(AutofillDataModel::EMPTY,
+            profile.GetValidityState(ADDRESS_HOME_DEPENDENT_LOCALITY,
+                                     AutofillDataModel::CLIENT));
+  EXPECT_EQ(
       AutofillDataModel::INVALID,
       profile.GetValidityState(ADDRESS_HOME_ZIP, AutofillDataModel::CLIENT));
   EXPECT_EQ(AutofillDataModel::VALID,
@@ -926,6 +932,12 @@
       profile.GetValidityState(ADDRESS_HOME_STATE, AutofillDataModel::CLIENT));
   EXPECT_EQ(
       AutofillDataModel::VALID,
+      profile.GetValidityState(ADDRESS_HOME_CITY, AutofillDataModel::CLIENT));
+  EXPECT_EQ(AutofillDataModel::EMPTY,
+            profile.GetValidityState(ADDRESS_HOME_DEPENDENT_LOCALITY,
+                                     AutofillDataModel::CLIENT));
+  EXPECT_EQ(
+      AutofillDataModel::VALID,
       profile.GetValidityState(ADDRESS_HOME_ZIP, AutofillDataModel::CLIENT));
   EXPECT_EQ(AutofillDataModel::INVALID,
             profile.GetValidityState(PHONE_HOME_WHOLE_NUMBER,
@@ -948,6 +960,12 @@
       profile.GetValidityState(ADDRESS_HOME_STATE, AutofillDataModel::CLIENT));
   EXPECT_EQ(
       AutofillDataModel::VALID,
+      profile.GetValidityState(ADDRESS_HOME_CITY, AutofillDataModel::CLIENT));
+  EXPECT_EQ(AutofillDataModel::EMPTY,
+            profile.GetValidityState(ADDRESS_HOME_DEPENDENT_LOCALITY,
+                                     AutofillDataModel::CLIENT));
+  EXPECT_EQ(
+      AutofillDataModel::VALID,
       profile.GetValidityState(ADDRESS_HOME_ZIP, AutofillDataModel::CLIENT));
   EXPECT_EQ(AutofillDataModel::VALID,
             profile.GetValidityState(PHONE_HOME_WHOLE_NUMBER,
@@ -1020,11 +1038,11 @@
   // We can't validate city, because state is not valid, in the strict
   // validation this is considered as invalid.
   EXPECT_EQ(
-      AutofillDataModel::INVALID,
+      AutofillDataModel::VALID,
       profile.GetValidityState(ADDRESS_HOME_CITY, AutofillDataModel::CLIENT));
   // The dependent locality is not a Canadian field, so it's considered as
   // invalid.
-  EXPECT_EQ(AutofillDataModel::INVALID,
+  EXPECT_EQ(AutofillDataModel::VALID,
             profile.GetValidityState(ADDRESS_HOME_DEPENDENT_LOCALITY,
                                      AutofillDataModel::CLIENT));
   EXPECT_EQ(AutofillDataModel::UNSUPPORTED,
diff --git a/components/autofill/core/browser/autofill_profile_validator_unittest.cc b/components/autofill/core/browser/autofill_profile_validator_unittest.cc
index 5278b8b..34203afe 100644
--- a/components/autofill/core/browser/autofill_profile_validator_unittest.cc
+++ b/components/autofill/core/browser/autofill_profile_validator_unittest.cc
@@ -122,11 +122,14 @@
   EXPECT_EQ(false, AreRulesLoadedForRegion(country_code));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::VALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
+      {EMAIL_ADDRESS, AutofillDataModel::VALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -222,11 +225,14 @@
                      base::UTF8ToUTF16("Invalid Phone"));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::INVALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::VALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::INVALID},
+      {EMAIL_ADDRESS, AutofillDataModel::VALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -238,11 +244,14 @@
   profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16("Invalid State"));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::INVALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::VALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::INVALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
+      {EMAIL_ADDRESS, AutofillDataModel::VALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -256,11 +265,14 @@
   profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16("Invalid State"));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::INVALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::EMPTY},
-                        {EMAIL_ADDRESS, AutofillDataModel::VALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::INVALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::EMPTY},
+      {EMAIL_ADDRESS, AutofillDataModel::VALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -274,11 +286,14 @@
   profile.SetRawInfo(ADDRESS_HOME_ZIP, base::UTF8ToUTF16("Invalid Zip"));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::INVALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::INVALID},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
+      {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -292,11 +307,14 @@
   profile.SetRawInfo(ADDRESS_HOME_ZIP, base::UTF8ToUTF16("Invalid Zip"));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::INVALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::EMPTY}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::INVALID},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
+      {EMAIL_ADDRESS, AutofillDataModel::EMPTY}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -310,11 +328,14 @@
   profile.SetRawInfo(ADDRESS_HOME_ZIP, base::string16());
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::EMPTY},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::EMPTY},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
+      {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -329,11 +350,14 @@
                      base::UTF8ToUTF16("Invalid Phone"));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::INVALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::INVALID},
+      {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -345,11 +369,14 @@
   profile.SetRawInfo(EMAIL_ADDRESS, base::ASCIIToUTF16("Invalid Email"));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::VALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::VALID},
+      {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
@@ -365,11 +392,14 @@
   profile.SetRawInfo(ADDRESS_HOME_STATE, base::UTF8ToUTF16("Invalid State"));
 
   // Set up the test expectations.
-  expected_validity_ = {{ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
-                        {ADDRESS_HOME_STATE, AutofillDataModel::INVALID},
-                        {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
-                        {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::INVALID},
-                        {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
+  expected_validity_ = {
+      {ADDRESS_HOME_COUNTRY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_STATE, AutofillDataModel::INVALID},
+      {ADDRESS_HOME_CITY, AutofillDataModel::VALID},
+      {ADDRESS_HOME_DEPENDENT_LOCALITY, AutofillDataModel::EMPTY},
+      {ADDRESS_HOME_ZIP, AutofillDataModel::VALID},
+      {PHONE_HOME_WHOLE_NUMBER, AutofillDataModel::INVALID},
+      {EMAIL_ADDRESS, AutofillDataModel::INVALID}};
 
   // Start the validator.
   validator_->StartProfileValidation(&profile, std::move(onvalidated_cb_));
diff --git a/components/autofill/core/browser/contact_form_label_formatter.cc b/components/autofill/core/browser/contact_form_label_formatter.cc
index 9c81ab0e..05a7a0e 100644
--- a/components/autofill/core/browser/contact_form_label_formatter.cc
+++ b/components/autofill/core/browser/contact_form_label_formatter.cc
@@ -18,21 +18,21 @@
 
 ContactFormLabelFormatter::~ContactFormLabelFormatter() {}
 
-// Note that the order--phone, name, and email--in which parts of the label
-// are added ensures that the label is formatted correctly for |focused_group|.
+// Note that the order--name, phone, and email--in which parts of the label
+// are possibly added ensures that the label is formatted correctly for
+// |focused_group| and for this kind of formatter.
 base::string16 ContactFormLabelFormatter::GetLabelForProfile(
     const AutofillProfile& profile,
     FieldTypeGroup focused_group) const {
   std::vector<base::string16> label_parts;
+  if (focused_group != NAME) {
+    AddLabelPartIfNotEmpty(GetLabelName(profile, app_locale()), &label_parts);
+  }
 
   if (focused_group != PHONE_HOME) {
     AddLabelPartIfNotEmpty(MaybeGetPhone(profile), &label_parts);
   }
 
-  if (focused_group != NAME) {
-    AddLabelPartIfNotEmpty(GetLabelName(profile, app_locale()), &label_parts);
-  }
-
   if (focused_group != EMAIL) {
     AddLabelPartIfNotEmpty(MaybeGetEmail(profile), &label_parts);
   }
diff --git a/components/autofill/core/browser/contact_form_label_formatter_unittest.cc b/components/autofill/core/browser/contact_form_label_formatter_unittest.cc
index dc95f6f..67add44d 100644
--- a/components/autofill/core/browser/contact_form_label_formatter_unittest.cc
+++ b/components/autofill/core/browser/contact_form_label_formatter_unittest.cc
@@ -97,9 +97,9 @@
   EXPECT_THAT(
       formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2,
                                                          &profile3, &profile4}),
-      ElementsAre(FormatExpectedLabel("(617) 730-2000", "John F Kennedy"),
+      ElementsAre(FormatExpectedLabel("John F Kennedy", "(617) 730-2000"),
                   base::ASCIIToUTF16("Jackie Kennedy"),
-                  FormatExpectedLabel("(617) 523-2338", "Paul Revere"),
+                  FormatExpectedLabel("Paul Revere", "(617) 523-2338"),
                   base::string16()));
 }
 
@@ -181,8 +181,8 @@
 
   EXPECT_THAT(
       formatter->GetLabels(std::vector<AutofillProfile*>{&profile1, &profile2}),
-      ElementsAre(FormatExpectedLabel("(11) 2648-0254", "Tarsila do Amaral"),
-                  FormatExpectedLabel("(21) 98765-0000", "Artur Avila")));
+      ElementsAre(FormatExpectedLabel("Tarsila do Amaral", "(11) 2648-0254"),
+                  FormatExpectedLabel("Artur Avila", "(21) 98765-0000")));
 }
 
 TEST(ContactFormLabelFormatterTest, GetLabelsForBRProfilesAndFocusedPhone) {
diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc
index 414ae10..e362a13 100644
--- a/components/autofill/core/browser/personal_data_manager_unittest.cc
+++ b/components/autofill/core/browser/personal_data_manager_unittest.cc
@@ -6987,7 +6987,7 @@
   EXPECT_EQ(
       AutofillDataModel::VALID,
       profiles[0]->GetValidityState(ADDRESS_HOME_ZIP, AutofillProfile::CLIENT));
-  EXPECT_EQ(AutofillDataModel::INVALID,
+  EXPECT_EQ(AutofillDataModel::VALID,
             profiles[0]->GetValidityState(ADDRESS_HOME_CITY,
                                           AutofillProfile::CLIENT));
   EXPECT_EQ(AutofillDataModel::EMPTY,
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index 128949e..469e1db8 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -170,6 +170,7 @@
     "script_executor_unittest.cc",
     "script_precondition_unittest.cc",
     "script_tracker_unittest.cc",
+    "selector_unittest.cc",
     "string_conversions_util_unittest.cc",
   ]
 
diff --git a/components/autofill_assistant/browser/actions/prompt_action.cc b/components/autofill_assistant/browser/actions/prompt_action.cc
index d94464f4..d9f769f 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action.cc
@@ -33,7 +33,7 @@
 void PromptAction::InternalProcessAction(ActionDelegate* delegate,
                                          ProcessActionCallback callback) {
   if (proto_.prompt().choices_size() == 0) {
-    UpdateProcessedAction(OTHER_ACTION_STATUS);
+    UpdateProcessedAction(INVALID_ACTION);
     std::move(callback).Run(std::move(processed_action_proto_));
     return;
   }
diff --git a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
index abe2649..2d8368b 100644
--- a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
@@ -68,8 +68,9 @@
 };
 
 TEST_F(PromptActionTest, ChoicesMissing) {
-  EXPECT_CALL(callback_, Run(Pointee(Property(&ProcessedActionProto::status,
-                                              OTHER_ACTION_STATUS))));
+  EXPECT_CALL(
+      callback_,
+      Run(Pointee(Property(&ProcessedActionProto::status, INVALID_ACTION))));
   PromptAction action(proto_);
   action.ProcessAction(&mock_action_delegate_, callback_.Get());
 }
diff --git a/components/autofill_assistant/browser/actions/show_details_action.cc b/components/autofill_assistant/browser/actions/show_details_action.cc
index 3839729..1f9ff6a8 100644
--- a/components/autofill_assistant/browser/actions/show_details_action.cc
+++ b/components/autofill_assistant/browser/actions/show_details_action.cc
@@ -56,7 +56,7 @@
 
   if (!details_valid) {
     DVLOG(1) << "Failed to fill the details";
-    UpdateProcessedAction(OTHER_ACTION_STATUS);
+    UpdateProcessedAction(INVALID_ACTION);
   } else {
     delegate->SetDetails(std::move(details));
     UpdateProcessedAction(ACTION_APPLIED);
diff --git a/components/autofill_assistant/browser/client_status.cc b/components/autofill_assistant/browser/client_status.cc
index 9c5c67b..70c40695 100644
--- a/components/autofill_assistant/browser/client_status.cc
+++ b/components/autofill_assistant/browser/client_status.cc
@@ -15,12 +15,25 @@
 
 void ClientStatus::FillProto(ProcessedActionProto* proto) const {
   proto->set_status(status_);
-  // TODO(b/129387787): Fill extra debugging information collected in the
-  // ClientStatus.
+  if (has_unexpected_error_info_)
+    proto->mutable_unexpected_error_info()->MergeFrom(unexpected_error_info_);
 }
 
 std::ostream& operator<<(std::ostream& out, const ClientStatus& status) {
-  return out << status.status_;
+  out << status.status_;
+#ifndef NDEBUG
+  if (status.has_unexpected_error_info_) {
+    out << status.unexpected_error_info_.source_file() << ":"
+        << status.unexpected_error_info_.source_line_number();
+    if (!status.unexpected_error_info_.js_exception_classname().empty()) {
+      out << " JS error "
+          << status.unexpected_error_info_.js_exception_classname() << " at "
+          << status.unexpected_error_info_.js_exception_line_number() << ":"
+          << status.unexpected_error_info_.js_exception_column_number();
+    }
+  }
+#endif
+  return out;
 }
 
 const ClientStatus& OkClientStatus() {
@@ -87,6 +100,14 @@
       out << "ELEMENT_UNSTABLE";
       break;
 
+    case ProcessedActionStatusProto::OPTION_VALUE_NOT_FOUND:
+      out << "OPTION_VALUE_NOT_FOUND";
+      break;
+
+    case ProcessedActionStatusProto::UNEXPECTED_JS_ERROR:
+      out << "UNEXPECTED_JS_ERROR";
+      break;
+
       // Intentionally no default case to make compilation fail if a new value
       // was added to the enum but not to this list.
   }
diff --git a/components/autofill_assistant/browser/client_status.h b/components/autofill_assistant/browser/client_status.h
index 7964001c..5f74d776 100644
--- a/components/autofill_assistant/browser/client_status.h
+++ b/components/autofill_assistant/browser/client_status.h
@@ -30,14 +30,26 @@
   // Modifies the corresponding proto status.
   void set_proto_status(ProcessedActionStatusProto status) { status_ = status; }
 
+  // Returns a mutable version of unexpected error info, creates one if
+  // necessary.
+  UnexpectedErrorInfoProto* mutable_unexpected_error_info() {
+    has_unexpected_error_info_ = true;
+    return &unexpected_error_info_;
+  }
+
+  // Returns the unexpected error infos associated with this status.
+  const UnexpectedErrorInfoProto& unexpected_error_info() const {
+    return unexpected_error_info_;
+  }
+
   // The output operator, for logging.
   friend std::ostream& operator<<(std::ostream& out,
                                   const ClientStatus& status);
 
  private:
   ProcessedActionStatusProto status_;
-  // TODO(b/129387787): Add more information, to be reported to
-  // ProcessedActionProto
+  bool has_unexpected_error_info_ = false;
+  UnexpectedErrorInfoProto unexpected_error_info_;
 };
 
 // An OK status.
diff --git a/components/autofill_assistant/browser/selector.cc b/components/autofill_assistant/browser/selector.cc
index 2b2ada2..b60f5d3e3 100644
--- a/components/autofill_assistant/browser/selector.cc
+++ b/components/autofill_assistant/browser/selector.cc
@@ -14,6 +14,7 @@
   for (const auto& selector : element.selectors()) {
     selectors.emplace_back(selector);
   }
+  inner_text_pattern = element.inner_text_pattern();
   pseudo_type = element.pseudo_type();
 }
 
@@ -29,13 +30,24 @@
 Selector& Selector::operator=(Selector&& other) = default;
 
 bool Selector::operator<(const Selector& other) const {
-  return this->selectors < other.selectors ||
-         (this->selectors == other.selectors &&
-          this->pseudo_type < other.pseudo_type);
+  if (selectors < other.selectors)
+    return true;
+
+  if (selectors != other.selectors)
+    return false;
+
+  if (inner_text_pattern < other.inner_text_pattern)
+    return true;
+
+  if (inner_text_pattern != other.inner_text_pattern)
+    return false;
+
+  return pseudo_type < other.pseudo_type;
 }
 
 bool Selector::operator==(const Selector& other) const {
   return this->selectors == other.selectors &&
+         this->inner_text_pattern == other.inner_text_pattern &&
          this->pseudo_type == other.pseudo_type;
 }
 
@@ -109,6 +121,11 @@
   return out;
 #else
   out << "elements=[" << base::JoinString(selector.selectors, ",") << "]";
+  if (!selector.inner_text_pattern.empty()) {
+    out << " innerText =~ /";
+    out << selector.inner_text_pattern;
+    out << "/";
+  }
   if (selector.pseudo_type != PseudoType::UNDEFINED) {
     out << "::" << selector.pseudo_type;
   }
diff --git a/components/autofill_assistant/browser/selector.h b/components/autofill_assistant/browser/selector.h
index 686d0d9..835b232 100644
--- a/components/autofill_assistant/browser/selector.h
+++ b/components/autofill_assistant/browser/selector.h
@@ -21,6 +21,10 @@
   // document.
   std::vector<std::string> selectors;
 
+  // If non-empty, this must be a regular expression that matches the inner text
+  // of the element(s) matching selectors.
+  std::string inner_text_pattern;
+
   // An optional pseudo type. This pseudo type is associated to the final
   // element matched by |selectors|, which means that we currently don't handle
   // matching an element inside a pseudo element.
@@ -46,6 +50,12 @@
 
   // Checks whether this selector is empty.
   bool empty() const;
+
+  // Convenience function to set inner_text_pattern in a fluent style.
+  Selector& MatchingInnerText(const std::string& pattern) {
+    inner_text_pattern = pattern;
+    return *this;
+  }
 };
 }  // namespace autofill_assistant
 
diff --git a/components/autofill_assistant/browser/selector_unittest.cc b/components/autofill_assistant/browser/selector_unittest.cc
new file mode 100644
index 0000000..94a4346
--- /dev/null
+++ b/components/autofill_assistant/browser/selector_unittest.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 "components/autofill_assistant/browser/selector.h"
+
+#include "base/macros.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+namespace {
+
+TEST(SelectorTest, FromProto) {
+  ElementReferenceProto proto;
+  proto.add_selectors("a");
+  proto.add_selectors("b");
+  proto.set_inner_text_pattern("c");
+  proto.set_pseudo_type(PseudoType::BEFORE);
+
+  Selector selector(proto);
+  EXPECT_THAT(selector.selectors, testing::ElementsAre("a", "b"));
+  EXPECT_EQ("c", selector.inner_text_pattern);
+  EXPECT_EQ(PseudoType::BEFORE, selector.pseudo_type);
+}
+
+TEST(SelectorTest, Comparison) {
+  EXPECT_FALSE(Selector({"a"}) == Selector({"b"}));
+  EXPECT_LT(Selector({"a"}), Selector({"b"}));
+  EXPECT_TRUE(Selector({"a"}) == Selector({"a"}));
+
+  EXPECT_FALSE(Selector({"a"}, PseudoType::BEFORE) ==
+               Selector({"a"}, PseudoType::AFTER));
+  EXPECT_LT(Selector({"a"}, PseudoType::BEFORE),
+            Selector({"a"}, PseudoType::AFTER));
+  EXPECT_LT(Selector({"a"}, PseudoType::BEFORE), Selector({"b"}));
+  EXPECT_TRUE(Selector({"a"}, PseudoType::BEFORE) ==
+              Selector({"a"}, PseudoType::BEFORE));
+
+  EXPECT_FALSE(Selector({"a"}).MatchingInnerText("a") ==
+               Selector({"a"}).MatchingInnerText("b"));
+  EXPECT_LT(Selector({"a"}).MatchingInnerText("a"),
+            Selector({"a"}).MatchingInnerText("b"));
+  EXPECT_TRUE(Selector({"a"}).MatchingInnerText("a") ==
+              Selector({"a"}).MatchingInnerText("a"));
+}
+
+}  // namespace
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index d76bee5..587db658 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -351,6 +351,44 @@
     // Should be set as a result of SetFormFieldValueAction.
     SetFormFieldValueProto.Result set_form_field_value_result = 17;
   }
+
+  // More information included for unexpected errors.
+  //
+  // Only set for action whose status are OTHER_ACTION_STATUS or
+  // UNEXPECTED_JS_ERROR.
+  optional UnexpectedErrorInfoProto unexpected_error_info = 18;
+}
+
+// Extra debugging information included in case of unexpected errors.
+//
+// Presence of this element is usually the sign of a bug in the client code and
+// is always the sign that the client code needs to be updated: such issues
+// should be either fixed or reported as proper, expected error with a useful
+// status code.
+message UnexpectedErrorInfoProto {
+  // Source file, within the client code, where an unexpected error was detected
+  // and reported.
+  //
+  // Only filled for unexpected errors OTHER_ACTION_STATUS and
+  // UNEXPECTED_JS_ERROR.
+  //
+  // This and source_line are only meaningful for the exact version reported in
+  // the client context.
+  optional string source_file = 1;
+
+  // Line number, within the client's source file, where the error was detected.
+  optional int32 source_line_number = 2;
+
+  // JavaScript exception class name, if reporting a JavaScript error.
+  optional string js_exception_classname = 3;
+
+  // JavaScript exception line number, within the js snippet that was sent to
+  // devtools runtime by the client, if reporting a JavaScript error.
+  optional int32 js_exception_line_number = 4;
+
+  // JavaScript exception column number, within the js snippet that was sent to
+  // devtools runtime by the client, if reporting a JavaScript error.
+  optional int32 js_exception_column_number = 5;
 }
 
 enum ProcessedActionStatusProto {
@@ -363,6 +401,12 @@
   ACTION_APPLIED = 2;
 
   // The action failed (generic error).
+  //
+  // This usually means that the client needs to be fixed: either the error
+  // should be assigned a more specific error code, or a bug in the client code
+  // needs to be fixed.
+  //
+  // ProcessedActionProto.UnexpectedErrorInfoProto contains more details.
   OTHER_ACTION_STATUS = 3;
 
   // The action failed to get payment information.
@@ -418,6 +462,16 @@
 
   // Failed to get a stable position for the element, usually to click on it.
   ELEMENT_UNSTABLE = 14;
+
+  // The value passed to the select option action does not exist in the element.
+  // This is usually a scripting error.
+  OPTION_VALUE_NOT_FOUND = 15;
+
+  // The client got an unexpected error from a JavaScript snippet executed
+  // through devtools. This means that there's a bug in the client code.
+  //
+  // ProcessedActionProto.UnexpectedErrorInfoProto contains more details.
+  UNEXPECTED_JS_ERROR = 16;
 }
 
 // The pseudo type values come from
@@ -450,9 +504,17 @@
   // elements (i.e. resolve to more than one element on the page).
   repeated string selectors = 2;
 
+  // If non-empty, only take into accounts the elements matched by selector
+  // whose inner text matches the given JavaScript regex. This does a search,
+  // not a full match. Add ^ and $ as necessary.
+  //
+  // This is used to filter the elements matching the last selector, before
+  // trying to get the pseudo_type, if any was set.
+  optional string inner_text_pattern = 4;
+
   // An optional pseudo type. This pseudo type is associated to the final
-  // element matched by |selector|, which means that we currently don't handle
-  // matching an element inside a pseudo element.
+  // element matched, which means that we currently don't handle matching an
+  // element inside a pseudo element.
   optional PseudoType pseudo_type = 3;
 }
 
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index d8f76aff..acc4d4e 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -157,12 +157,18 @@
 
 // Javascript code to query a visible elements for a selector, either the first
 // (non-strict) or a single (strict) visible element.q
-const char* const kQuerySelectorVisible =
-    R"(function (selector, strict) {
+const char* const kQuerySelectorWithConditions =
+    R"(function (selector, strict, visible, inner_text_re) {
         var found = this.querySelectorAll(selector);
         var found_index = -1;
+        var re = inner_text_re ? RegExp(inner_text_re) : undefined;
+        var match = function(e) {
+          if (visible && e.getClientRects().length == 0) return false;
+          if (re && !re.test(e.innerText)) return false;
+          return true;
+        };
         for (let i = 0; i < found.length; i++) {
-          if (found[i].getClientRects().length > 0) {
+          if (match(found[i])) {
             if (found_index != -1) return undefined;
             found_index = i;
             if (!strict) break;
@@ -231,6 +237,38 @@
   }
   return false;
 }
+
+// Builds a ClientStatus appropriate for an unexpected error.
+//
+// This should only be used in situations where getting an error cannot be
+// anything but a bug in the client.
+ClientStatus UnexpectedErrorStatus(const std::string& file, int line) {
+  ClientStatus status(OTHER_ACTION_STATUS);
+  auto* info = status.mutable_unexpected_error_info();
+  info->set_source_file(file);
+  info->set_source_line_number(line);
+  return status;
+}
+
+// Builds a ClientStatus appropriate for a JavaScript error.
+ClientStatus JavaScriptErrorStatus(const std::string& file,
+                                   int line,
+                                   const runtime::ExceptionDetails* exception) {
+  ClientStatus status(UNEXPECTED_JS_ERROR);
+  auto* info = status.mutable_unexpected_error_info();
+  info->set_source_file(file);
+  info->set_source_line_number(line);
+  if (exception) {
+    if (exception->HasException() &&
+        exception->GetException()->HasClassName()) {
+      info->set_js_exception_classname(
+          exception->GetException()->GetClassName());
+    }
+    info->set_js_exception_line_number(exception->GetLineNumber());
+    info->set_js_exception_column_number(exception->GetColumnNumber());
+  }
+  return status;
+}
 }  // namespace
 
 class WebController::Worker {
@@ -507,7 +545,7 @@
   element_result_->object_id = "";
   if (!result || !result->GetResult() || !result->GetResult()->HasObjectId()) {
     DVLOG(1) << __func__ << " Failed to get document root element.";
-    SendResult(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    SendResult(UnexpectedErrorStatus(__FILE__, __LINE__));
     return;
   }
 
@@ -532,11 +570,24 @@
       runtime::CallArgument::Builder()
           .SetValue(base::Value::ToUniquePtrValue(base::Value(strict_)))
           .Build());
-  const char* function = (index == (selector_.selectors.size() - 1) &&
-                          selector_.pseudo_type == PseudoType::UNDEFINED &&
-                          check_type_ == kVisibilityCheck)
-                             ? kQuerySelectorVisible
-                             : kQuerySelector;
+  std::string function;
+  if (index == (selector_.selectors.size() - 1)) {
+    bool visible = check_type_ == kVisibilityCheck;
+    if (visible || !selector_.inner_text_pattern.empty()) {
+      function.assign(kQuerySelectorWithConditions);
+      argument.emplace_back(
+          runtime::CallArgument::Builder()
+              .SetValue(base::Value::ToUniquePtrValue(base::Value(visible)))
+              .Build());
+      argument.emplace_back(runtime::CallArgument::Builder()
+                                .SetValue(base::Value::ToUniquePtrValue(
+                                    base::Value(selector_.inner_text_pattern)))
+                                .Build());
+    }
+  }
+  if (function.empty()) {
+    function.assign(kQuerySelector);
+  }
   devtools_client_->GetRuntime()->CallFunctionOn(
       runtime::CallFunctionOnParams::Builder()
           .SetObjectId(object_id)
@@ -550,6 +601,12 @@
 void WebController::ElementFinder::OnQuerySelectorAll(
     size_t index,
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
+  if (!result || result->HasExceptionDetails()) {
+    DVLOG(1) << __func__ << "Failed to query selector " << index << " of "
+             << selector_;
+    SendResult(ClientStatus(OTHER_ACTION_STATUS));
+    return;
+  }
   if (!result || !result->GetResult() || !result->GetResult()->HasObjectId()) {
     SendResult(ClientStatus(ELEMENT_RESOLUTION_FAILED));
     return;
@@ -598,7 +655,7 @@
     std::unique_ptr<dom::DescribeNodeResult> result) {
   if (!result || !result->GetNode()) {
     DVLOG(1) << __func__ << " Failed to describe the node for pseudo element.";
-    SendResult(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    SendResult(UnexpectedErrorStatus(__FILE__, __LINE__));
     return;
   }
 
@@ -628,7 +685,6 @@
   if (result && result->GetObject() && result->GetObject()->HasObjectId()) {
     element_result_->object_id = result->GetObject()->GetObjectId();
   }
-
   SendResult(OkClientStatus());
 }
 
@@ -638,7 +694,7 @@
     std::unique_ptr<dom::DescribeNodeResult> result) {
   if (!result || !result->GetNode()) {
     DVLOG(1) << __func__ << " Failed to describe the node.";
-    SendResult(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    SendResult(UnexpectedErrorStatus(__FILE__, __LINE__));
     return;
   }
 
@@ -669,7 +725,7 @@
         frame_name, node->GetContentDocument()->GetDocumentURL());
     if (!element_result_->container_frame_host) {
       DVLOG(1) << __func__ << " Failed to find corresponding owner frame.";
-      SendResult(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+      SendResult(UnexpectedErrorStatus(__FILE__, __LINE__));
       return;
     }
   } else if (node->HasFrameId()) {
@@ -703,7 +759,7 @@
     std::unique_ptr<dom::ResolveNodeResult> result) {
   if (!result || !result->GetObject() || !result->GetObject()->HasObjectId()) {
     DVLOG(1) << __func__ << " Failed to resolve object id from backend id.";
-    SendResult(ClientStatus(OTHER_ACTION_STATUS));
+    SendResult(UnexpectedErrorStatus(__FILE__, __LINE__));
     return;
   }
 
@@ -849,7 +905,10 @@
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
   if (!result || result->HasExceptionDetails()) {
     DVLOG(1) << __func__ << " Failed to scroll the element.";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    std::move(callback).Run(JavaScriptErrorStatus(
+        __FILE__, __LINE__,
+        result->HasExceptionDetails() ? result->GetExceptionDetails()
+                                      : nullptr));
     return;
   }
 
@@ -916,7 +975,7 @@
   if (!result) {
     DVLOG(1) << __func__
              << " Failed to dispatch mouse left button pressed event.";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
     return;
   }
 
@@ -943,7 +1002,7 @@
     std::unique_ptr<input::DispatchTouchEventResult> result) {
   if (!result) {
     DVLOG(1) << __func__ << " Failed to dispatch touch start event.";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__));
     return;
   }
 
@@ -1052,7 +1111,10 @@
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
   if (!result || result->HasExceptionDetails()) {
     DVLOG(1) << __func__ << " Failed to focus on element.";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    std::move(callback).Run(JavaScriptErrorStatus(
+        __FILE__, __LINE__,
+        result->HasExceptionDetails() ? result->GetExceptionDetails()
+                                      : nullptr));
     return;
   }
   std::move(callback).Run(OkClientStatus());
@@ -1108,7 +1170,8 @@
     const autofill::FormFieldData& form_field) {
   if (form_data.fields.empty()) {
     DVLOG(1) << __func__ << " Failed to get form data to fill form.";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    std::move(callback).Run(
+        UnexpectedErrorStatus(__FILE__, __LINE__));  // unexpected
     return;
   }
 
@@ -1116,7 +1179,8 @@
       ContentAutofillDriver::GetForRenderFrameHost(container_frame_host);
   if (!driver) {
     DVLOG(1) << __func__ << " Failed to get the autofill driver.";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    std::move(callback).Run(
+        UnexpectedErrorStatus(__FILE__, __LINE__));  // unexpected
     return;
   }
 
@@ -1192,12 +1256,15 @@
     base::OnceCallback<void(const ClientStatus&)> callback,
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
   if (!result || result->HasExceptionDetails() ||
-      !result->GetResult()->GetValue()->GetBool()) {
+      !result->GetResult()->GetValue()->is_bool()) {
     DVLOG(1) << __func__ << " Failed to select option.";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
     return;
   }
-  DCHECK(result->GetResult()->GetValue()->is_bool());
+  if (!result->GetResult()->GetValue()->GetBool()) {
+    DVLOG(1) << __func__ << " Failed to find option.";
+    std::move(callback).Run(ClientStatus(OPTION_VALUE_NOT_FOUND));
+    return;
+  }
   std::move(callback).Run(OkClientStatus());
 }
 
@@ -1243,7 +1310,10 @@
   if (!result || result->HasExceptionDetails() ||
       !result->GetResult()->GetValue()->GetBool()) {
     DVLOG(1) << __func__ << " Failed to highlight element.";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS));  // unexpected
+    std::move(callback).Run(JavaScriptErrorStatus(
+        __FILE__, __LINE__,
+        result->HasExceptionDetails() ? result->GetExceptionDetails()
+                                      : nullptr));  // unexpected
     return;
   }
   DCHECK(result->GetResult()->GetValue()->is_bool());
@@ -1445,9 +1515,13 @@
 void WebController::OnSetValueAttribute(
     base::OnceCallback<void(const ClientStatus&)> callback,
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
-  std::move(callback).Run(result && !result->HasExceptionDetails()
-                              ? OkClientStatus()
-                              : ClientStatus(OTHER_ACTION_STATUS));
+  std::move(callback).Run(
+      result && !result->HasExceptionDetails()
+          ? OkClientStatus()
+          : ClientStatus(JavaScriptErrorStatus(
+                __FILE__, __LINE__,
+                result->HasExceptionDetails() ? result->GetExceptionDetails()
+                                              : nullptr)));
 }
 
 void WebController::SetAttribute(
@@ -1505,9 +1579,13 @@
 void WebController::OnSetAttribute(
     base::OnceCallback<void(const ClientStatus&)> callback,
     std::unique_ptr<runtime::CallFunctionOnResult> result) {
-  std::move(callback).Run(result && !result->HasExceptionDetails()
-                              ? OkClientStatus()
-                              : ClientStatus(OTHER_ACTION_STATUS));
+  std::move(callback).Run(
+      result && !result->HasExceptionDetails()
+          ? OkClientStatus()
+          : JavaScriptErrorStatus(__FILE__, __LINE__,
+                                  result->HasExceptionDetails()
+                                      ? result->GetExceptionDetails()
+                                      : nullptr));
 }
 
 void WebController::SendKeyboardInput(
@@ -1651,7 +1729,7 @@
   if (!result || result->HasExceptionDetails() || !result->GetResult() ||
       !result->GetResult()->GetValue()->is_string()) {
     DVLOG(2) << __func__ << " Failed to get HTML content for GetOuterHtml";
-    std::move(callback).Run(ClientStatus(OTHER_ACTION_STATUS), "");
+    std::move(callback).Run(UnexpectedErrorStatus(__FILE__, __LINE__), "");
     return;
   }
 
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc
index 4fe3ff8..26cd2e6 100644
--- a/components/autofill_assistant/browser/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -509,6 +509,45 @@
                         false);
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, InnerTextCondition) {
+  Selector selector({"#with_inner_text span"});
+  RunLaxElementCheck(kVisibilityCheck, selector, true);
+  RunStrictElementCheck(kVisibilityCheck, selector, false);
+
+  // No matches
+  selector.inner_text_pattern = "no match";
+  RunLaxElementCheck(kExistenceCheck, selector, false);
+  RunLaxElementCheck(kVisibilityCheck, selector, false);
+
+  // Matches exactly one visible element.
+  selector.inner_text_pattern = "hello, world";
+  RunLaxElementCheck(kExistenceCheck, selector, true);
+  RunStrictElementCheck(kExistenceCheck, selector, true);
+  RunLaxElementCheck(kVisibilityCheck, selector, true);
+  RunStrictElementCheck(kVisibilityCheck, selector, true);
+
+  // Matches two visible elements
+  selector.inner_text_pattern = "^hello";
+  RunLaxElementCheck(kExistenceCheck, selector, true);
+  RunStrictElementCheck(kExistenceCheck, selector, false);
+  RunLaxElementCheck(kVisibilityCheck, selector, true);
+  RunStrictElementCheck(kVisibilityCheck, selector, false);
+
+  // Matches one visible, one invisible element
+  selector.inner_text_pattern = "world$";
+  RunLaxElementCheck(kExistenceCheck, selector, true);
+  RunStrictElementCheck(kExistenceCheck, selector, false);
+  RunLaxElementCheck(kVisibilityCheck, selector, true);
+  RunStrictElementCheck(kVisibilityCheck, selector, true);
+
+  // Inner text conditions are applied before looking for the pseudo-type.
+  selector.pseudo_type = PseudoType::BEFORE;
+  selector.inner_text_pattern = "world";
+  RunLaxElementCheck(kExistenceCheck, selector, true);
+  selector.inner_text_pattern = "before";  // matches :before content
+  RunLaxElementCheck(kExistenceCheck, selector, false);
+}
+
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
                        ConcurrentElementsVisibilityCheck) {
   std::vector<Selector> selectors;
@@ -735,7 +774,7 @@
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SelectOption) {
   Selector selector;
   selector.selectors.emplace_back("#select");
-  EXPECT_EQ(OTHER_ACTION_STATUS,
+  EXPECT_EQ(OPTION_VALUE_NOT_FOUND,
             SelectOption(selector, "incorrect_label").proto_status());
   EXPECT_EQ(ACTION_APPLIED, SelectOption(selector, "two").proto_status());
 
diff --git a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
index e270678..67db6a6 100644
--- a/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
+++ b/components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.cc
@@ -5,7 +5,6 @@
 #include "components/data_reduction_proxy/content/common/data_reduction_proxy_url_loader_throttle.h"
 
 #include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
 #include "components/data_reduction_proxy/content/common/header_util.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h"
@@ -21,17 +20,6 @@
 
 namespace data_reduction_proxy {
 
-namespace {
-void RecordQuicProxyStatus(const net::ProxyServer& proxy_server) {
-  if (proxy_server.is_https()) {
-    RecordQuicProxyStatus(IsQuicProxy(proxy_server)
-                              ? QUIC_PROXY_STATUS_AVAILABLE
-                              : QUIC_PROXY_NOT_SUPPORTED);
-  }
-}
-
-}  // namespace
-
 DataReductionProxyURLLoaderThrottle::DataReductionProxyURLLoaderThrottle(
     const net::HttpRequestHeaders& post_cache_headers,
     DataReductionProxyThrottleManager* manager)
@@ -90,7 +78,6 @@
 
   before_will_process_response_received_ = true;
   MaybeRetry(proxy_server, response_head.headers.get(), net::OK, defer);
-  RecordQuicProxyStatus(proxy_server);
 }
 
 void DataReductionProxyURLLoaderThrottle::MaybeRetry(
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
index 99a7bd7..367c0b73 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.cc
@@ -15,7 +15,6 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "net/base/host_port_pair.h"
@@ -27,6 +26,12 @@
 
 namespace data_reduction_proxy {
 
+namespace {
+
+static const char kDataReductionCoreProxy[] = "proxy.googlezip.net";
+
+}  // namespace
+
 DataReductionProxyDelegate::DataReductionProxyDelegate(
     DataReductionProxyConfig* config,
     const DataReductionProxyConfigurator* configurator,
@@ -199,7 +204,18 @@
 bool DataReductionProxyDelegate::SupportsQUIC(
     const net::ProxyServer& proxy_server) const {
   DCHECK(thread_checker_.CalledOnValidThread());
-  return IsQuicProxy(proxy_server);
+  // Enable QUIC for whitelisted proxies.
+  return params::IsQuicEnabledForNonCoreProxies() ||
+         proxy_server ==
+             net::ProxyServer(net::ProxyServer::SCHEME_HTTPS,
+                              net::HostPortPair(kDataReductionCoreProxy, 443));
+}
+
+void DataReductionProxyDelegate::RecordQuicProxyStatus(
+    QuicProxyStatus status) const {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.Quic.ProxyStatus", status,
+                            QUIC_PROXY_STATUS_BOUNDARY);
 }
 
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
index 8f5e0c4..14ba5fa 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h
@@ -56,7 +56,20 @@
   // Returns true if |proxy_server| supports QUIC.
   virtual bool SupportsQUIC(const net::ProxyServer& proxy_server) const;
 
+  // Availability status of data reduction QUIC proxy.
+  // Protected so that the enum values are accessible for testing.
+  enum QuicProxyStatus {
+    QUIC_PROXY_STATUS_AVAILABLE,
+    QUIC_PROXY_NOT_SUPPORTED,
+    QUIC_PROXY_STATUS_MARKED_AS_BROKEN,
+    QUIC_PROXY_DISABLED_VIA_FIELD_TRIAL,
+    QUIC_PROXY_STATUS_BOUNDARY
+  };
+
  private:
+  // Records the availability status of data reduction proxy.
+  void RecordQuicProxyStatus(QuicProxyStatus status) const;
+
   // Checks if the first proxy server in |result| supports QUIC and if so
   // adds an alternative proxy configuration to |result|.
   void GetAlternativeProxy(const GURL& url,
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
index c38b9c9..fc1049e 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_delegate_unittest.cc
@@ -33,7 +33,6 @@
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h"
-#include "components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params_test_utils.h"
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h"
@@ -105,19 +104,27 @@
     if (expect_alternative_proxy_server && !broken) {
       histogram_tester.ExpectUniqueSample(
           "DataReductionProxy.Quic.ProxyStatus",
-          QuicProxyStatus::QUIC_PROXY_STATUS_AVAILABLE, 1);
+          TestDataReductionProxyDelegate::QuicProxyStatus::
+              QUIC_PROXY_STATUS_AVAILABLE,
+          1);
     } else if (!supports_quic && !broken) {
       histogram_tester.ExpectUniqueSample(
           "DataReductionProxy.Quic.ProxyStatus",
-          QuicProxyStatus::QUIC_PROXY_NOT_SUPPORTED, 1);
+          TestDataReductionProxyDelegate::QuicProxyStatus::
+              QUIC_PROXY_NOT_SUPPORTED,
+          1);
     } else {
       ASSERT_TRUE(broken);
       histogram_tester.ExpectUniqueSample(
           "DataReductionProxy.Quic.ProxyStatus",
-          QuicProxyStatus::QUIC_PROXY_STATUS_MARKED_AS_BROKEN, 1);
+          TestDataReductionProxyDelegate::QuicProxyStatus::
+              QUIC_PROXY_STATUS_MARKED_AS_BROKEN,
+          1);
     }
   }
 
+  using DataReductionProxyDelegate::QuicProxyStatus;
+
  private:
   const bool proxy_supports_quic_;
 
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.cc
index 4bdb409..6ffe2d4 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.cc
@@ -27,8 +27,6 @@
 
 namespace {
 
-static const char kDataReductionCoreProxy[] = "proxy.googlezip.net";
-
 // Returns the Data Reduction Proxy servers in |proxy_type_info| that should be
 // marked bad according to |data_reduction_proxy_info|.
 std::vector<net::ProxyServer> GetProxiesToMarkBad(
@@ -295,17 +293,4 @@
   return true;
 }
 
-bool IsQuicProxy(const net::ProxyServer& proxy_server) {
-  // Enable QUIC for whitelisted proxies.
-  return params::IsQuicEnabledForNonCoreProxies() ||
-         proxy_server ==
-             net::ProxyServer(net::ProxyServer::SCHEME_HTTPS,
-                              net::HostPortPair(kDataReductionCoreProxy, 443));
-}
-
-void RecordQuicProxyStatus(QuicProxyStatus status) {
-  UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.Quic.ProxyStatus", status,
-                            QUIC_PROXY_STATUS_BOUNDARY);
-}
-
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.h
index b3ba446..d87153bb 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_bypass_protocol.h
@@ -15,15 +15,6 @@
 
 struct DataReductionProxyTypeInfo;
 
-// Availability status of data reduction QUIC proxy.
-enum QuicProxyStatus {
-  QUIC_PROXY_STATUS_AVAILABLE,
-  QUIC_PROXY_NOT_SUPPORTED,
-  QUIC_PROXY_STATUS_MARKED_AS_BROKEN,
-  QUIC_PROXY_DISABLED_VIA_FIELD_TRIAL,
-  QUIC_PROXY_STATUS_BOUNDARY
-};
-
 // Records a data reduction proxy bypass event as a "BlockType" if
 // |bypass_all| is true and as a "BypassType" otherwise. Records the event as
 // "Primary" if |is_primary| is true and "Fallback" otherwise.
@@ -119,11 +110,6 @@
                            base::TimeTicks t,
                            base::TimeDelta* retry_delay);
 
-// Returns true if the proxy supports QUIC.
-bool IsQuicProxy(const net::ProxyServer& proxy_server);
-
-void RecordQuicProxyStatus(QuicProxyStatus status);
-
 }  // namespace data_reduction_proxy
 
 #endif  // COMPONENTS_DATA_REDUCTION_PROXY_CORE_COMMON_DATA_REDUCTION_PROXY_BYPASS_PROTOCOL_H_
diff --git a/components/keyed_service/core/simple_keyed_service_factory.h b/components/keyed_service/core/simple_keyed_service_factory.h
index 18584bb..25ce3654 100644
--- a/components/keyed_service/core/simple_keyed_service_factory.h
+++ b/components/keyed_service/core/simple_keyed_service_factory.h
@@ -24,6 +24,40 @@
 // We do this because services depend on each other and we need to control
 // shutdown/destruction order. In each derived classes' constructors, the
 // implementors must explicitly state on which services they depend.
+//
+// Note:
+// The SimpleKeyedServiceFactory (SKSF) provides a way to create or get a
+// SimpleKeyedService before BrowserContext is created.
+// BrowserContextKeyedServiceFactories (BCKSFs) can only be converted to
+// SKSFs as long as they access only part of Profile properties:
+// path, PrefService, and is_off_the_record flag.
+//
+// An SKSF shouldn't declare DependsOn() of any BCKSF on creation (constructor).
+// It is because an SKSF can't depend on any BCKSF when it is created. However,
+// dependencies from SKSFs to BCKSFs may exist after full browser launches,
+// since some SimpleKeyedServices move from "reduced mode" to "full browser
+// mode" when the full browser starts up, which involves injection of
+// BrowserContextKeyedService dependencies into the SimpleKeyedService.
+//
+// If such dependencies exist in a SimpleKeyedService, the service **MUST**
+// explicitly reset/clean up the dependencies in KeyedService::Shutdown().
+//
+// Once the dependencies are reset, the dependencies from the BCKSF dependency
+// graph to the SKSF dependency graph are removed. Therefore, we adopt a
+// two-phase shutdown:
+// - Shutdown of all BCKSFactories
+// - Shutdown of all SKSFactories
+// - Destruction of all BCKSFactories
+// - Destruction of all SKSFactories
+
+// A SimpleKeyedService should *AVOID* full browser inflation whenever it is
+// possible. A solution might be splitting the part of the service that
+// depends on BrowserContextKeyedService or BrowserContext into a separate
+// BrowserContextKeyedService.
+//
+// See
+// https://docs.google.com/document/d/1caWonaPnBhMb6sk4syNe0BbdsQih13S6QmDW237Mcrg/edit?usp=sharing
+// for more details.
 class KEYED_SERVICE_EXPORT SimpleKeyedServiceFactory
     : public KeyedServiceFactory {
  public:
diff --git a/components/password_manager/core/browser/new_password_form_manager_unittest.cc b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
index 953358f1..556fa88 100644
--- a/components/password_manager/core/browser/new_password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
@@ -420,7 +420,6 @@
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
 
   CreateFormManager(observed_form_);
-  EXPECT_CALL(driver_, AllowPasswordGenerationForForm(_));
   EXPECT_CALL(driver_, FormEligibleForGenerationFound(_)).Times(0);
   PasswordFormFillData fill_data;
   EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
@@ -1269,7 +1268,6 @@
 TEST_F(NewPasswordFormManagerTest, RecordReadonlyWhenFilling) {
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
-  EXPECT_CALL(driver_, AllowPasswordGenerationForForm(_));
   EXPECT_CALL(driver_, FillPasswordForm(_));
   SetNonFederatedAndNotifyFetchCompleted({&saved_match_});
 
diff --git a/components/password_manager/core/browser/password_form_filling_unittest.cc b/components/password_manager/core/browser/password_form_filling_unittest.cc
index 043740a..c0c2eab6 100644
--- a/components/password_manager/core/browser/password_form_filling_unittest.cc
+++ b/components/password_manager/core/browser/password_form_filling_unittest.cc
@@ -97,7 +97,6 @@
 TEST_F(PasswordFormFillingTest, NoSavedCredentials) {
   std::map<base::string16, const autofill::PasswordForm*> best_matches;
 
-  EXPECT_CALL(driver_, AllowPasswordGenerationForForm(observed_form_));
   EXPECT_CALL(driver_, InformNoSavedCredentials());
   EXPECT_CALL(driver_, FillPasswordForm(_)).Times(0);
   EXPECT_CALL(driver_, ShowInitialPasswordAccountSuggestions(_)).Times(0);
@@ -117,8 +116,6 @@
     another_saved_match.password_value += ASCIIToUTF16("1");
     best_matches[another_saved_match.username_value] = &another_saved_match;
 
-    EXPECT_CALL(driver_, AllowPasswordGenerationForForm(observed_form_))
-        .Times(is_blacklisted ? 0 : 1);
     EXPECT_CALL(driver_, InformNoSavedCredentials()).Times(0);
     PasswordFormFillData fill_data;
     EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
@@ -212,7 +209,6 @@
   std::map<base::string16, const autofill::PasswordForm*> best_matches;
   best_matches[saved_match_.username_value] = &psl_saved_match_;
 
-  EXPECT_CALL(driver_, AllowPasswordGenerationForForm(observed_form_));
   EXPECT_CALL(driver_, InformNoSavedCredentials()).Times(0);
   PasswordFormFillData fill_data;
   EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&fill_data));
diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc
index 9f3d248a..973448f 100644
--- a/components/password_manager/core/browser/password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_form_manager_unittest.cc
@@ -398,6 +398,11 @@
     psl_saved_match_.action = GURL("http://m.accounts.google.com/a/Login");
     psl_saved_match_.signon_realm = "http://m.accounts.google.com";
 
+    scoped_feature_list_.InitWithFeatures(
+        /* enabled_features */ {},
+        /*  disabled_features*/ {features::kNewPasswordFormParsing,
+                                 features::kNewPasswordFormParsingForSaving,
+                                 features::kOnlyNewParser});
     password_manager_.reset(new PasswordManager(&client_));
     form_manager_.reset(new PasswordFormManager(
         password_manager_.get(), &client_, client_.driver(), observed_form_,
@@ -1018,6 +1023,7 @@
   // needs to outlive the latter.
   FakeFormFetcher fake_form_fetcher_;
   std::unique_ptr<PasswordFormManager> form_manager_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 class PasswordFormManagerFillOnAccountSelectTest
diff --git a/components/password_manager/core/browser/password_generation_frame_helper_unittest.cc b/components/password_manager/core/browser/password_generation_frame_helper_unittest.cc
index 98df547..8d6f550 100644
--- a/components/password_manager/core/browser/password_generation_frame_helper_unittest.cc
+++ b/components/password_manager/core/browser/password_generation_frame_helper_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/autofill_metrics.h"
@@ -27,6 +28,7 @@
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/browser/stub_password_manager_driver.h"
 #include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/common/password_manager_features.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
@@ -344,6 +346,13 @@
   EXPECT_CALL(*client_, GetPasswordSyncState())
       .WillRepeatedly(testing::Return(SYNCING_NORMAL_ENCRYPTION));
 
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      /* enabled_features */ {},
+      /*  disabled_features*/ {features::kNewPasswordFormParsing,
+                               features::kNewPasswordFormParsingForSaving,
+                               features::kOnlyNewParser});
+
   autofill::FormData login_form;
   login_form.origin = GURL("http://www.yahoo.com/login/");
   autofill::FormFieldData username;
diff --git a/components/password_manager/core/browser/password_manager_unittest.cc b/components/password_manager/core/browser/password_manager_unittest.cc
index 27304f9..7006db1 100644
--- a/components/password_manager/core/browser/password_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_manager_unittest.cc
@@ -265,6 +265,12 @@
         .WillByDefault(WithArg<0>(DeletePtr()));
     ON_CALL(client_, ShowManualFallbackForSavingPtr(_, _, _))
         .WillByDefault(WithArg<0>(DeletePtr()));
+
+    // When waiting for predictions is on, it makes tests more complicated.
+    // Disable waiting, since most tests have nothing to do with predictions.
+    // All tests that test working with prediction should explicitly turn
+    // predictions on.
+    NewPasswordFormManager::set_wait_for_server_predictions_for_filling(false);
   }
 
   void TearDown() override {
@@ -692,8 +698,10 @@
   PasswordForm form(MakeSimpleForm());
   std::vector<PasswordForm> observed = {form};
   EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _))
-      .WillOnce(WithArg<1>(InvokeConsumer(existing_different)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(existing_different)));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
   EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.origin))
@@ -833,8 +841,10 @@
   std::vector<PasswordForm> observed = {form};
   EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.origin))
       .WillRepeatedly(Return(true));
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
   EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
@@ -1161,8 +1171,11 @@
   std::vector<PasswordForm> observed;
   observed.push_back(form);
   EXPECT_CALL(driver_, FillPasswordForm(_));
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the
+  // old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   observed.clear();
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
@@ -1177,8 +1190,10 @@
   std::vector<PasswordForm> observed;
   observed.push_back(form);
   EXPECT_CALL(driver_, FillPasswordForm(_));
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
   manager()->OnPasswordFormsParsed(&driver_, observed);
 }
 
@@ -1526,8 +1541,10 @@
   form.new_password_element = ASCIIToUTF16("new_password_element");
   form.new_password_value.clear();
   observed.push_back(form);
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms()));
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1560,8 +1577,10 @@
   // Loads passsword form without username input field.
   PasswordForm form(MakeSimpleFormWithOnlyPasswordField());
   observed.push_back(form);
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms()));
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1600,11 +1619,9 @@
 TEST_F(PasswordManagerTest, FillPasswordOnManyFrames_SameId) {
   // Setting task runner is required since NewPasswordFormManager uses
   // PostDelayTask for making filling.
+  NewPasswordFormManager::set_wait_for_server_predictions_for_filling(true);
   TestMockTimeTaskRunner::ScopedContext scoped_context_(task_runner_.get());
 
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(features::kNewPasswordFormParsing);
-
   // Two unrelated forms...
   FormData form_data;
   form_data.origin = GURL("http://www.google.com/a/LoginAuth");
@@ -1665,7 +1682,10 @@
 // similar, then it is important to ensure that the single governing
 // PasswordFormManager knows about both PasswordManagerDriver instances and
 // instructs them to fill.
+// TODO(https://crbug.com/949519): Remove this test when the old parser is gone.
 TEST_F(PasswordManagerTest, FillPasswordOnManyFrames_SameForm) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  TurnOnNewParsingForFilling(&scoped_feature_list, false);
   PasswordForm same_form = MakeSimpleForm();
 
   // Observe the form in the first frame.
@@ -1690,7 +1710,7 @@
   PasswordForm form(MakeSimpleForm());
   observed.push_back(form);
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms()));
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1708,7 +1728,10 @@
   EXPECT_CALL(*store_, AddLogin(FormMatches(form)));
   // The Save() call triggers updating for |pending_login_managers_|, hence the
   // further GetLogins call.
-  EXPECT_CALL(*store_, GetLogins(_, _));
+  // There are 2 calls to |store_| because both PasswordFormManager and
+  // NewPasswordFormManager call it. TODO(https://crbug.com/949519): remove
+  // Times(2) when the old parser is gone.
+  EXPECT_CALL(*store_, GetLogins(_, _)).Times(2);
   form_manager_to_save->Save();
 }
 
@@ -1722,8 +1745,10 @@
   PasswordForm blacklisted_form(form);
   blacklisted_form.username_value = ASCIIToUTF16("");
   blacklisted_form.blacklisted_by_user = true;
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(blacklisted_form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(blacklisted_form)));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1892,8 +1917,10 @@
   PasswordForm form(MakeSimpleForm());
   observed.push_back(form);
   EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -1927,7 +1954,10 @@
 
   // GetLogins calls remain unanswered to emulate that PasswordStore did not
   // fetch a form in time before submission.
-  EXPECT_CALL(*store_, GetLogins(_, _));
+  // There are 2 calls to |store_| because both PasswordFormManager and
+  // NewPasswordFormManager call it. TODO(https://crbug.com/949519): remove
+  // Times(2) when the old parser is gone.
+  EXPECT_CALL(*store_, GetLogins(_, _)).Times(2);
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
   ASSERT_EQ(1u, manager()->pending_login_managers().size());
@@ -2365,8 +2395,10 @@
 
   autofill::PasswordFormFillData form_data;
   EXPECT_CALL(driver_, FillPasswordForm(_)).WillOnce(SaveArg<0>(&form_data));
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(android_form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(android_form)));
   manager()->OnPasswordFormsParsed(&driver_, observed_forms);
   observed_forms.clear();
   manager()->OnPasswordFormsRendered(&driver_, observed_forms, true);
@@ -2410,8 +2442,10 @@
   std::vector<PasswordForm> observed_forms = {observed_form};
 
   EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(android_form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(android_form)));
   manager()->OnPasswordFormsParsed(&driver_, observed_forms);
   manager()->OnPasswordFormsRendered(&driver_, observed_forms, true);
 
@@ -2453,7 +2487,9 @@
   std::vector<PasswordForm> observed = {form};
 
   // Emulate page load.
-  EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _));
+  // TODO(https://crbug.com/949519): remove Times(2) when the old parser is
+  // gone.
+  EXPECT_CALL(*store_, GetLogins(PasswordStore::FormDigest(form), _)).Times(2);
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
   ASSERT_EQ(1u, manager()->pending_login_managers().size());
@@ -2549,8 +2585,10 @@
   stored_form.password_value = ASCIIToUTF16("old_password");
   EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.origin))
       .WillRepeatedly(Return(true));
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(stored_form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(stored_form)));
   EXPECT_CALL(driver_, FillPasswordForm(_)).Times(2);
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
@@ -2599,7 +2637,10 @@
   PasswordStoreConsumer* store_consumer = nullptr;
   EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.origin))
       .WillRepeatedly(Return(true));
-  EXPECT_CALL(*store_, GetLogins(_, _)).WillOnce(SaveArg<1>(&store_consumer));
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
+  EXPECT_CALL(*store_, GetLogins(_, _))
+      .WillRepeatedly(SaveArg<1>(&store_consumer));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -2623,8 +2664,10 @@
   observed.push_back(form);
   EXPECT_CALL(client_, IsSavingAndFillingEnabled(form.origin))
       .WillRepeatedly(Return(true));
+  // TODO(https://crbug.com/949519): replace WillRepeatedly with WillOnce when
+  // the old parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeEmptyConsumerWithForms()));
+      .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
   manager()->OnPasswordFormsParsed(&driver_, observed);
   manager()->OnPasswordFormsRendered(&driver_, observed, true);
 
@@ -2852,8 +2895,11 @@
   };
 
   const std::vector<PasswordForm> observed = {PasswordForm()};
-  // PasswordStore requested only once for the same form.
-  EXPECT_CALL(*store_, GetLogins(_, _));
+  // PasswordStore requested only once for the same form by both
+  // PasswordFormManager and NewPasswordFormManager.
+  // TODO(https://crbug.com/949519): remove Times(2) when the old parser is
+  // gone.
+  EXPECT_CALL(*store_, GetLogins(_, _)).Times(2);
 
   for (const auto& test_case : kCases) {
     SCOPED_TRACE(testing::Message("index of test_case = ")
@@ -3216,6 +3262,7 @@
 // Check that when autofill predictions are received before a form is found then
 // server predictions are not ignored and used for filling.
 TEST_F(PasswordManagerTest, AutofillPredictionBeforeFormParsed) {
+  NewPasswordFormManager::set_wait_for_server_predictions_for_filling(true);
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndEnableFeature(features::kNewPasswordFormParsing);
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index 99538db..70386e5 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -62,7 +62,7 @@
 // Enables new password form parsing mechanism for filling passwords, details in
 // https://goo.gl/QodPH1
 const base::Feature kNewPasswordFormParsing = {
-    "new-password-form-parsing", base::FEATURE_DISABLED_BY_DEFAULT};
+    "new-password-form-parsing", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables new password form parsing mechanism for saving passwords, details in
 // https://goo.gl/QodPH1
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index 58f56a3..5a83474 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -370,8 +370,10 @@
   v8::Context::Scope context_scope(context);
   v8::Local<v8::Object> global = context->Global();
 
-  global->Set(gin::StringToV8(isolate, "plugin"),
-              plugin_->delegate_->GetV8Handle(isolate));
+  global
+      ->Set(context, gin::StringToV8(isolate, "plugin"),
+            plugin_->delegate_->GetV8Handle(isolate))
+      .Check();
 }
 
 void WebViewPlugin::WebViewHelper::FrameDetached(DetachType type) {
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index 63cb402a..c6dc10b 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -672,7 +672,9 @@
     std::unique_ptr<DMAuth> auth,
     const CloudPolicyClient::StatusCallback& callback) {
   CHECK(is_registered());
-  DCHECK(auth->has_oauth_token() || auth->has_enrollment_token());
+  // This condition is wrong in case of Attestation enrollment
+  // (https://crbug.com/942013).
+  // DCHECK(auth->has_oauth_token() || auth->has_enrollment_token());
 
   std::unique_ptr<DeviceManagementRequestJob> request_job(service_->CreateJob(
       DeviceManagementRequestJob::TYPE_ATTRIBUTE_UPDATE_PERMISSION,
diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc
index 6210b15..b3cffb2 100644
--- a/components/signin/core/browser/about_signin_internals.cc
+++ b/components/signin/core/browser/about_signin_internals.cc
@@ -414,11 +414,6 @@
   NotifyObservers();
 }
 
-void AboutSigninInternals::OnPrimaryAccountSigninFailed(
-    const GoogleServiceAuthError& error) {
-  NotifyObservers();
-}
-
 void AboutSigninInternals::OnPrimaryAccountSet(
     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 89a1b42a..370e47f 100644
--- a/components/signin/core/browser/about_signin_internals.h
+++ b/components/signin/core/browser/about_signin_internals.h
@@ -203,8 +203,6 @@
   // IdentityManager::Observer implementations.
   void OnRefreshTokensLoaded() override;
   void OnEndBatchOfRefreshTokenStateChanges() override;
-  void OnPrimaryAccountSigninFailed(
-      const GoogleServiceAuthError& error) override;
   void OnPrimaryAccountSet(
       const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index abced09d..e8c81d90 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -2204,9 +2204,9 @@
   }
 
   base::HistogramTester::CountsMap expected_counts;
-  expected_counts["Signin.Reconciler.Duration.Success"] = 1;
+  expected_counts["Signin.Reconciler.Duration.UpTo3mins.Success"] = 1;
   EXPECT_THAT(histogram_tester()->GetTotalCountsForPrefix(
-                  "Signin.Reconciler.Duration.Success"),
+                  "Signin.Reconciler.Duration.UpTo3mins.Success"),
               testing::ContainerEq(expected_counts));
 }
 
@@ -2307,10 +2307,10 @@
   identity_test_env()->ClearPrimaryAccount();
 
   base::HistogramTester::CountsMap expected_counts;
-  expected_counts["Signin.Reconciler.Duration.Failure"] = 1;
+  expected_counts["Signin.Reconciler.Duration.UpTo3mins.Failure"] = 1;
   if (!IsMultiloginEnabled()) {
     EXPECT_THAT(histogram_tester()->GetTotalCountsForPrefix(
-                    "Signin.Reconciler.Duration.Failure"),
+                    "Signin.Reconciler.Duration.UpTo3mins.Failure"),
                 testing::ContainerEq(expected_counts));
   }
 }
diff --git a/components/signin/core/browser/signin_manager.cc b/components/signin/core/browser/signin_manager.cc
index fc683c3..0975933 100644
--- a/components/signin/core/browser/signin_manager.cc
+++ b/components/signin/core/browser/signin_manager.cc
@@ -41,12 +41,6 @@
   local_state_pref_registrar_.RemoveAll();
 }
 
-void SigninManager::HandleAuthError(const GoogleServiceAuthError& error) {
-  if (observer_ != nullptr) {
-    observer_->GoogleSigninFailed(error);
-  }
-}
-
 void SigninManager::SignOut(
     signin_metrics::ProfileSignout signout_source_metric,
     signin_metrics::SignoutDelete signout_delete_metric) {
diff --git a/components/signin/core/browser/signin_manager.h b/components/signin/core/browser/signin_manager.h
index 1b5ebf6..d4a0b78 100644
--- a/components/signin/core/browser/signin_manager.h
+++ b/components/signin/core/browser/signin_manager.h
@@ -40,7 +40,6 @@
 #include "net/cookies/canonical_cookie.h"
 
 class GaiaCookieManagerService;
-class GoogleServiceAuthError;
 class PrefService;
 
 namespace identity {
@@ -142,11 +141,6 @@
   // OAuth2TokenService::Observer:
   void OnRefreshTokensLoaded() override;
 
-  // Called to handle an error from a GAIA auth fetch.  Sets the last error
-  // to |error|, sends out a notification of login failure and clears the
-  // transient signin data.
-  void HandleAuthError(const GoogleServiceAuthError& error);
-
   // Starts the sign out process.
   void StartSignOut(signin_metrics::ProfileSignout signout_source_metric,
                     signin_metrics::SignoutDelete signout_delete_metric,
diff --git a/components/signin/core/browser/signin_manager_base.h b/components/signin/core/browser/signin_manager_base.h
index 5344bd1b..234d1de 100644
--- a/components/signin/core/browser/signin_manager_base.h
+++ b/components/signin/core/browser/signin_manager_base.h
@@ -46,9 +46,6 @@
  public:
   class Observer {
    public:
-    // Called when a user fails to sign into Google services such as sync.
-    virtual void GoogleSigninFailed(const GoogleServiceAuthError& error) {}
-
     // Called when a user signs into Google services such as sync.
     // This method is not called during a reauth.
     virtual void GoogleSigninSucceeded(const AccountInfo& account_info) {}
diff --git a/components/signin/core/browser/signin_manager_unittest.cc b/components/signin/core/browser/signin_manager_unittest.cc
index b5f7b96..f6f77f7 100644
--- a/components/signin/core/browser/signin_manager_unittest.cc
+++ b/components/signin/core/browser/signin_manager_unittest.cc
@@ -38,21 +38,14 @@
 
 class TestSigninManagerObserver : public SigninManagerBase::Observer {
  public:
-  TestSigninManagerObserver()
-      : num_failed_signins_(0), num_successful_signins_(0), num_signouts_(0) {}
+  TestSigninManagerObserver() : num_successful_signins_(0), num_signouts_(0) {}
 
   ~TestSigninManagerObserver() override {}
 
-  int num_failed_signins_;
   int num_successful_signins_;
   int num_signouts_;
 
  private:
-  // SigninManagerBase::Observer:
-  void GoogleSigninFailed(const GoogleServiceAuthError& error) override {
-    num_failed_signins_++;
-  }
-
   void GoogleSigninSucceeded(const AccountInfo& account_info) override {
     num_successful_signins_++;
   }
@@ -136,7 +129,6 @@
 
     // Should go into token service and stop.
     EXPECT_EQ(1, test_observer_.num_successful_signins_);
-    EXPECT_EQ(0, test_observer_.num_failed_signins_);
   }
 
   base::test::ScopedTaskEnvironment task_environment_;
@@ -318,7 +310,6 @@
   std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
   manager_->SignIn("user@gmail.com");
   EXPECT_EQ(1, test_observer_.num_successful_signins_);
-  EXPECT_EQ(0, test_observer_.num_failed_signins_);
   EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
   EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
 }
@@ -332,13 +323,11 @@
   std::string account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
   manager_->SignIn("user@gmail.com");
   EXPECT_EQ(1, test_observer_.num_successful_signins_);
-  EXPECT_EQ(0, test_observer_.num_failed_signins_);
   EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
   EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
 
   manager_->SignIn("user@gmail.com");
   EXPECT_EQ(1, test_observer_.num_successful_signins_);
-  EXPECT_EQ(0, test_observer_.num_failed_signins_);
   EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedAccountInfo().email);
   EXPECT_EQ(account_id, manager_->GetAuthenticatedAccountId());
 }
diff --git a/components/signin/core/browser/signin_metrics.cc b/components/signin/core/browser/signin_metrics.cc
index d2cdbef..3827846 100644
--- a/components/signin/core/browser/signin_metrics.cc
+++ b/components/signin/core/browser/signin_metrics.cc
@@ -697,9 +697,13 @@
 void LogSigninAccountReconciliationDuration(base::TimeDelta duration,
                                             bool successful) {
   if (successful) {
-    UMA_HISTOGRAM_TIMES("Signin.Reconciler.Duration.Success", duration);
+    UMA_HISTOGRAM_CUSTOM_TIMES("Signin.Reconciler.Duration.UpTo3mins.Success",
+                               duration, base::TimeDelta::FromMilliseconds(1),
+                               base::TimeDelta::FromMinutes(3), 100);
   } else {
-    UMA_HISTOGRAM_TIMES("Signin.Reconciler.Duration.Failure", duration);
+    UMA_HISTOGRAM_CUSTOM_TIMES("Signin.Reconciler.Duration.UpTo3mins.Failure",
+                               duration, base::TimeDelta::FromMilliseconds(1),
+                               base::TimeDelta::FromMinutes(3), 100);
   }
 }
 
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 5fdf913c..000c7425 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -472,6 +472,8 @@
     "nigori/nigori_model_type_processor.cc",
     "nigori/nigori_model_type_processor.h",
     "nigori/nigori_sync_bridge.h",
+    "nigori/nigori_sync_bridge_impl.cc",
+    "nigori/nigori_sync_bridge_impl.h",
     "protocol/proto_enum_conversions.cc",
     "protocol/proto_enum_conversions.h",
     "protocol/proto_memory_estimations.cc",
diff --git a/components/sync/engine/sync_engine_switches.cc b/components/sync/engine/sync_engine_switches.cc
index c0d6921..e05efd0 100644
--- a/components/sync/engine/sync_engine_switches.cc
+++ b/components/sync/engine/sync_engine_switches.cc
@@ -23,7 +23,7 @@
 // via scrypt when we receive a remote Nigori node that specifies it as the key
 // derivation method.
 const base::Feature kSyncUseScryptForNewCustomPassphrases{
-    "SyncUseScryptForNewCustomPassphrases", base::FEATURE_DISABLED_BY_DEFAULT};
+    "SyncUseScryptForNewCustomPassphrases", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable USS implementation of Nigori datatype.
 const base::Feature kSyncUSSNigori{"SyncUSSNigori",
diff --git a/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc b/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc
index a3bfcd66..baa6fdd 100644
--- a/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc
+++ b/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc
@@ -1752,9 +1752,9 @@
   VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
   EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
   EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null());
-  VerifyMigratedNigoriWithTimestamp(migration_time,
-                                    PassphraseType::CUSTOM_PASSPHRASE, kNewKey,
-                                    {KeyDerivationParams::CreateForPbkdf2()});
+  VerifyMigratedNigoriWithTimestamp(
+      migration_time, PassphraseType::CUSTOM_PASSPHRASE, kNewKey,
+      {KeyDerivationParams::CreateForScrypt(kScryptSalt)});
 
   // Check that the cryptographer can decrypt the old key.
   sync_pb::EncryptedData old_encrypted;
@@ -1770,8 +1770,9 @@
   keystore_cryptographer.EncryptString("string", &keystore_encrypted);
   EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted));
 
-  // Check the the cryptographer is encrypting with the new key.
-  KeyParams new_key = {KeyDerivationParams::CreateForPbkdf2(), kNewKey};
+  // Check that the cryptographer is encrypting with the new key.
+  KeyParams new_key = {KeyDerivationParams::CreateForScrypt(kScryptSalt),
+                       kNewKey};
   Cryptographer new_cryptographer(GetCryptographer()->encryptor());
   new_cryptographer.AddKey(new_key);
   sync_pb::EncryptedData new_encrypted;
@@ -1783,7 +1784,7 @@
   VerifyRestoreAfterExplicitPaspshrase(
       migration_time, kNewKey, captured_bootstrap_token, captured_nigori_state,
       PassphraseType::CUSTOM_PASSPHRASE,
-      {KeyDerivationParams::CreateForPbkdf2()});
+      {KeyDerivationParams::CreateForScrypt(kScryptSalt)});
 }
 
 // Test that if a client without a keystore key (e.g. one without keystore
@@ -1864,9 +1865,9 @@
   VerifyPassphraseType(PassphraseType::CUSTOM_PASSPHRASE);
   EXPECT_TRUE(encryption_handler()->IsEncryptEverythingEnabled());
   EXPECT_FALSE(encryption_handler()->custom_passphrase_time().is_null());
-  VerifyMigratedNigoriWithTimestamp(migration_time,
-                                    PassphraseType::CUSTOM_PASSPHRASE, kNewKey,
-                                    {KeyDerivationParams::CreateForPbkdf2()});
+  VerifyMigratedNigoriWithTimestamp(
+      migration_time, PassphraseType::CUSTOM_PASSPHRASE, kNewKey,
+      {KeyDerivationParams::CreateForScrypt(kScryptSalt)});
 
   // Check that the cryptographer can decrypt the old key.
   sync_pb::EncryptedData old_encrypted;
@@ -1881,8 +1882,9 @@
   keystore_cryptographer.EncryptString("string", &keystore_encrypted);
   EXPECT_TRUE(GetCryptographer()->CanDecrypt(keystore_encrypted));
 
-  // Check the the cryptographer is encrypting with the new key.
-  KeyParams new_key = {KeyDerivationParams::CreateForPbkdf2(), kNewKey};
+  // Check that the cryptographer is encrypting with the new key.
+  KeyParams new_key = {KeyDerivationParams::CreateForScrypt(kScryptSalt),
+                       kNewKey};
   Cryptographer new_cryptographer(GetCryptographer()->encryptor());
   new_cryptographer.AddKey(new_key);
   sync_pb::EncryptedData new_encrypted;
@@ -1894,7 +1896,7 @@
   VerifyRestoreAfterExplicitPaspshrase(
       migration_time, kNewKey, captured_bootstrap_token, captured_nigori_state,
       PassphraseType::CUSTOM_PASSPHRASE,
-      {KeyDerivationParams::CreateForPbkdf2()});
+      {KeyDerivationParams::CreateForScrypt(kScryptSalt)});
 }
 
 // Test that if a client without a keystore key (e.g. one without keystore
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc
new file mode 100644
index 0000000..2481db8
--- /dev/null
+++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -0,0 +1,99 @@
+// 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/sync/nigori/nigori_sync_bridge_impl.h"
+
+namespace syncer {
+
+NigoriSyncBridgeImpl::NigoriSyncBridgeImpl() = default;
+
+NigoriSyncBridgeImpl::~NigoriSyncBridgeImpl() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+void NigoriSyncBridgeImpl::AddObserver(Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+void NigoriSyncBridgeImpl::RemoveObserver(Observer* observer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+void NigoriSyncBridgeImpl::Init() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+void NigoriSyncBridgeImpl::SetEncryptionPassphrase(
+    const std::string& passphrase) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+void NigoriSyncBridgeImpl::SetDecryptionPassphrase(
+    const std::string& passphrase) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+void NigoriSyncBridgeImpl::EnableEncryptEverything() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+bool NigoriSyncBridgeImpl::IsEncryptEverythingEnabled() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool NigoriSyncBridgeImpl::NeedKeystoreKey() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool NigoriSyncBridgeImpl::SetKeystoreKeys(
+    const std::vector<std::string>& keys) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+  return false;
+}
+
+base::Optional<ModelError> NigoriSyncBridgeImpl::MergeSyncData(
+    const base::Optional<EntityData>& data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+  return base::nullopt;
+}
+
+base::Optional<ModelError> NigoriSyncBridgeImpl::ApplySyncChanges(
+    const EntityData& data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+  return base::nullopt;
+}
+
+std::unique_ptr<EntityData> NigoriSyncBridgeImpl::GetData() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+  return nullptr;
+}
+
+ConflictResolution NigoriSyncBridgeImpl::ResolveConflict(
+    const EntityData& local_data,
+    const EntityData& remote_data) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+  return ConflictResolution::UseLocal();
+}
+
+void NigoriSyncBridgeImpl::ApplyDisableSyncChanges() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  NOTIMPLEMENTED();
+}
+
+}  // namespace syncer
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.h b/components/sync/nigori/nigori_sync_bridge_impl.h
new file mode 100644
index 0000000..f52a676
--- /dev/null
+++ b/components/sync/nigori/nigori_sync_bridge_impl.h
@@ -0,0 +1,68 @@
+// 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_SYNC_NIGORI_NIGORI_SYNC_BRIDGE_IMPL_H_
+#define COMPONENTS_SYNC_NIGORI_NIGORI_SYNC_BRIDGE_IMPL_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/optional.h"
+#include "base/sequence_checker.h"
+#include "components/sync/engine/sync_encryption_handler.h"
+#include "components/sync/model/conflict_resolution.h"
+#include "components/sync/model/model_error.h"
+#include "components/sync/nigori/keystore_keys_handler.h"
+#include "components/sync/nigori/nigori_sync_bridge.h"
+
+namespace syncer {
+
+// USS implementation of SyncEncryptionHandler.
+// This class holds the current Nigori state and processes incoming changes and
+// queries:
+// 1. Serves observers of SyncEncryptionHandler interface.
+// 2. Allows the passphrase manipulations (via SyncEncryptionHandler).
+// 3. Communicates local and remote changes with a processor (via
+// NigoriSyncBridge).
+// 4. Handles keystore keys from a sync server (via KeystoreKeysHandler).
+class NigoriSyncBridgeImpl : public KeystoreKeysHandler,
+                             public NigoriSyncBridge,
+                             public SyncEncryptionHandler {
+ public:
+  NigoriSyncBridgeImpl();
+  ~NigoriSyncBridgeImpl() override;
+
+  // SyncEncryptionHandler implementation.
+  void AddObserver(Observer* observer) override;
+  void RemoveObserver(Observer* observer) override;
+  void Init() override;
+  void SetEncryptionPassphrase(const std::string& passphrase) override;
+  void SetDecryptionPassphrase(const std::string& passphrase) override;
+  void EnableEncryptEverything() override;
+  bool IsEncryptEverythingEnabled() const override;
+
+  // KeystoreKeysHandler implementation.
+  bool NeedKeystoreKey() const override;
+  bool SetKeystoreKeys(const std::vector<std::string>& keys) override;
+
+  // NigoriSyncBridge implementation.
+  base::Optional<ModelError> MergeSyncData(
+      const base::Optional<EntityData>& data) override;
+  base::Optional<ModelError> ApplySyncChanges(const EntityData& data) override;
+  std::unique_ptr<EntityData> GetData() override;
+  ConflictResolution ResolveConflict(const EntityData& local_data,
+                                     const EntityData& remote_data) override;
+  void ApplyDisableSyncChanges() override;
+
+ private:
+  SEQUENCE_CHECKER(sequence_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(NigoriSyncBridgeImpl);
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_NIGORI_NIGORI_SYNC_BRIDGE_IMPL_H_
diff --git a/components/sync_preferences/pref_model_associator.cc b/components/sync_preferences/pref_model_associator.cc
index 1acf64c..d3e8110 100644
--- a/components/sync_preferences/pref_model_associator.cc
+++ b/components/sync_preferences/pref_model_associator.cc
@@ -169,14 +169,6 @@
   // we'll send the new user controlled value to the syncer.
 }
 
-void PrefModelAssociator::RegisterMergeDataFinishedCallback(
-    const base::Closure& callback) {
-  if (!models_associated_)
-    callback_list_.push_back(callback);
-  else
-    callback.Run();
-}
-
 void PrefModelAssociator::WaitUntilReadyToSync(base::OnceClosure done) {
   // Prefs are loaded very early during profile initialization.
   DCHECK_NE(pref_service_->GetAllPrefStoresInitializationStatus(),
@@ -229,10 +221,6 @@
   if (merge_result.error().IsSet())
     return merge_result;
 
-  for (const auto& callback : callback_list_)
-    callback.Run();
-  callback_list_.clear();
-
   models_associated_ = true;
   pref_service_->OnIsSyncingChanged();
   return merge_result;
diff --git a/components/sync_preferences/pref_model_associator.h b/components/sync_preferences/pref_model_associator.h
index ebf59e4..4271095d 100644
--- a/components/sync_preferences/pref_model_associator.h
+++ b/components/sync_preferences/pref_model_associator.h
@@ -5,12 +5,10 @@
 #ifndef COMPONENTS_SYNC_PREFERENCES_PREF_MODEL_ASSOCIATOR_H_
 #define COMPONENTS_SYNC_PREFERENCES_PREF_MODEL_ASSOCIATOR_H_
 
-#include <map>
 #include <memory>
 #include <set>
 #include <string>
 #include <unordered_map>
-#include <vector>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
@@ -119,10 +117,6 @@
   // Returns the PrefModelAssociatorClient for this object.
   const PrefModelAssociatorClient* client() const { return client_; }
 
-  // Register callback method which will get called at the end of
-  // PrefModelAssociator::MergeDataAndStartSyncing().
-  void RegisterMergeDataFinishedCallback(const base::Closure& callback);
-
  private:
   friend class PrefServiceSyncableTest;
 
@@ -202,8 +196,6 @@
 
   const PrefModelAssociatorClient* client_;  // Weak.
 
-  std::vector<base::Closure> callback_list_;
-
   SEQUENCE_CHECKER(sequence_checker_);
 
   DISALLOW_COPY_AND_ASSIGN(PrefModelAssociator);
diff --git a/components/sync_preferences/pref_service_syncable.cc b/components/sync_preferences/pref_service_syncable.cc
index e95aa297..7cdc8ed 100644
--- a/components/sync_preferences/pref_service_syncable.cc
+++ b/components/sync_preferences/pref_service_syncable.cc
@@ -175,11 +175,6 @@
   priority_pref_sync_associator_.RemoveSyncedPrefObserver(name, observer);
 }
 
-void PrefServiceSyncable::RegisterMergeDataFinishedCallback(
-    const base::Closure& callback) {
-  pref_sync_associator_.RegisterMergeDataFinishedCallback(callback);
-}
-
 void PrefServiceSyncable::AddRegisteredSyncablePreference(
     const std::string& path,
     uint32_t flags) {
diff --git a/components/sync_preferences/pref_service_syncable.h b/components/sync_preferences/pref_service_syncable.h
index b5bc127..85e3384 100644
--- a/components/sync_preferences/pref_service_syncable.h
+++ b/components/sync_preferences/pref_service_syncable.h
@@ -78,8 +78,6 @@
   void AddObserver(PrefServiceSyncableObserver* observer);
   void RemoveObserver(PrefServiceSyncableObserver* observer);
 
-  void RegisterMergeDataFinishedCallback(const base::Closure& callback);
-
   // TODO(zea): Have PrefServiceSyncable implement
   // syncer::SyncableService directly.
   syncer::SyncableService* GetSyncableService(const syncer::ModelType& type);
diff --git a/components/sync_preferences/pref_service_syncable_unittest.cc b/components/sync_preferences/pref_service_syncable_unittest.cc
index e792149..85fda24 100644
--- a/components/sync_preferences/pref_service_syncable_unittest.cc
+++ b/components/sync_preferences/pref_service_syncable_unittest.cc
@@ -57,10 +57,6 @@
 const char kNonDefaultCharsetValue[] = "foo";
 const char kDefaultCharsetValue[] = "utf-8";
 
-void Increment(int* num) {
-  (*num)++;
-}
-
 class TestSyncProcessorStub : public syncer::SyncChangeProcessor {
  public:
   explicit TestSyncProcessorStub(syncer::SyncChangeList* output)
@@ -909,17 +905,6 @@
   EXPECT_TRUE(pref->IsDefaultValue());
 }
 
-TEST_F(PrefServiceSyncableTest, RegisterMergeDataFinishedCallback) {
-  int num_callbacks = 0;
-
-  prefs_.RegisterMergeDataFinishedCallback(
-      base::Bind(&Increment, &num_callbacks));
-  EXPECT_EQ(0, num_callbacks);
-
-  InitWithNoSyncData();
-  EXPECT_EQ(1, num_callbacks);
-}
-
 }  // namespace
 
 }  // namespace sync_preferences
diff --git a/components/test/data/autofill_assistant/autofill_assistant_target_website.html b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
index e8ef87b..6c995ab 100644
--- a/components/test/data/autofill_assistant/autofill_assistant_target_website.html
+++ b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
@@ -60,6 +60,10 @@
           content: "before";
       }
 
+      #with_inner_text span::before {
+          content: "before";
+      }
+
       #button::before {
           content: "before";
           display: none;
@@ -188,5 +192,11 @@
         upperCaseInput.value = upperCaseInput.value.toUpperCase();
       });
     </script>
+
+    <div id="with_inner_text">
+      <span>hello</span>
+      <span>hello, world</span>
+      <span style="display: none">world</span>
+    </div>
   </body>
 </html>
diff --git a/components/viz/common/resources/resource_format_utils.cc b/components/viz/common/resources/resource_format_utils.cc
index f7852c4..548c20d2 100644
--- a/components/viz/common/resources/resource_format_utils.cc
+++ b/components/viz/common/resources/resource_format_utils.cc
@@ -36,8 +36,8 @@
     case LUMINANCE_8:
       return kGray_8_SkColorType;
     case RGBX_8888:
-      return kRGB_888x_SkColorType;
     case ETC1:
+      return kRGB_888x_SkColorType;
     case RED_8:
     case LUMINANCE_F16:
     case R16_EXT:
@@ -275,11 +275,11 @@
     case R16_EXT:
       return GL_R16_EXT;
     case RGBX_8888:
+    case ETC1:
       return GL_RGB8_OES;
     case RGBX_1010102:
       return GL_RGB10_A2_EXT;
     case BGR_565:
-    case ETC1:
     case BGRX_8888:
     case BGRX_1010102:
     case YVU_420:
diff --git a/components/viz/host/host_frame_sink_manager_unittest.cc b/components/viz/host/host_frame_sink_manager_unittest.cc
index 9fcb12a7..34403f5 100644
--- a/components/viz/host/host_frame_sink_manager_unittest.cc
+++ b/components/viz/host/host_frame_sink_manager_unittest.cc
@@ -16,6 +16,7 @@
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "components/viz/test/compositor_frame_helpers.h"
 #include "components/viz/test/fake_host_frame_sink_client.h"
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 8392dc6..090d82cd 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -164,8 +164,6 @@
     "surfaces/surface_deadline_client.h",
     "surfaces/surface_dependency_deadline.cc",
     "surfaces/surface_dependency_deadline.h",
-    "surfaces/surface_dependency_tracker.cc",
-    "surfaces/surface_dependency_tracker.h",
     "surfaces/surface_hittest.cc",
     "surfaces/surface_hittest.h",
     "surfaces/surface_hittest_delegate.h",
diff --git a/components/viz/service/display/display.h b/components/viz/service/display/display.h
index 9323237..4c77f44 100644
--- a/components/viz/service/display/display.h
+++ b/components/viz/service/display/display.h
@@ -23,6 +23,7 @@
 #include "components/viz/service/display/software_output_device_client.h"
 #include "components/viz/service/display/surface_aggregator.h"
 #include "components/viz/service/surfaces/latest_local_surface_id_lookup_delegate.h"
+#include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "components/viz/service/viz_service_export.h"
 #include "gpu/command_buffer/common/texture_in_use_response.h"
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 59c3c89..54b2c14 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -123,6 +123,7 @@
   sk_sp<SkImage> MakePromiseSkImage(SkiaOutputSurfaceImpl* impl) {
     SkColorType color_type = ResourceFormatToClosestSkColorType(
         true /* gpu_compositing */, resource_format_);
+    impl->CreateFallbackPromiseImage(color_type);
     GrBackendFormat backend_format = impl->GetGrBackendFormatForTexture(
         resource_format_,
         render_pass_id_ ? GL_TEXTURE_2D : mailbox_holder_.texture_target);
@@ -285,6 +286,7 @@
       renderer_settings_(renderer_settings),
       weak_ptr_factory_(this) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  seen_resource_formats_.resize(kLastEnum_SkColorType + 1);
 }
 
 SkiaOutputSurfaceImpl::~SkiaOutputSurfaceImpl() {
@@ -771,4 +773,14 @@
   }
 }
 
+void SkiaOutputSurfaceImpl::CreateFallbackPromiseImage(SkColorType color_type) {
+  if (seen_resource_formats_[color_type])
+    return;
+  seen_resource_formats_[color_type] = true;
+  auto callback =
+      base::BindOnce(&SkiaOutputSurfaceImplOnGpu::CreateFallbackPromiseImage,
+                     base::Unretained(impl_on_gpu_.get()), color_type);
+  ScheduleGpuTask(std::move(callback), std::vector<gpu::SyncToken>());
+}
+
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index 74a70ac..7334d12 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -128,6 +128,8 @@
   GrBackendFormat GetGrBackendFormatForTexture(ResourceFormat resource_format,
                                                uint32_t gl_texture_target);
 
+  void CreateFallbackPromiseImage(SkColorType color_type);
+
   uint64_t sync_fence_release_ = 0;
 
   GpuServiceImpl* const gpu_service_;
@@ -172,6 +174,8 @@
   // Observers for context lost.
   base::ObserverList<ContextLostObserver>::Unchecked observers_;
 
+  std::vector<bool> seen_resource_formats_;
+
   THREAD_CHECKER(thread_checker_);
 
   base::WeakPtr<SkiaOutputSurfaceImpl> weak_ptr_;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index eae8a86..91e70c5 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -12,6 +12,7 @@
 #include "base/synchronization/waitable_event.h"
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/frame_sinks/copy_output_util.h"
+#include "components/viz/common/resources/resource_format_utils.h"
 #include "components/viz/common/skia_helper.h"
 #include "components/viz/service/display/gl_renderer_copier.h"
 #include "components/viz/service/display/output_surface_frame.h"
@@ -284,6 +285,7 @@
       context_lost_callback_(context_lost_callback),
       weak_ptr_factory_(this) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  fallback_promise_image_texture_.resize(kLastEnum_SkColorType + 1);
   weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
 }
 
@@ -646,6 +648,13 @@
   }
 }
 
+sk_sp<SkPromiseImageTexture> SkiaOutputSurfaceImplOnGpu::FallbackPromiseImage(
+    ResourceFormat format) {
+  SkColorType color_type =
+      ResourceFormatToClosestSkColorType(true /* gpu_compositing */, format);
+  return fallback_promise_image_texture_[color_type];
+}
+
 sk_sp<SkPromiseImageTexture> SkiaOutputSurfaceImplOnGpu::FulfillPromiseTexture(
     const gpu::MailboxHolder& mailbox_holder,
     const gfx::Size& size,
@@ -660,12 +669,12 @@
     if (!shared_image) {
       DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
                      "mailbox not found in SharedImageManager.";
-      return nullptr;
+      return FallbackPromiseImage(resource_format);
     }
     if (!(shared_image->usage() & gpu::SHARED_IMAGE_USAGE_DISPLAY)) {
       DLOG(ERROR) << "Failed to fulfill the promise texture - SharedImage "
                      "was not created with display usage.";
-      return nullptr;
+      return FallbackPromiseImage(resource_format);
     }
     *shared_image_out = std::move(shared_image);
   }
@@ -681,13 +690,13 @@
     // Probably this texture is created with wrong inteface (GLES2Interface).
     DLOG(ERROR) << "Failed to fulfill the promise texture whose backend is not "
                    "compitable with vulkan.";
-    return nullptr;
+    return FallbackPromiseImage(resource_format);
   }
 
   auto* texture_base = mailbox_manager_->ConsumeTexture(mailbox_holder.mailbox);
   if (!texture_base) {
     DLOG(ERROR) << "Failed to fulfill the promise texture.";
-    return nullptr;
+    return FallbackPromiseImage(resource_format);
   }
   BindOrCopyTextureIfNecessary(texture_base);
   GrBackendTexture backend_texture;
@@ -696,7 +705,7 @@
                            &backend_texture);
   if (!backend_texture.isValid()) {
     DLOG(ERROR) << "Failed to fulfill the promise texture.";
-    return nullptr;
+    return FallbackPromiseImage(resource_format);
   }
   return SkPromiseImageTexture::Make(backend_texture);
 }
@@ -740,6 +749,29 @@
   ReleaseFenceSyncAndPushTextureUpdates(sync_fence_release);
 }
 
+void SkiaOutputSurfaceImplOnGpu::CreateFallbackPromiseImage(
+    SkColorType color_type) {
+  MakeCurrent(false /* need_fbo0 */);
+
+  auto image_info = SkImageInfo::Make(1 /* width */, 1 /* height */, color_type,
+                                      kOpaque_SkAlphaType);
+  auto surface = SkSurface::MakeRenderTarget(
+      gr_context(), SkBudgeted::kNo, image_info, 0 /* sampleCount */,
+      kTopLeft_GrSurfaceOrigin, nullptr /* surfaceProps */);
+  DCHECK(!!surface);
+  auto* canvas = surface->getCanvas();
+#if DCHECK_IS_ON()
+  canvas->clear(SK_ColorRED);
+#else
+  canvas->clear(SK_ColorWHITE);
+#endif
+  fallback_promise_images_.push_back(surface->makeImageSnapshot());
+  auto gr_texture = fallback_promise_images_.back()->getBackendTexture(
+      false /* flushPendingGrContextIO */);
+  fallback_promise_image_texture_[color_type] =
+      SkPromiseImageTexture::Make(gr_texture);
+}
+
 void SkiaOutputSurfaceImplOnGpu::SetCapabilitiesForTesting(
     const OutputSurface::Capabilities& capabilities) {
   MakeCurrent(false /* need_fbo0 */);
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index 01a910e..d5ceea4 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -154,6 +154,8 @@
   void DestroySkImages(std::vector<sk_sp<SkImage>>&& images,
                        uint64_t sync_fence_release);
 
+  void CreateFallbackPromiseImage(SkColorType color_type);
+
   bool was_context_lost() { return context_state_->context_lost(); }
 
   class ScopedUseContextProvider;
@@ -187,6 +189,8 @@
     return output_device_->draw_surface();
   }
 
+  sk_sp<SkPromiseImageTexture> FallbackPromiseImage(ResourceFormat format);
+
   const gpu::SurfaceHandle surface_handle_;
   scoped_refptr<gpu::gles2::FeatureInfo> feature_info_;
   gpu::MailboxManager* const mailbox_manager_;
@@ -247,6 +251,11 @@
   gl::GLApi* api_ = nullptr;
   bool supports_alpha_ = false;
 
+  // What we display when the mailbox for a texture is invalid. Indexed by
+  // SkColorType.
+  std::vector<sk_sp<SkPromiseImageTexture>> fallback_promise_image_texture_;
+  std::vector<sk_sp<SkImage>> fallback_promise_images_;
+
   THREAD_CHECKER(thread_checker_);
 
   base::WeakPtr<SkiaOutputSurfaceImplOnGpu> weak_ptr_;
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index 6824d9d..d365085 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/viz/common/surfaces/surface_info.h"
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "components/viz/service/surfaces/surface.h"
 #include "components/viz/test/begin_frame_args_test.h"
 #include "components/viz/test/compositor_frame_helpers.h"
 #include "components/viz/test/fake_compositor_frame_sink_client.h"
diff --git a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
index d23cb76..9955da0 100644
--- a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
+++ b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -8,6 +8,7 @@
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_allocation_group.h"
 #include "components/viz/test/begin_frame_args_test.h"
 #include "components/viz/test/compositor_frame_helpers.h"
@@ -412,7 +413,7 @@
 
   parent_support().SubmitCompositorFrame(
       parent_id.local_surface_id(),
-      MakeCompositorFrame({child_id2}, empty_surface_ranges(),
+      MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)},
                           std::vector<TransferableResource>()));
 
   // parent_support is blocked on |child_id2|.
@@ -425,7 +426,7 @@
   // child_support1 should now be blocked on |child_id2|.
   child_support1().SubmitCompositorFrame(
       child_id1.local_surface_id(),
-      MakeCompositorFrame({child_id2}, empty_surface_ranges(),
+      MakeCompositorFrame({child_id2}, {SurfaceRange(base::nullopt, child_id2)},
                           std::vector<TransferableResource>()));
 
   EXPECT_TRUE(child_surface1()->has_deadline());
@@ -3138,9 +3139,7 @@
   EXPECT_TRUE(child_surface2->has_deadline());
 
   // |child_id3| Surface should activate immediately because it corresponds to a
-  // parent-initiated synchronization event. |child_surface3| activating
-  // triggers all predecessors to activate as well if they're blocked on a
-  // parent.
+  // parent-initiated synchronization event.
   child_support1().SubmitCompositorFrame(child_id3.local_surface_id(),
                                          MakeDefaultCompositorFrame());
   Surface* child_surface3 = GetSurfaceForId(child_id3);
@@ -3148,12 +3147,6 @@
   EXPECT_FALSE(child_surface3->HasPendingFrame());
   EXPECT_TRUE(child_surface3->HasActiveFrame());
   EXPECT_FALSE(IsMarkedForDestruction(child_id3));
-
-  // |child_surface2| should have activated now (and should be a candidate for
-  // garbage collection).
-  EXPECT_FALSE(child_surface2->HasPendingFrame());
-  EXPECT_TRUE(child_surface2->HasActiveFrame());
-  EXPECT_TRUE(IsMarkedForDestruction(child_id2));
 }
 
 TEST_F(SurfaceSynchronizationTest, EvictSurface) {
@@ -3528,4 +3521,58 @@
   EXPECT_TRUE(surface_manager()->GetAllocationGroupForSurfaceId(child_id2));
 }
 
+// This test verifies that the child gets unthrottled when the parent embeds the
+// second last surface. https://crbug.com/898460
+TEST_F(SurfaceSynchronizationTest,
+       ChildUnthrottledWhenSecondLastSurfaceEmbedded) {
+  const SurfaceId parent_id = MakeSurfaceId(kParentFrameSink, 1);
+  const SurfaceId child_id1 = MakeSurfaceId(kChildFrameSink1, 1, 1);
+  const SurfaceId child_id2 = MakeSurfaceId(kChildFrameSink1, 1, 2);
+  const SurfaceId child_id3 = MakeSurfaceId(kChildFrameSink1, 1, 3);
+
+  // |child_id1| Surface should immediately activate because one unembedded
+  // surface is allowed.
+  child_support1().SubmitCompositorFrame(child_id1.local_surface_id(),
+                                         MakeDefaultCompositorFrame());
+  Surface* child_surface1 = GetSurfaceForId(child_id1);
+  ASSERT_NE(nullptr, child_surface1);
+  EXPECT_FALSE(child_surface1->HasPendingFrame());
+  EXPECT_TRUE(child_surface1->HasActiveFrame());
+
+  // |child_id2| Surface should not activate because now there are two surfaces
+  // not embedded by the parent makes child throttling kick in.
+  child_support1().SubmitCompositorFrame(
+      child_id2.local_surface_id(),
+      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+                          std::vector<TransferableResource>(),
+                          MakeDeadline(1u)));
+  Surface* child_surface2 = GetSurfaceForId(child_id2);
+  ASSERT_NE(nullptr, child_surface2);
+  EXPECT_TRUE(child_surface2->HasPendingFrame());
+  EXPECT_FALSE(child_surface2->HasActiveFrame());
+  EXPECT_TRUE(child_surface2->has_deadline());
+
+  // The parent embeds |child_id1| and blocks. Both |child_id2| should activate
+  // because now again there is only one surface not embedded by the parent.
+  parent_support().SubmitCompositorFrame(
+      parent_id.local_surface_id(),
+      MakeCompositorFrame({child_id1}, {SurfaceRange(base::nullopt, child_id1)},
+                          std::vector<TransferableResource>(),
+                          MakeDefaultDeadline()));
+  EXPECT_FALSE(child_surface2->HasPendingFrame());
+  EXPECT_TRUE(child_surface2->HasActiveFrame());
+
+  // The child submits to |child_id3|. Now again we have two unembedded surface
+  // so throttling should kick in.
+  child_support1().SubmitCompositorFrame(
+      child_id3.local_surface_id(),
+      MakeCompositorFrame(empty_surface_ids(), empty_surface_ranges(),
+                          std::vector<TransferableResource>(),
+                          MakeDeadline(1u)));
+  Surface* child_surface3 = GetSurfaceForId(child_id3);
+  ASSERT_NE(nullptr, child_surface3);
+  EXPECT_TRUE(child_surface3->HasPendingFrame());
+  EXPECT_FALSE(child_surface3->HasActiveFrame());
+}
+
 }  // namespace viz
diff --git a/components/viz/service/frame_sinks/video_detector.cc b/components/viz/service/frame_sinks/video_detector.cc
index 8f560c3af..b84e9b2 100644
--- a/components/viz/service/frame_sinks/video_detector.cc
+++ b/components/viz/service/frame_sinks/video_detector.cc
@@ -6,6 +6,7 @@
 
 #include "base/time/time.h"
 #include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/rect.h"
diff --git a/components/viz/service/hit_test/hit_test_aggregator.h b/components/viz/service/hit_test/hit_test_aggregator.h
index 3d09c668..203ff01 100644
--- a/components/viz/service/hit_test/hit_test_aggregator.h
+++ b/components/viz/service/hit_test/hit_test_aggregator.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_VIZ_SERVICE_HIT_TEST_HIT_TEST_AGGREGATOR_H_
 
 #include "components/viz/common/hit_test/aggregated_hit_test_region.h"
+#include "components/viz/common/quads/render_pass.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "components/viz/service/hit_test/hit_test_manager.h"
 #include "components/viz/service/surfaces/surface_observer.h"
diff --git a/components/viz/service/hit_test/hit_test_manager.cc b/components/viz/service/hit_test/hit_test_manager.cc
index ce7e72b7..8c2239d 100644
--- a/components/viz/service/hit_test/hit_test_manager.cc
+++ b/components/viz/service/hit_test/hit_test_manager.cc
@@ -6,6 +6,7 @@
 
 #include "components/viz/common/hit_test/aggregated_hit_test_region.h"
 #include "components/viz/service/surfaces/latest_local_surface_id_lookup_delegate.h"
+#include "components/viz/service/surfaces/surface.h"
 
 namespace viz {
 
diff --git a/components/viz/service/surfaces/surface.cc b/components/viz/service/surfaces/surface.cc
index 81b583ba..01a6440 100644
--- a/components/viz/service/surfaces/surface.cc
+++ b/components/viz/service/surfaces/surface.cc
@@ -57,6 +57,8 @@
   // it references.
   for (SurfaceAllocationGroup* group : referenced_allocation_groups_)
     group->UnregisterActiveEmbedder(this);
+  for (SurfaceAllocationGroup* group : blocking_allocation_groups_)
+    group->UnregisterBlockedEmbedder(this, false /* did_activate */);
 
   DCHECK(deadline_);
   deadline_->Cancel();
@@ -196,8 +198,6 @@
   if (!activation_dependencies_.empty() || !pending_frame_data_)
     return;
 
-  DCHECK(frame_sink_id_dependencies_.empty());
-
   // All blockers have been cleared. The surface can be activated now.
   ActivatePendingFrame(base::nullopt);
 }
@@ -241,9 +241,7 @@
   if (!seen_first_surface_dependency_) {
     // We should not throttle this client if there is another client blocked on
     // it, in order to avoid deadlocks.
-    seen_first_surface_dependency_ =
-        surface_manager_->dependency_tracker()->HasSurfaceBlockedOn(
-            surface_id().frame_sink_id());
+    seen_first_surface_dependency_ = allocation_group_->HasBlockedEmbedder();
   }
 
   bool block_activation =
@@ -259,17 +257,9 @@
         FrameData(std::move(frame), frame_index, std::move(presented_callback));
     RejectCompositorFramesToFallbackSurfaces();
 
-    // Ask SurfaceDependencyTracker to inform |this| when it is embedded.
-    if (block_activation)
-      surface_manager_->dependency_tracker()->TrackEmbedding(this);
-
     // If the deadline is in the past, then the CompositorFrame will activate
     // immediately.
-    if (deadline_->Set(ResolveFrameDeadline(pending_frame_data_->frame))) {
-      // Ask the SurfaceDependencyTracker to inform |this| when its dependencies
-      // are resolved.
-      surface_manager_->dependency_tracker()->RequestSurfaceResolution(this);
-    }
+    deadline_->Set(ResolveFrameDeadline(pending_frame_data_->frame));
   }
 
   // Returns resources for the previous pending frame.
@@ -302,61 +292,16 @@
   copy_requests.push_back(std::move(copy_request));
 }
 
-void Surface::NotifySurfaceIdAvailable(const SurfaceId& surface_id) {
-  auto it = frame_sink_id_dependencies_.find(surface_id.frame_sink_id());
-  if (it == frame_sink_id_dependencies_.end())
-    return;
-
-  if (surface_id.local_surface_id().parent_sequence_number() >
-          it->second.parent_sequence_number ||
-      surface_id.local_surface_id().child_sequence_number() >
-          it->second.child_sequence_number ||
-      (surface_id.local_surface_id().parent_sequence_number() ==
-           it->second.parent_sequence_number &&
-       surface_id.local_surface_id().child_sequence_number() ==
-           it->second.child_sequence_number)) {
-    frame_sink_id_dependencies_.erase(it);
-    surface_manager_->SurfaceDependenciesChanged(this, {},
-                                                 {surface_id.frame_sink_id()});
-  }
-
-  // TODO(fsamuel): This is a linear scan which is probably fine today because
-  // a given surface has a small number of dependencies. We might need to
-  // revisit this in the future if the number of dependencies grows
-  // significantly.
-  auto delete_fn = [surface_id](const SurfaceId& dependency) {
-    if (dependency.frame_sink_id() != surface_id.frame_sink_id())
-      return false;
-    // The dependency will never get satisfied if the child is already using a
-    // larger parent or child sequence number, so drop the dependency in that
-    // case.
-    if (dependency.local_surface_id().parent_sequence_number() <
-            surface_id.local_surface_id().parent_sequence_number() ||
-        dependency.local_surface_id().child_sequence_number() <
-            surface_id.local_surface_id().child_sequence_number()) {
-      return true;
-    }
-    // For the dependency to get satisfied, both parent and child sequence
-    // numbers of the activated SurfaceId must be equal to those of the
-    // dependency.
-    return dependency.local_surface_id().parent_sequence_number() ==
-               surface_id.local_surface_id().parent_sequence_number() &&
-           dependency.local_surface_id().child_sequence_number() ==
-               surface_id.local_surface_id().child_sequence_number();
-  };
-
-  base::EraseIf(activation_dependencies_, delete_fn);
-
-  // We cannot activate this CompositorFrame if there are still missing
-  // activation dependencies or this surface is blocked on its parent arriving
-  // and the parent has not arrived yet.
+void Surface::OnActivationDependencyResolved(
+    const SurfaceId& activation_dependency,
+    SurfaceAllocationGroup* group) {
+  DCHECK(activation_dependencies_.count(activation_dependency));
+  activation_dependencies_.erase(activation_dependency);
+  blocking_allocation_groups_.erase(group);
   bool block_activation =
       block_activation_on_parent_ && !seen_first_surface_dependency_;
   if (block_activation || !activation_dependencies_.empty())
     return;
-
-  DCHECK(frame_sink_id_dependencies_.empty());
-
   // All blockers have been cleared. The surface can be activated now.
   ActivatePendingFrame(base::nullopt);
 }
@@ -369,7 +314,12 @@
   // If a frame is being activated because of a deadline, then clear its set
   // of blockers.
   activation_dependencies_.clear();
-  frame_sink_id_dependencies_.clear();
+
+  // We treat an activation (by deadline) as being the equivalent of a parent
+  // embedding the surface in order to avoid blocking future frames to the same
+  // surface.
+  seen_first_surface_dependency_ = true;
+
   ActivatePendingFrame(duration);
 }
 
@@ -440,7 +390,7 @@
             surface_range.end());
     if (end_allocation_group) {
       new_referenced_allocation_groups.push_back(end_allocation_group);
-      end_allocation_group->UpdateLastReferencedSurfaceAndMaybeActivate(
+      end_allocation_group->UpdateLastActiveReferenceAndMaybeActivate(
           surface_range.end());
     }
     // Only reference the allocation group for the start of SurfaceRange if the
@@ -453,7 +403,7 @@
               *surface_range.start());
       if (start_allocation_group) {
         new_referenced_allocation_groups.push_back(start_allocation_group);
-        start_allocation_group->UpdateLastReferencedSurfaceAndMaybeActivate(
+        start_allocation_group->UpdateLastActiveReferenceAndMaybeActivate(
             *surface_range.start());
       }
     }
@@ -487,6 +437,12 @@
 
   active_frame_data_ = std::move(frame_data);
 
+  // We no longer have a pending frame, so unregister self from
+  // |blocking_allocation_groups_|.
+  for (SurfaceAllocationGroup* group : blocking_allocation_groups_)
+    group->UnregisterBlockedEmbedder(this, true /* did_activate */);
+  blocking_allocation_groups_.clear();
+
   RecomputeActiveReferencedSurfaces();
 
   for (auto& copy_request : old_copy_requests)
@@ -531,7 +487,7 @@
   // active frame already, active this frame immediately so we have something to
   // show.
   if (!HasActiveFrame() &&
-      allocation_group_->GetLastReferencedSurfaceId().IsSameOrNewerThan(
+      allocation_group_->GetLastActiveReference().IsSameOrNewerThan(
           surface_id())) {
     return FrameDeadline::MakeZero();
   }
@@ -560,76 +516,41 @@
 
 void Surface::UpdateActivationDependencies(
     const CompositorFrame& current_frame) {
-  base::flat_map<FrameSinkId, SequenceNumbers> new_frame_sink_id_dependencies;
-  base::flat_set<SurfaceId> new_activation_dependencies;
+  for (SurfaceAllocationGroup* group : blocking_allocation_groups_)
+    group->UnregisterBlockedEmbedder(this, false /* did_activate */);
+  blocking_allocation_groups_.clear();
+  activation_dependencies_.clear();
 
+  std::vector<SurfaceAllocationGroup*> new_blocking_allocation_groups;
+  std::vector<SurfaceId> new_activation_dependencies;
   for (const SurfaceId& surface_id :
        current_frame.metadata.activation_dependencies) {
-    // Inform the Surface |dependency| that it's been added as a dependency in
-    // another Surface's CompositorFrame.
-    surface_manager_->SurfaceDependencyAdded(surface_id);
-
+    SurfaceAllocationGroup* group =
+        surface_manager_->GetOrCreateAllocationGroupForSurfaceId(surface_id);
+    if (group)
+      group->UpdateLastPendingReferenceAndMaybeActivate(surface_id);
     Surface* dependency = surface_manager_->GetSurfaceForId(surface_id);
-
-    // If a activation dependency does not have a corresponding active frame in
-    // the display compositor, then it blocks this frame.
-    if (!dependency || !dependency->HasActiveFrame()) {
-      new_activation_dependencies.insert(surface_id);
-      TRACE_EVENT_WITH_FLOW2(
-          TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
-          "LocalSurfaceId.Embed.Flow",
-          TRACE_ID_GLOBAL(surface_id.local_surface_id().embed_trace_id()),
-          TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
-          "AddedActivationDependency", "child_surface_id",
-          surface_id.ToString());
-
-      // Record the latest |parent_sequence_number| this surface is interested
-      // in observing for the provided FrameSinkId.
-      uint32_t& parent_sequence_number =
-          new_frame_sink_id_dependencies[surface_id.frame_sink_id()]
-              .parent_sequence_number;
-      parent_sequence_number =
-          std::max(parent_sequence_number,
-                   surface_id.local_surface_id().parent_sequence_number());
-
-      uint32_t& child_sequence_number =
-          new_frame_sink_id_dependencies[surface_id.frame_sink_id()]
-              .child_sequence_number;
-      child_sequence_number =
-          std::max(child_sequence_number,
-                   surface_id.local_surface_id().child_sequence_number());
+    if (dependency && dependency->HasActiveFrame()) {
+      // Normally every creation of SurfaceAllocationGroup should be followed by
+      // a call to Register* to keep it alive. However, since this one already
+      // has a registered surface, we don't have to do that.
+      DCHECK(!group->IsReadyToDestroy());
+      continue;
     }
+    if (group) {
+      group->RegisterBlockedEmbedder(this, surface_id);
+      new_blocking_allocation_groups.push_back(group);
+    }
+    TRACE_EVENT_WITH_FLOW2(
+        TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
+        "LocalSurfaceId.Embed.Flow",
+        TRACE_ID_GLOBAL(surface_id.local_surface_id().embed_trace_id()),
+        TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
+        "AddedActivationDependency", "child_surface_id", surface_id.ToString());
+    new_activation_dependencies.push_back(surface_id);
   }
-
-  // If this Surface has a previous pending frame, then we must determine the
-  // changes in dependencies so that we can update the SurfaceDependencyTracker
-  // map.
-  ComputeChangeInDependencies(new_frame_sink_id_dependencies);
-
   activation_dependencies_ = std::move(new_activation_dependencies);
-  frame_sink_id_dependencies_ = std::move(new_frame_sink_id_dependencies);
-}
-
-void Surface::ComputeChangeInDependencies(
-    const base::flat_map<FrameSinkId, SequenceNumbers>& new_dependencies) {
-  base::flat_set<FrameSinkId> added_dependencies;
-  base::flat_set<FrameSinkId> removed_dependencies;
-
-  for (const auto& kv : frame_sink_id_dependencies_) {
-    if (!new_dependencies.count(kv.first))
-      removed_dependencies.insert(kv.first);
-  }
-
-  for (const auto& kv : new_dependencies) {
-    if (!frame_sink_id_dependencies_.count(kv.first))
-      added_dependencies.insert(kv.first);
-  }
-
-  // If there is a change in the dependency set, then inform SurfaceManager.
-  if (!added_dependencies.empty() || !removed_dependencies.empty()) {
-    surface_manager_->SurfaceDependenciesChanged(this, added_dependencies,
-                                                 removed_dependencies);
-  }
+  blocking_allocation_groups_ = std::move(new_blocking_allocation_groups);
 }
 
 void Surface::TakeCopyOutputRequests(Surface::CopyRequestsMap* copy_requests) {
@@ -824,4 +745,17 @@
   ActivatePendingFrameForDeadline(base::nullopt);
 }
 
+void Surface::ResetBlockActivationOnParent() {
+  if (!block_activation_on_parent_)
+    return;
+
+  block_activation_on_parent_ = false;
+
+  if (!activation_dependencies_.empty() || !pending_frame_data_)
+    return;
+
+  // All blockers have been cleared. The surface can be activated now.
+  ActivatePendingFrame(base::nullopt);
+}
+
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface.h b/components/viz/service/surfaces/surface.h
index 1d779af..2f8d332 100644
--- a/components/viz/service/surfaces/surface.h
+++ b/components/viz/service/surfaces/surface.h
@@ -229,12 +229,16 @@
   // is already taken.
   bool is_latency_info_taken() { return is_latency_info_taken_; }
 
- private:
-  struct SequenceNumbers {
-    uint32_t parent_sequence_number = 0u;
-    uint32_t child_sequence_number = 0u;
-  };
+  // Called by a blocking SurfaceAllocationGroup when |activation_dependency|
+  // is resolved. |this| will be automatically unregistered from |group|, the
+  // SurfaceAllocationGroup corresponding to |activation_dependency|.
+  void OnActivationDependencyResolved(const SurfaceId& activation_dependency,
+                                      SurfaceAllocationGroup* group);
 
+  // Called when this surface's activation no longer has to block on the parent.
+  void ResetBlockActivationOnParent();
+
+ private:
   struct FrameData {
     FrameData(CompositorFrame&& frame,
               uint64_t frame_index,
@@ -292,9 +296,6 @@
   // dependencies will be added even if they're not yet available.
   void UpdateActivationDependencies(const CompositorFrame& current_frame);
 
-  void ComputeChangeInDependencies(
-      const base::flat_map<FrameSinkId, SequenceNumbers>& new_dependencies);
-
   void UnrefFrameResourcesAndRunCallbacks(base::Optional<FrameData> frame_data);
   void ClearCopyRequests();
 
@@ -318,18 +319,7 @@
   bool seen_first_surface_embedding_ = false;
   bool seen_first_surface_dependency_ = false;
   const bool needs_sync_tokens_;
-  const bool block_activation_on_parent_;
-
-  base::flat_set<SurfaceId> activation_dependencies_;
-
-  // A map from FrameSinkIds of SurfaceIds that this surface depends on for
-  // activation to the latest local_id associated with the given FrameSinkId
-  // that this surface is dependent on. This map is used to determine which
-  // FrameSinkIds this surface would like to observe activations for. Once
-  // the latest activated SurfaceId associated with the given FrameSinkId
-  // passes the local_id in the map, then this surface is no longer interested
-  // in observing activations for that FrameSinkId.
-  base::flat_map<FrameSinkId, SequenceNumbers> frame_sink_id_dependencies_;
+  bool block_activation_on_parent_ = false;
 
   // A set of all valid SurfaceIds contained |last_surface_id_for_range_| to
   // avoid recompution.
@@ -344,6 +334,16 @@
   // Allocation groups that this surface references by its active frame.
   base::flat_set<SurfaceAllocationGroup*> referenced_allocation_groups_;
 
+  // The set of the SurfaceIds that are blocking the pending frame from being
+  // activated.
+  base::flat_set<SurfaceId> activation_dependencies_;
+
+  // The SurfaceAllocationGroups corresponding to the surfaces in
+  // |activation_dependencies_|. When an activation dependency is
+  // resolved, the corresponding SurfaceAllocationGroup will call back into this
+  // surface to let us know.
+  base::flat_set<SurfaceAllocationGroup*> blocking_allocation_groups_;
+
   bool is_latency_info_taken_ = false;
 
   SurfaceAllocationGroup* const allocation_group_;
diff --git a/components/viz/service/surfaces/surface_allocation_group.cc b/components/viz/service/surfaces/surface_allocation_group.cc
index 7e67c9f..6e60ce22 100644
--- a/components/viz/service/surfaces/surface_allocation_group.cc
+++ b/components/viz/service/surfaces/surface_allocation_group.cc
@@ -17,10 +17,15 @@
       embed_token_(embed_token),
       surface_manager_(surface_manager) {}
 
-SurfaceAllocationGroup::~SurfaceAllocationGroup() = default;
+SurfaceAllocationGroup::~SurfaceAllocationGroup() {
+  DCHECK(surfaces_.empty());
+  DCHECK(active_embedders_.empty());
+  DCHECK(blocked_embedders_.empty());
+}
 
 bool SurfaceAllocationGroup::IsReadyToDestroy() const {
-  return surfaces_.empty() && active_embedders_.empty();
+  return surfaces_.empty() && active_embedders_.empty() &&
+         blocked_embedders_.empty();
 }
 
 void SurfaceAllocationGroup::RegisterSurface(Surface* surface) {
@@ -39,6 +44,27 @@
   MaybeMarkForDestruction();
 }
 
+void SurfaceAllocationGroup::RegisterBlockedEmbedder(
+    Surface* surface,
+    const SurfaceId& activation_dependency) {
+  blocked_embedders_[surface] = activation_dependency;
+}
+
+void SurfaceAllocationGroup::UnregisterBlockedEmbedder(Surface* surface,
+                                                       bool did_activate) {
+  DCHECK(blocked_embedders_.count(surface));
+  blocked_embedders_.erase(surface);
+  // If the pending frame activated, don't notify SurfaceManager that this
+  // allocation group needs to be destroyed, because the embedder will soon
+  // call RegisterActiveEmbedder.
+  if (!did_activate)
+    MaybeMarkForDestruction();
+}
+
+bool SurfaceAllocationGroup::HasBlockedEmbedder() const {
+  return !blocked_embedders_.empty();
+}
+
 void SurfaceAllocationGroup::RegisterActiveEmbedder(Surface* surface) {
   DCHECK(!active_embedders_.count(surface));
   active_embedders_.insert(surface);
@@ -50,22 +76,32 @@
   MaybeMarkForDestruction();
 }
 
-void SurfaceAllocationGroup::UpdateLastReferencedSurfaceAndMaybeActivate(
+void SurfaceAllocationGroup::UpdateLastActiveReferenceAndMaybeActivate(
     const SurfaceId& surface_id) {
   DCHECK_EQ(submitter_, surface_id.frame_sink_id());
   DCHECK_EQ(embed_token_, surface_id.local_surface_id().embed_token());
-  if (last_referenced_surface_id_.is_valid() &&
-      last_referenced_surface_id_.IsSameOrNewerThan(surface_id)) {
+  if (last_active_reference_.is_valid() &&
+      last_active_reference_.IsSameOrNewerThan(surface_id)) {
     return;
   }
-  last_referenced_surface_id_ = surface_id;
+  last_active_reference_ = surface_id;
   auto it = FindLatestSurfaceUpTo(surface_id);
   if (it != surfaces_.end() && !(*it)->HasActiveFrame())
     (*it)->ActivatePendingFrameForInheritedDeadline();
+  UpdateLastReferenceAndMaybeActivate(surface_id);
 }
 
-const SurfaceId& SurfaceAllocationGroup::GetLastReferencedSurfaceId() {
-  return last_referenced_surface_id_;
+void SurfaceAllocationGroup::UpdateLastPendingReferenceAndMaybeActivate(
+    const SurfaceId& surface_id) {
+  UpdateLastReferenceAndMaybeActivate(surface_id);
+}
+
+const SurfaceId& SurfaceAllocationGroup::GetLastActiveReference() {
+  return last_active_reference_;
+}
+
+const SurfaceId& SurfaceAllocationGroup::GetLastReference() {
+  return last_reference_;
 }
 
 Surface* SurfaceAllocationGroup::FindLatestActiveSurfaceInRange(
@@ -121,6 +157,23 @@
 void SurfaceAllocationGroup::OnFirstSurfaceActivation(Surface* surface) {
   for (Surface* embedder : active_embedders_)
     embedder->OnChildActivatedForActiveFrame(surface->surface_id());
+  base::flat_map<Surface*, SurfaceId> embedders_to_notify;
+  for (const auto& entry : blocked_embedders_) {
+    if (!entry.second.IsNewerThan(surface->surface_id()))
+      embedders_to_notify[entry.first] = entry.second;
+  }
+  for (const auto& entry : embedders_to_notify)
+    blocked_embedders_.erase(entry.first);
+  for (const auto& entry : embedders_to_notify)
+    entry.first->OnActivationDependencyResolved(entry.second, this);
+}
+
+void SurfaceAllocationGroup::WillNotRegisterNewSurfaces() {
+  base::flat_map<Surface*, SurfaceId> embedders = std::move(blocked_embedders_);
+  blocked_embedders_.clear();
+  for (const auto& entry : embedders) {
+    entry.first->OnActivationDependencyResolved(entry.second, this);
+  }
 }
 
 std::vector<Surface*>::const_iterator
@@ -178,4 +231,21 @@
     surface_manager_->SetAllocationGroupsNeedGarbageCollection();
 }
 
+void SurfaceAllocationGroup::UpdateLastReferenceAndMaybeActivate(
+    const SurfaceId& surface_id) {
+  if (last_reference_.IsSameOrNewerThan(surface_id))
+    return;
+  last_reference_ = surface_id;
+  if (surfaces_.empty())
+    return;
+  auto it = FindLatestSurfaceUpTo(surface_id);
+  if (it == surfaces_.end())
+    return;
+  (*it)->OnSurfaceDependencyAdded();
+  ++it;
+  if (it == surfaces_.end())
+    return;
+  (*it)->ResetBlockActivationOnParent();
+}
+
 }  // namespace viz
diff --git a/components/viz/service/surfaces/surface_allocation_group.h b/components/viz/service/surfaces/surface_allocation_group.h
index cd36f27..f88acdf 100644
--- a/components/viz/service/surfaces/surface_allocation_group.h
+++ b/components/viz/service/surfaces/surface_allocation_group.h
@@ -51,6 +51,21 @@
   // allocation group.
   void UnregisterSurface(Surface* surface);
 
+  // Called by |surface| when it has a pending frame that is blocked on
+  // |activation_dependency| in this allocation group. The embedder will be
+  // notified when |activation_dependency| becomes available.
+  void RegisterBlockedEmbedder(Surface* surface,
+                               const SurfaceId& activation_dependency);
+
+  // Called by |surface| when its pending frame that still has an unresolved
+  // activation dependency in this allocation group either activates
+  // (|did_activate| == true) or gets dropped (|did_activate| == false).
+  void UnregisterBlockedEmbedder(Surface* surface, bool did_activate);
+
+  // Returns whether there is any embedder that is blocked on a surface in this
+  // allocation group.
+  bool HasBlockedEmbedder() const;
+
   // Called by |surface| when its newly activated frame references a surface in
   // this allocation group. The embedder will be notified whenever a surface in
   // this allocation group activates for the first time.
@@ -60,14 +75,23 @@
   // surface in this allocation group.
   void UnregisterActiveEmbedder(Surface* surface);
 
-  // Called by an active embedder when its CompositorFrame references a surface
-  // in this allocation group. |surface_id| or the last surface prior to it will
-  // be forcefully activated due to deadline inheritance.
-  void UpdateLastReferencedSurfaceAndMaybeActivate(const SurfaceId& surface_id);
+  // Notifies that a surface exists whose active frame references |surface_id|
+  // in this allocation group. |surface_id| or the last surface prior to it may
+  // be activated due to deadline inheritance.
+  void UpdateLastActiveReferenceAndMaybeActivate(const SurfaceId& surface_id);
+
+  // Notifies that a surface exists whose pending frame references |surface_id|
+  // in this allocation group. |surface_id| or some surface prior to it might
+  // activate if it was blocked due to child throttling.
+  void UpdateLastPendingReferenceAndMaybeActivate(const SurfaceId& surface_id);
 
   // Returns the last SurfaceId in this allocation group that was ever
-  // referenced.
-  const SurfaceId& GetLastReferencedSurfaceId();
+  // referenced by the active frame of a surface.
+  const SurfaceId& GetLastActiveReference();
+
+  // Returns the last SurfaceId in this allocation group that was ever
+  // referenced by a pending or an active frame of a surface.
+  const SurfaceId& GetLastReference();
 
   // Returns the latest active surface in the given range that is a part of this
   // allocation group. The embed token of at least one end of the range must
@@ -84,6 +108,11 @@
   // time.
   void OnFirstSurfaceActivation(Surface* surface);
 
+  // Called when there will not be any calls to RegisterSurface in the future.
+  // All pending embedders that were blocked on surfaces that don't exist yet
+  // will have their dependency resolved.
+  void WillNotRegisterNewSurfaces();
+
   // Returns the last surface created in this allocation group.
   Surface* last_created_surface() const {
     return surfaces_.empty() ? nullptr : surfaces_.back();
@@ -105,6 +134,10 @@
   // (see IsReadyToDestroy() for the requirements).
   void MaybeMarkForDestruction();
 
+  // Updates the last reference. |surface_id| or a surface prior to it might
+  // activate if it was blocked due to child throttling.
+  void UpdateLastReferenceAndMaybeActivate(const SurfaceId& surface_id);
+
   // The ID of the FrameSink that is submitting to the surfaces in this
   // allocation group.
   const FrameSinkId submitter_;
@@ -119,6 +152,10 @@
   // increasing.
   std::vector<Surface*> surfaces_;
 
+  // A map from the surfaces that have an unresolved activation dependency in
+  // this allocation group, to the said activation dependency.
+  base::flat_map<Surface*, SurfaceId> blocked_embedders_;
+
   // The set of surfaces that reference a surface in this allocation group by
   // their active frame.
   base::flat_set<Surface*> active_embedders_;
@@ -127,9 +164,13 @@
   // ready to be destroyed.
   SurfaceManager* const surface_manager_;
 
-  // The last SurfaceId of this allocation group that was ever referenced by an
-  // active embedder.
-  SurfaceId last_referenced_surface_id_;
+  // The last SurfaceId of this allocation group that was ever referenced by the
+  // active frame of a surface.
+  SurfaceId last_active_reference_;
+
+  // The last SurfaceId of this allocation group that was ever referenced by the
+  // active or pending frame of a surface.
+  SurfaceId last_reference_;
 
   DISALLOW_COPY_AND_ASSIGN(SurfaceAllocationGroup);
 };
diff --git a/components/viz/service/surfaces/surface_dependency_tracker.cc b/components/viz/service/surfaces/surface_dependency_tracker.cc
deleted file mode 100644
index 0f06d6e..0000000
--- a/components/viz/service/surfaces/surface_dependency_tracker.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2016 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/viz/service/surfaces/surface_dependency_tracker.h"
-
-#include "build/build_config.h"
-#include "components/viz/common/surfaces/surface_info.h"
-#include "components/viz/service/surfaces/surface.h"
-#include "components/viz/service/surfaces/surface_manager.h"
-
-namespace viz {
-
-SurfaceDependencyTracker::SurfaceDependencyTracker(
-    SurfaceManager* surface_manager)
-    : surface_manager_(surface_manager) {}
-
-SurfaceDependencyTracker::~SurfaceDependencyTracker() = default;
-
-void SurfaceDependencyTracker::TrackEmbedding(Surface* surface) {
-  // If |surface| is blocking on the arrival of a parent and the parent frame
-  // has not yet arrived then track this |surface|'s SurfaceId by FrameSinkId so
-  // that if a parent refers to it or a more recent surface, then
-  // SurfaceDependencyTracker reports back that a dependency has been added.
-  if (surface->block_activation_on_parent() && !surface->HasDependentFrame()) {
-    surfaces_blocked_on_parent_by_frame_sink_id_[surface->surface_id()
-                                                     .frame_sink_id()]
-        .insert(surface->surface_id());
-  }
-}
-
-void SurfaceDependencyTracker::RequestSurfaceResolution(Surface* surface) {
-  DCHECK(surface->HasPendingFrame());
-
-  // Activation dependencies that aren't currently known to the surface manager
-  // or do not have an active CompositorFrame block this frame.
-  for (const SurfaceId& surface_id : surface->activation_dependencies()) {
-    Surface* dependency = surface_manager_->GetSurfaceForId(surface_id);
-    if (!dependency || !dependency->HasActiveFrame()) {
-      blocked_surfaces_from_dependency_[surface_id.frame_sink_id()].insert(
-          surface->surface_id());
-    }
-  }
-}
-
-bool SurfaceDependencyTracker::HasSurfaceBlockedOn(
-    const FrameSinkId& frame_sink_id) const {
-  auto it = blocked_surfaces_from_dependency_.find(frame_sink_id);
-  DCHECK(it == blocked_surfaces_from_dependency_.end() || !it->second.empty());
-  return it != blocked_surfaces_from_dependency_.end();
-}
-
-void SurfaceDependencyTracker::OnSurfaceActivated(Surface* surface) {
-  NotifySurfaceIdAvailable(surface->surface_id());
-  // We treat an activation (by deadline) as being the equivalent of a parent
-  // embedding the surface.
-  OnSurfaceDependencyAdded(surface->surface_id());
-}
-
-void SurfaceDependencyTracker::OnSurfaceDependencyAdded(
-    const SurfaceId& surface_id) {
-  auto it = surfaces_blocked_on_parent_by_frame_sink_id_.find(
-      surface_id.frame_sink_id());
-  if (it == surfaces_blocked_on_parent_by_frame_sink_id_.end())
-    return;
-
-  std::vector<SurfaceId> dependencies_to_notify;
-
-  base::flat_set<SurfaceId>& blocked_surfaces = it->second;
-  for (auto iter = blocked_surfaces.begin(); iter != blocked_surfaces.end();) {
-    bool should_notify =
-        iter->local_surface_id() <= surface_id.local_surface_id();
-#if defined(OS_ANDROID)
-    // On Android we work around a throttling bug by also firing if the
-    // immediately preceding surface has a dependency added.
-    // TODO(https://crbug.com/898460): Solve this generally.
-    bool is_same_parent =
-        iter->local_surface_id().parent_sequence_number() ==
-        surface_id.local_surface_id().parent_sequence_number();
-    bool is_next_child =
-        iter->local_surface_id().child_sequence_number() ==
-        surface_id.local_surface_id().child_sequence_number() + 1;
-    should_notify |= is_same_parent && is_next_child;
-#endif
-    if (should_notify) {
-      dependencies_to_notify.push_back(*iter);
-      iter = blocked_surfaces.erase(iter);
-    } else {
-      ++iter;
-    }
-  }
-
-  if (blocked_surfaces.empty())
-    surfaces_blocked_on_parent_by_frame_sink_id_.erase(it);
-
-  for (const SurfaceId& dependency : dependencies_to_notify) {
-    Surface* surface = surface_manager_->GetSurfaceForId(dependency);
-    if (surface)
-      surface->OnSurfaceDependencyAdded();
-  }
-}
-
-void SurfaceDependencyTracker::OnSurfaceDependenciesChanged(
-    Surface* surface,
-    const base::flat_set<FrameSinkId>& added_dependencies,
-    const base::flat_set<FrameSinkId>& removed_dependencies) {
-  // Update the |blocked_surfaces_from_dependency_| map with the changes in
-  // dependencies.
-  for (const FrameSinkId& frame_sink_id : added_dependencies) {
-    blocked_surfaces_from_dependency_[frame_sink_id].insert(
-        surface->surface_id());
-  }
-
-  for (const FrameSinkId& frame_sink_id : removed_dependencies) {
-    auto it = blocked_surfaces_from_dependency_.find(frame_sink_id);
-    if (it != blocked_surfaces_from_dependency_.end()) {
-      it->second.erase(surface->surface_id());
-      if (it->second.empty())
-        blocked_surfaces_from_dependency_.erase(it);
-    }
-  }
-}
-
-void SurfaceDependencyTracker::OnSurfaceDiscarded(Surface* surface) {
-  base::flat_set<FrameSinkId> removed_dependencies;
-  for (const SurfaceId& surface_id : surface->activation_dependencies())
-    removed_dependencies.insert(surface_id.frame_sink_id());
-
-  OnSurfaceDependenciesChanged(surface, {}, removed_dependencies);
-
-  // Pretend that the discarded surface's SurfaceId is now available to
-  // unblock dependencies because we now know the surface will never activate.
-  NotifySurfaceIdAvailable(surface->surface_id());
-  OnSurfaceDependencyAdded(surface->surface_id());
-}
-
-void SurfaceDependencyTracker::OnFrameSinkInvalidated(
-    const FrameSinkId& frame_sink_id) {
-  // We now know the frame sink will never generated any more frames,
-  // thus unblock all dependencies to any future surfaces.
-  NotifySurfaceIdAvailable(SurfaceId::MaxSequenceId(frame_sink_id));
-  OnSurfaceDependencyAdded(SurfaceId::MaxSequenceId(frame_sink_id));
-}
-
-void SurfaceDependencyTracker::NotifySurfaceIdAvailable(
-    const SurfaceId& surface_id) {
-  auto it = blocked_surfaces_from_dependency_.find(surface_id.frame_sink_id());
-  if (it == blocked_surfaces_from_dependency_.end())
-    return;
-
-  // Unblock surfaces that depend on this |surface_id|.
-  base::flat_set<SurfaceId> blocked_surfaces_by_id(it->second);
-
-  // Tell each surface about the availability of its blocker.
-  for (const SurfaceId& blocked_surface_by_id : blocked_surfaces_by_id) {
-    Surface* blocked_surface =
-        surface_manager_->GetSurfaceForId(blocked_surface_by_id);
-    if (!blocked_surface) {
-      // A blocked surface may have been garbage collected during dependency
-      // resolution.
-      continue;
-    }
-    blocked_surface->NotifySurfaceIdAvailable(surface_id);
-  }
-}
-
-}  // namespace viz
diff --git a/components/viz/service/surfaces/surface_dependency_tracker.h b/components/viz/service/surfaces/surface_dependency_tracker.h
deleted file mode 100644
index eacb163..0000000
--- a/components/viz/service/surfaces/surface_dependency_tracker.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2017 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_VIZ_SERVICE_SURFACES_SURFACE_DEPENDENCY_TRACKER_H_
-#define COMPONENTS_VIZ_SERVICE_SURFACES_SURFACE_DEPENDENCY_TRACKER_H_
-
-#include "components/viz/service/surfaces/surface.h"
-#include "components/viz/service/viz_service_export.h"
-
-namespace viz {
-
-class SurfaceManager;
-
-// SurfaceDependencyTracker tracks unresolved dependencies blocking
-// CompositorFrames from activating. This class maintains a map from
-// a dependent surface ID to a set of Surfaces that have CompositorFrames
-// blocked on that surface ID. SurfaceDependencyTracker observes when
-// dependent frames activate, and informs blocked surfaces.
-//
-// When a blocking CompositorFrame is first submitted,
-// SurfaceDependencyTracker will begin listening for BeginFrames, setting a
-// deadline some number of BeginFrames in the future. If there are unresolved
-// dependencies when the deadline hits, then SurfaceDependencyTracker will clear
-// then and activate all pending CompositorFrames. Once there are no more
-// remaining pending frames, then SurfaceDependencyTracker will stop observing
-// BeginFrames.
-class VIZ_SERVICE_EXPORT SurfaceDependencyTracker {
- public:
-  explicit SurfaceDependencyTracker(SurfaceManager* surface_manager);
-  ~SurfaceDependencyTracker();
-
-  // Called when |surface| wishes to track when it is embedded.
-  void TrackEmbedding(Surface* surface);
-
-  // Called when |surface| has a pending CompositorFrame and it wishes to be
-  // informed when that surface's dependencies are resolved.
-  void RequestSurfaceResolution(Surface* surface);
-
-  // Returns whether the dependency tracker has a surface blocked on the
-  // provided |frame_sink_id|.
-  bool HasSurfaceBlockedOn(const FrameSinkId& frame_sink_id) const;
-
-  void OnSurfaceActivated(Surface* surface);
-  void OnSurfaceDependencyAdded(const SurfaceId& surface_id);
-  void OnSurfaceDependenciesChanged(
-      Surface* surface,
-      const base::flat_set<FrameSinkId>& added_dependencies,
-      const base::flat_set<FrameSinkId>& removed_dependencies);
-  void OnSurfaceDiscarded(Surface* surface);
-  void OnFrameSinkInvalidated(const FrameSinkId& frame_sink_id);
-
- private:
-  // Informs all Surfaces with pending frames blocked on the provided
-  // |surface_id| that there is now an active frame available in Surface
-  // corresponding to |surface_id|.
-  void NotifySurfaceIdAvailable(const SurfaceId& surface_id);
-
-  SurfaceManager* const surface_manager_;
-
-  // A map from a FrameSinkId to the set of Surfaces that are blocked on
-  // surfaces associated with that FrameSinkId.
-  std::unordered_map<FrameSinkId, base::flat_set<SurfaceId>, FrameSinkIdHash>
-      blocked_surfaces_from_dependency_;
-
-  // A map from a FrameSinkid to a set of surfaces with that FrameSinkId that
-  // are blocked on a parent arriving to embed them.
-  std::unordered_map<FrameSinkId, base::flat_set<SurfaceId>, FrameSinkIdHash>
-      surfaces_blocked_on_parent_by_frame_sink_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(SurfaceDependencyTracker);
-};
-
-}  // namespace viz
-
-#endif  // COMPONENTS_VIZ_SERVICE_SURFACES_SURFACE_DEPENDENCY_TRACKER_H_
diff --git a/components/viz/service/surfaces/surface_manager.cc b/components/viz/service/surfaces/surface_manager.cc
index b6c4318..0c522bf 100644
--- a/components/viz/service/surfaces/surface_manager.cc
+++ b/components/viz/service/surfaces/surface_manager.cc
@@ -47,7 +47,6 @@
     base::Optional<uint32_t> activation_deadline_in_frames)
     : delegate_(delegate),
       activation_deadline_in_frames_(activation_deadline_in_frames),
-      dependency_tracker_(this),
       root_surface_id_(FrameSinkId(0u, 0u),
                        LocalSurfaceId(1u, base::UnguessableToken::Create())),
       tick_clock_(base::DefaultTickClock::GetInstance()),
@@ -150,8 +149,12 @@
 }
 
 void SurfaceManager::InvalidateFrameSinkId(const FrameSinkId& frame_sink_id) {
-  dependency_tracker_.OnFrameSinkInvalidated(frame_sink_id);
-
+  auto it = frame_sink_id_to_allocation_groups_.find(frame_sink_id);
+  if (it != frame_sink_id_to_allocation_groups_.end()) {
+    for (SurfaceAllocationGroup* group : it->second) {
+      group->WillNotRegisterNewSurfaces();
+    }
+  }
   GarbageCollectSurfaces();
 }
 
@@ -487,26 +490,11 @@
 
   for (auto& observer : observer_list_)
     observer.OnSurfaceActivated(surface->surface_id(), duration);
-
-  dependency_tracker_.OnSurfaceActivated(surface);
-}
-
-void SurfaceManager::SurfaceDependencyAdded(const SurfaceId& surface_id) {
-  dependency_tracker_.OnSurfaceDependencyAdded(surface_id);
-}
-
-void SurfaceManager::SurfaceDependenciesChanged(
-    Surface* surface,
-    const base::flat_set<FrameSinkId>& added_dependencies,
-    const base::flat_set<FrameSinkId>& removed_dependencies) {
-  dependency_tracker_.OnSurfaceDependenciesChanged(surface, added_dependencies,
-                                                   removed_dependencies);
 }
 
 void SurfaceManager::SurfaceDestroyed(Surface* surface) {
   for (auto& observer : observer_list_)
     observer.OnSurfaceDestroyed(surface->surface_id());
-  dependency_tracker_.OnSurfaceDiscarded(surface);
 }
 
 void SurfaceManager::SurfaceDamageExpected(const SurfaceId& surface_id,
@@ -591,6 +579,8 @@
     allocation_group = std::make_unique<SurfaceAllocationGroup>(
         this, surface_id.frame_sink_id(),
         surface_id.local_surface_id().embed_token());
+    frame_sink_id_to_allocation_groups_[surface_id.frame_sink_id()].push_back(
+        allocation_group.get());
   }
   return allocation_group.get();
 }
@@ -617,8 +607,30 @@
   if (!allocation_groups_need_garbage_collection_)
     return;
 
-  base::EraseIf(embed_token_to_allocation_group_,
-                [](auto& entry) { return entry.second->IsReadyToDestroy(); });
+  bool did_destroy = false;
+  for (auto it = embed_token_to_allocation_group_.begin();
+       it != embed_token_to_allocation_group_.end(); ++it) {
+    if (!it->second->IsReadyToDestroy())
+      continue;
+    // Before destroying the allocation group, remove it from
+    // |frame_sink_id_to_allocation_groups_|.
+    auto list_it = frame_sink_id_to_allocation_groups_.find(
+        it->second->submitter_frame_sink_id());
+    DCHECK(list_it != frame_sink_id_to_allocation_groups_.end());
+    base::Erase(list_it->second, it->second.get());
+    if (list_it->second.empty())
+      frame_sink_id_to_allocation_groups_.erase(list_it);
+    // Destroy the allocation group. Removing it from the map is done in a
+    // separate pass to avoid invalidating the iterator.
+    it->second.reset();
+    did_destroy = true;
+  }
+
+  // Remove the destroyed allocation groups from the map.
+  if (did_destroy) {
+    base::EraseIf(embed_token_to_allocation_group_,
+                  [](auto& entry) { return !entry.second; });
+  }
 
   allocation_groups_need_garbage_collection_ = false;
 }
diff --git a/components/viz/service/surfaces/surface_manager.h b/components/viz/service/surfaces/surface_manager.h
index f8e4b534..1635548 100644
--- a/components/viz/service/surfaces/surface_manager.h
+++ b/components/viz/service/surfaces/surface_manager.h
@@ -23,7 +23,6 @@
 #include "base/timer/timer.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/surface_id.h"
-#include "components/viz/service/surfaces/surface_dependency_tracker.h"
 #include "components/viz/service/surfaces/surface_observer.h"
 #include "components/viz/service/surfaces/surface_reference.h"
 
@@ -38,9 +37,12 @@
 
 namespace viz {
 
+class BeginFrameSource;
 class Surface;
 class SurfaceAllocationGroup;
+class SurfaceClient;
 class SurfaceManagerDelegate;
+class SurfaceRange;
 struct BeginFrameAck;
 struct BeginFrameArgs;
 
@@ -65,10 +67,6 @@
     return activation_deadline_in_frames_;
   }
 
-  SurfaceDependencyTracker* dependency_tracker() {
-    return &dependency_tracker_;
-  }
-
   // Sets an alternative base::TickClock to pass into surfaces for surface
   // synchronization deadlines. This allows unit tests to mock the wall clock.
   void SetTickClockForTesting(const base::TickClock* tick_clock);
@@ -117,17 +115,6 @@
   void SurfaceActivated(Surface* surface,
                         base::Optional<base::TimeDelta> duration);
 
-  // Called when this |surface_id| is referenced as an activation dependency
-  // from a parent CompositorFrame.
-  void SurfaceDependencyAdded(const SurfaceId& surface_id);
-
-  // Called when the dependencies of a pending CompositorFrame within |surface|
-  // has changed.
-  void SurfaceDependenciesChanged(
-      Surface* surface,
-      const base::flat_set<FrameSinkId>& added_dependencies,
-      const base::flat_set<FrameSinkId>& removed_dependencies);
-
   // Called when |surface| is being destroyed.
   void SurfaceDestroyed(Surface* surface);
 
@@ -284,10 +271,11 @@
 
   base::Optional<uint32_t> activation_deadline_in_frames_;
 
-  // SurfaceDependencyTracker needs to be destroyed after Surfaces are destroyed
-  // because they will call back into the dependency tracker.
-  SurfaceDependencyTracker dependency_tracker_;
-
+  base::flat_map<base::UnguessableToken,
+                 std::unique_ptr<SurfaceAllocationGroup>>
+      embed_token_to_allocation_group_;
+  base::flat_map<FrameSinkId, std::vector<SurfaceAllocationGroup*>>
+      frame_sink_id_to_allocation_groups_;
   base::flat_map<SurfaceId, std::unique_ptr<Surface>> surface_map_;
   base::ObserverList<SurfaceObserver>::Unchecked observer_list_;
   base::ThreadChecker thread_checker_;
@@ -332,10 +320,6 @@
   // are temporary references. Also the timer isn't used with Android WebView.
   base::Optional<base::RepeatingTimer> expire_timer_;
 
-  base::flat_map<base::UnguessableToken,
-                 std::unique_ptr<SurfaceAllocationGroup>>
-      embed_token_to_allocation_group_;
-
   bool allocation_groups_need_garbage_collection_ = false;
 
   base::WeakPtrFactory<SurfaceManager> weak_factory_;
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index 579d055..122abfbb 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -10,7 +10,6 @@
 #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
-#include "components/viz/service/surfaces/surface_dependency_tracker.h"
 #include "components/viz/test/begin_frame_args_test.h"
 #include "components/viz/test/compositor_frame_helpers.h"
 #include "components/viz/test/fake_external_begin_frame_source.h"
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
index 8683b72..24c83f29 100644
--- a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
@@ -152,6 +152,8 @@
   AddATKEventListener("ATK:AtkObject:state-change");
   AddATKEventListener("ATK:AtkObject:focus-event");
   AddATKEventListener("ATK:AtkObject:property-change");
+  AddATKEventListener("ATK:AtkText:text-insert");
+  AddATKEventListener("ATK:AtkText:text-remove");
   AddATKEventListener("ATK:AtkSelection:selection-changed");
 }
 
@@ -190,8 +192,9 @@
     return;
   }
 
+  std::string event_name(event);
   std::string log;
-  if (std::string(event).find("property-change") != std::string::npos) {
+  if (event_name.find("property-change") != std::string::npos) {
     DCHECK_GE(n_params, 2u);
     AtkPropertyValues* property_values =
         static_cast<AtkPropertyValues*>(g_value_get_pointer(&params[1]));
@@ -213,9 +216,20 @@
     }
   } else {
     log += base::ToUpperASCII(event);
-    if (std::string(event).find("state-change") != std::string::npos) {
-      log += ":" + base::ToUpperASCII(g_value_get_string(&params[1]));
-      log += base::StringPrintf(":%s", g_strdup_value_contents(&params[2]));
+    if (event_name.find("state-change") != std::string::npos) {
+      std::string state_type = g_value_get_string(&params[1]);
+      log += ":" + base::ToUpperASCII(state_type);
+
+      gchar* parameter = g_strdup_value_contents(&params[2]);
+      log += base::StringPrintf(":%s", parameter);
+      g_free(parameter);
+
+    } else if (event_name.find("text-insert") != std::string::npos ||
+               event_name.find("text-remove") != std::string::npos) {
+      DCHECK_GE(n_params, 4u);
+      log += base::StringPrintf(
+          " (start=%i length=%i '%s')", g_value_get_int(&params[1]),
+          g_value_get_int(&params[2]), g_value_get_string(&params[3]));
     }
   }
 
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index 1217bae..8531dfe 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -574,6 +574,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
+                       AccessibilityEventsTextChangedContentEditable) {
+  RunEventTest(FILE_PATH_LITERAL("text-changed-contenteditable.html"));
+}
+
+IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
                        AccessibilityEventsAriaCheckedChanged) {
   RunEventTest(FILE_PATH_LITERAL("aria-checked-changed.html"));
 }
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 22560099..9168181a 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -517,6 +517,17 @@
 
 #if defined(OS_ANDROID)
 bool g_browser_main_loop_shutting_down = false;
+
+namespace {
+// Whether or not BrowserMainLoop::CreateStartupTasks() posts any tasks.
+bool g_post_startup_tasks = true;
+}  // namespace
+
+// static
+void BrowserMainLoop::EnableStartupTasks(bool enabled) {
+  g_post_startup_tasks = enabled;
+}
+
 #endif
 
 // BrowserMainLoop construction / destruction =============================
@@ -873,9 +884,15 @@
 
   DCHECK(!startup_task_runner_);
 #if defined(OS_ANDROID)
+  // Some java scheduler tests need to test migration to C++, but the browser
+  // environment isn't set up fully and if these tasks run they may crash.
+  if (!g_post_startup_tasks)
+    return;
+
   startup_task_runner_ = std::make_unique<StartupTaskRunner>(
       base::BindOnce(&BrowserStartupComplete),
-      base::ThreadTaskRunnerHandle::Get());
+      base::CreateSingleThreadTaskRunnerWithTraits(
+          {BrowserThread::UI, BrowserTaskType::kBootstrap}));
 #else
   startup_task_runner_ = std::make_unique<StartupTaskRunner>(
       base::OnceCallback<void(int)>(), base::ThreadTaskRunnerHandle::Get());
diff --git a/content/browser/browser_main_loop.h b/content/browser/browser_main_loop.h
index febdcd0..24cca0cf 100644
--- a/content/browser/browser_main_loop.h
+++ b/content/browser/browser_main_loop.h
@@ -212,6 +212,12 @@
 
 #if defined(OS_ANDROID)
   void SynchronouslyFlushStartupTasks();
+
+  // |enabled| Whether or not CreateStartupTasks() posts any tasks. This is
+  // useful because some javatests want to test native task posting without the
+  // whole browser loaded. In that scenario tasks posted by CreateStartupTasks()
+  // may crash if run.
+  static void EnableStartupTasks(bool enabled);
 #endif  // OS_ANDROID
 
 #if !defined(OS_ANDROID)
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index acd158e..f0107bb 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -123,7 +123,6 @@
     NavigationRequest* navigation_request,
     const std::vector<GURL>& redirect_chain,
     int pending_nav_entry_id,
-    std::unique_ptr<NavigationUIData> navigation_ui_data,
     net::HttpRequestHeaders request_headers,
     const Referrer& sanitized_referrer)
     : navigation_request_(navigation_request),
@@ -134,14 +133,12 @@
       subframe_entry_committed_(false),
       request_headers_(std::move(request_headers)),
       pending_nav_entry_id_(pending_nav_entry_id),
-      navigation_ui_data_(std::move(navigation_ui_data)),
       navigation_id_(CreateUniqueHandleID()),
       redirect_chain_(redirect_chain),
       reload_type_(ReloadType::NONE),
       restore_type_(RestoreType::NONE),
       navigation_type_(NAVIGATION_TYPE_UNKNOWN),
       is_same_process_(true),
-      throttle_runner_(this, this),
       weak_factory_(this) {
   const GURL& url = navigation_request_->common_params().url;
   TRACE_EVENT_ASYNC_BEGIN2("navigation", "NavigationHandle", this,
@@ -192,8 +189,6 @@
   navigation_handle_proxy_ = std::make_unique<NavigationHandleProxy>(this);
 #endif
 
-  GetDelegate()->DidStartNavigation(this);
-
   if (IsInMainFrame()) {
     TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1(
         "navigation", "Navigation StartToCommit", this,
@@ -303,7 +298,7 @@
 }
 
 const NavigationUIData* NavigationHandleImpl::GetNavigationUIData() {
-  return navigation_ui_data_.get();
+  return navigation_request_->navigation_ui_data();
 }
 
 bool NavigationHandleImpl::IsExternalProtocol() {
@@ -409,26 +404,10 @@
              : net::IPEndPoint();
 }
 
-void NavigationHandleImpl::Resume(NavigationThrottle* resuming_throttle) {
-  DCHECK(resuming_throttle);
-  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this,
-                               "Resume");
-  throttle_runner_.ResumeProcessingNavigationEvent(resuming_throttle);
-  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
-  // by the previous call.
-}
-
-void NavigationHandleImpl::CancelDeferredNavigation(
-    NavigationThrottle* cancelling_throttle,
-    NavigationThrottle::ThrottleCheckResult result) {
-  DCHECK(cancelling_throttle);
-  DCHECK_EQ(cancelling_throttle, throttle_runner_.GetDeferringThrottle());
-  CancelDeferredNavigationInternal(result);
-}
-
 void NavigationHandleImpl::RegisterThrottleForTesting(
     std::unique_ptr<NavigationThrottle> navigation_throttle) {
-  throttle_runner_.AddThrottle(std::move(navigation_throttle));
+  navigation_request_->RegisterThrottleForTesting(
+      std::move(navigation_throttle));
 }
 
 #if defined(OS_ANDROID)
@@ -439,7 +418,7 @@
 #endif
 
 bool NavigationHandleImpl::IsDeferredForTesting() {
-  return throttle_runner_.GetDeferringThrottle() != nullptr;
+  return navigation_request_->IsDeferredForTesting();
 }
 
 bool NavigationHandleImpl::WasStartedFromContextMenu() const {
@@ -497,7 +476,7 @@
 }
 
 void NavigationHandleImpl::CallResumeForTesting() {
-  throttle_runner_.CallResumeForTesting();
+  navigation_request_->CallResumeForTesting();
 }
 
 const base::Optional<url::Origin>& NavigationHandleImpl::GetInitiatorOrigin() {
@@ -550,41 +529,6 @@
   return std::move(appcache_handle_);
 }
 
-void NavigationHandleImpl::WillStartRequest(
-    ThrottleChecksFinishedCallback callback) {
-  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this,
-                               "WillStartRequest");
-  // WillStartRequest should only be called once.
-  if (state() != NavigationRequest::INITIAL) {
-    navigation_request_->set_handle_state(NavigationRequest::CANCELING);
-    RunCompleteCallback(NavigationThrottle::CANCEL);
-    return;
-  }
-
-  navigation_request_->set_handle_state(
-      NavigationRequest::PROCESSING_WILL_START_REQUEST);
-  complete_callback_ = std::move(callback);
-
-  if (IsSelfReferentialURL()) {
-    navigation_request_->set_handle_state(NavigationRequest::CANCELING);
-    RunCompleteCallback(NavigationThrottle::CANCEL);
-    return;
-  }
-
-  throttle_runner_.RegisterNavigationThrottles();
-
-  // If the content/ embedder did not pass the NavigationUIData at the beginning
-  // of the navigation, ask for it now.
-  if (!navigation_ui_data_)
-    navigation_ui_data_ = GetDelegate()->GetNavigationUIData(this);
-
-  // Notify each throttle of the request.
-  throttle_runner_.ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::WillStartRequest);
-  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
-  // by the previous call.
-}
-
 void NavigationHandleImpl::UpdateStateFollowingRedirect(
     const GURL& new_referrer_url,
     ThrottleChecksFinishedCallback callback) {
@@ -607,62 +551,12 @@
 
   navigation_request_->set_handle_state(
       NavigationRequest::PROCESSING_WILL_REDIRECT_REQUEST);
-  complete_callback_ = std::move(callback);
-}
 
-void NavigationHandleImpl::WillRedirectRequest(
-    const GURL& new_referrer_url,
-    RenderProcessHost* post_redirect_process,
-    ThrottleChecksFinishedCallback callback) {
-  TRACE_EVENT_ASYNC_STEP_INTO1("navigation", "NavigationHandle", this,
-                               "WillRedirectRequest", "url",
-                               GetURL().possibly_invalid_spec());
-  UpdateStateFollowingRedirect(new_referrer_url, std::move(callback));
-  navigation_request_->UpdateSiteURL(post_redirect_process);
-
-  if (IsSelfReferentialURL()) {
-    navigation_request_->set_handle_state(NavigationRequest::CANCELING);
-    RunCompleteCallback(NavigationThrottle::CANCEL);
-    return;
-  }
-
-  // Notify each throttle of the request.
-  throttle_runner_.ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::WillRedirectRequest);
-  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
-  // by the previous call.
-}
-
-void NavigationHandleImpl::WillFailRequest(
-    ThrottleChecksFinishedCallback callback) {
-  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this,
-                               "WillFailRequest");
+#if defined(OS_ANDROID)
+  navigation_handle_proxy_->DidRedirect();
+#endif
 
   complete_callback_ = std::move(callback);
-  navigation_request_->set_handle_state(
-      NavigationRequest::PROCESSING_WILL_FAIL_REQUEST);
-
-  // Notify each throttle of the request.
-  throttle_runner_.ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::WillFailRequest);
-  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
-  // by the previous call.
-}
-
-void NavigationHandleImpl::WillProcessResponse(
-    ThrottleChecksFinishedCallback callback) {
-  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this,
-                               "WillProcessResponse");
-
-  navigation_request_->set_handle_state(
-      NavigationRequest::PROCESSING_WILL_PROCESS_RESPONSE);
-  complete_callback_ = std::move(callback);
-
-  // Notify each throttle of the response.
-  throttle_runner_.ProcessNavigationEvent(
-      NavigationThrottleRunner::Event::WillProcessResponse);
-  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
-  // by the previous call.
 }
 
 void NavigationHandleImpl::ReadyToCommitNavigation(bool is_error) {
@@ -814,115 +708,6 @@
   }
 }
 
-void NavigationHandleImpl::OnNavigationEventProcessed(
-    NavigationThrottleRunner::Event event,
-    NavigationThrottle::ThrottleCheckResult result) {
-  DCHECK_NE(NavigationThrottle::DEFER, result.action());
-  switch (event) {
-    case NavigationThrottleRunner::Event::WillStartRequest:
-      OnWillStartRequestProcessed(result);
-      return;
-    case NavigationThrottleRunner::Event::WillRedirectRequest:
-      OnWillRedirectRequestProcessed(result);
-      return;
-    case NavigationThrottleRunner::Event::WillFailRequest:
-      OnWillFailRequestProcessed(result);
-      return;
-    case NavigationThrottleRunner::Event::WillProcessResponse:
-      OnWillProcessResponseProcessed(result);
-      return;
-    default:
-      NOTREACHED();
-  }
-  NOTREACHED();
-}
-
-void NavigationHandleImpl::OnWillStartRequestProcessed(
-    NavigationThrottle::ThrottleCheckResult result) {
-  DCHECK_EQ(NavigationRequest::PROCESSING_WILL_START_REQUEST, state());
-  DCHECK_NE(NavigationThrottle::BLOCK_RESPONSE, result.action());
-  if (result.action() == NavigationThrottle::PROCEED) {
-    navigation_request_->set_handle_state(
-        NavigationRequest::WILL_START_REQUEST);
-  } else {
-    navigation_request_->set_handle_state(NavigationRequest::CANCELING);
-  }
-  RunCompleteCallback(result);
-}
-
-void NavigationHandleImpl::OnWillRedirectRequestProcessed(
-    NavigationThrottle::ThrottleCheckResult result) {
-  DCHECK_EQ(NavigationRequest::PROCESSING_WILL_REDIRECT_REQUEST, state());
-  DCHECK_NE(NavigationThrottle::BLOCK_RESPONSE, result.action());
-  if (result.action() == NavigationThrottle::PROCEED) {
-    navigation_request_->set_handle_state(
-        NavigationRequest::WILL_REDIRECT_REQUEST);
-
-#if defined(OS_ANDROID)
-    navigation_handle_proxy_->DidRedirect();
-#endif
-
-    // Notify the delegate that a redirect was encountered and will be followed.
-    if (GetDelegate())
-      GetDelegate()->DidRedirectNavigation(this);
-  } else {
-    navigation_request_->set_handle_state(NavigationRequest::CANCELING);
-  }
-  RunCompleteCallback(result);
-}
-
-void NavigationHandleImpl::OnWillFailRequestProcessed(
-    NavigationThrottle::ThrottleCheckResult result) {
-  DCHECK_EQ(NavigationRequest::PROCESSING_WILL_FAIL_REQUEST, state());
-  DCHECK_NE(NavigationThrottle::BLOCK_RESPONSE, result.action());
-  if (result.action() == NavigationThrottle::PROCEED) {
-    navigation_request_->set_handle_state(NavigationRequest::WILL_FAIL_REQUEST);
-    result = NavigationThrottle::ThrottleCheckResult(
-        NavigationThrottle::PROCEED, net_error_code_);
-  } else {
-    navigation_request_->set_handle_state(NavigationRequest::CANCELING);
-  }
-  RunCompleteCallback(result);
-}
-
-void NavigationHandleImpl::OnWillProcessResponseProcessed(
-    NavigationThrottle::ThrottleCheckResult result) {
-  DCHECK_EQ(NavigationRequest::PROCESSING_WILL_PROCESS_RESPONSE, state());
-  DCHECK_NE(NavigationThrottle::BLOCK_REQUEST, result.action());
-  DCHECK_NE(NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE, result.action());
-  if (result.action() == NavigationThrottle::PROCEED) {
-    navigation_request_->set_handle_state(
-        NavigationRequest::WILL_PROCESS_RESPONSE);
-    // If the navigation is done processing the response, then it's ready to
-    // commit. Inform observers that the navigation is now ready to commit,
-    // unless it is not set to commit (204/205s/downloads).
-    if (GetRenderFrameHost())
-      ReadyToCommitNavigation(false);
-  } else {
-    navigation_request_->set_handle_state(NavigationRequest::CANCELING);
-  }
-  RunCompleteCallback(result);
-}
-
-void NavigationHandleImpl::CancelDeferredNavigationInternal(
-    NavigationThrottle::ThrottleCheckResult result) {
-  DCHECK(state() == NavigationRequest::PROCESSING_WILL_START_REQUEST ||
-         state() == NavigationRequest::PROCESSING_WILL_REDIRECT_REQUEST ||
-         state() == NavigationRequest::PROCESSING_WILL_FAIL_REQUEST ||
-         state() == NavigationRequest::PROCESSING_WILL_PROCESS_RESPONSE);
-  DCHECK(result.action() == NavigationThrottle::CANCEL_AND_IGNORE ||
-         result.action() == NavigationThrottle::CANCEL ||
-         result.action() == NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
-  DCHECK(result.action() != NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE ||
-         state() == NavigationRequest::PROCESSING_WILL_START_REQUEST ||
-         state() == NavigationRequest::PROCESSING_WILL_REDIRECT_REQUEST);
-
-  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationHandle", this,
-                               "CancelDeferredNavigation");
-  navigation_request_->set_handle_state(NavigationRequest::CANCELING);
-  RunCompleteCallback(result);
-}
-
 void NavigationHandleImpl::RunCompleteCallback(
     NavigationThrottle::ThrottleCheckResult result) {
   DCHECK(result.action() != NavigationThrottle::DEFER);
@@ -940,37 +725,6 @@
   // destruction.
 }
 
-bool NavigationHandleImpl::IsSelfReferentialURL() {
-  // about: URLs should be exempted since they are reserved for other purposes
-  // and cannot be the source of infinite recursion. See
-  // https://crbug.com/341858 .
-  if (GetURL().SchemeIs("about"))
-    return false;
-
-  // Browser-triggered navigations should be exempted.
-  if (navigation_request_->browser_initiated())
-    return false;
-
-  // Some sites rely on constructing frame hierarchies where frames are loaded
-  // via POSTs with the same URLs, so exempt POST requests.  See
-  // https://crbug.com/710008.
-  if (navigation_request_->common_params().method == "POST")
-    return false;
-
-  // We allow one level of self-reference because some sites depend on that,
-  // but we don't allow more than one.
-  bool found_self_reference = false;
-  for (const FrameTreeNode* node = frame_tree_node()->parent(); node;
-       node = node->parent()) {
-    if (node->current_url().EqualsIgnoringRef(GetURL())) {
-      if (found_self_reference)
-        return true;
-      found_self_reference = true;
-    }
-  }
-  return false;
-}
-
 void NavigationHandleImpl::RenderProcessBlockedStateChanged(bool blocked) {
   if (blocked)
     StopCommitTimeout();
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index e0639d4..5536a028 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -59,8 +59,7 @@
 // NavigationHandleImpl ownership is then transferred to the RenderFrameHost in
 // which the navigation will commit. It is finaly destroyed when the navigation
 // commits.
-class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle,
-                                            NavigationThrottleRunner::Delegate {
+class CONTENT_EXPORT NavigationHandleImpl : public NavigationHandle {
  public:
   ~NavigationHandleImpl() override;
 
@@ -127,14 +126,6 @@
 
   const std::string& GetOriginPolicy() const;
 
-  // Resume and CancelDeferredNavigation must only be called by the
-  // NavigationThrottle that is currently deferring the navigation.
-  // |resuming_throttle| and |cancelling_throttle| are the throttles calling
-  // these methods.
-  void Resume(NavigationThrottle* resuming_throttle);
-  void CancelDeferredNavigation(NavigationThrottle* cancelling_throttle,
-                                NavigationThrottle::ThrottleCheckResult result);
-
   // Simulates the navigation resuming. Most callers should just let the
   // deferring NavigationThrottle do the resuming.
   void CallResumeForTesting();
@@ -201,46 +192,12 @@
   typedef base::OnceCallback<void(NavigationThrottle::ThrottleCheckResult)>
       ThrottleChecksFinishedCallback;
 
-  // Called when the URLRequest will start in the network stack.  |callback|
-  // will be called when all throttle checks have completed. This will allow
-  // the caller to cancel the navigation or let it proceed.
-  void WillStartRequest(ThrottleChecksFinishedCallback callback);
-
   // Updates the state of the navigation handle after encountering a server
   // redirect.
   void UpdateStateFollowingRedirect(
       const GURL& new_referrer_url,
       ThrottleChecksFinishedCallback callback);
 
-  // Called when the URLRequest will be redirected in the network stack.
-  // |callback| will be called when all throttles check have completed. This
-  // will allow the caller to cancel the navigation or let it proceed.
-  // This will also inform the delegate that the request was redirected.
-  //
-  // |post_redirect_process| is the renderer process we expect to
-  // use to commit the navigation now that it has been redirected. It can be
-  // null if there is no live process that can be used. In that case, a suitable
-  // renderer process will be created at commit time.
-  void WillRedirectRequest(
-      const GURL& new_referrer_url,
-      RenderProcessHost* post_redirect_process,
-      ThrottleChecksFinishedCallback callback);
-
-  // Called when the URLRequest will fail. |callback| will be
-  // called when all throttles check have completed. This will allow the caller
-  // to explicitly cancel the navigation (with a custom error code and/or
-  // custom error page HTML) or let the failure proceed as normal.
-  void WillFailRequest(ThrottleChecksFinishedCallback callback);
-
-  // Called when the URLRequest has delivered response headers and metadata.
-  // |callback| will be called when all throttle checks have completed,
-  // allowing the caller to cancel the navigation or let it proceed.
-  // NavigationHandle will not call |callback| with a result of DEFER.
-  // If the result is PROCEED, then 'ReadyToCommitNavigation' will be called
-  // just before calling |callback|.
-  void WillProcessResponse(
-      ThrottleChecksFinishedCallback callback);
-
   // Returns the FrameTreeNode this navigation is happening in.
   FrameTreeNode* frame_tree_node() const {
     return navigation_request_->frame_tree_node();
@@ -270,7 +227,7 @@
   }
 
   NavigationUIData* navigation_ui_data() const {
-    return navigation_ui_data_.get();
+    return navigation_request_->navigation_ui_data();
   }
 
   const GURL& base_url() { return base_url_; }
@@ -312,7 +269,7 @@
   }
 
   NavigationThrottle* GetDeferringThrottleForTesting() const {
-    return throttle_runner_.GetDeferringThrottle();
+    return navigation_request_->GetDeferringThrottleForTesting();
   }
 
   // Whether the navigation was sent to be committed in a renderer by the
@@ -338,40 +295,21 @@
   NavigationHandleImpl(NavigationRequest* navigation_request,
                        const std::vector<GURL>& redirect_chain,
                        int pending_nav_entry_id,
-                       std::unique_ptr<NavigationUIData> navigation_ui_data,
                        net::HttpRequestHeaders request_headers,
                        const Referrer& sanitized_referrer);
 
-  // NavigationThrottleRunner::Delegate:
-  void OnNavigationEventProcessed(
-      NavigationThrottleRunner::Event event,
-      NavigationThrottle::ThrottleCheckResult result) override;
-
-  void OnWillStartRequestProcessed(
-      NavigationThrottle::ThrottleCheckResult result);
-  void OnWillRedirectRequestProcessed(
-      NavigationThrottle::ThrottleCheckResult result);
-  void OnWillFailRequestProcessed(
-      NavigationThrottle::ThrottleCheckResult result);
-  void OnWillProcessResponseProcessed(
-      NavigationThrottle::ThrottleCheckResult result);
-
-  void CancelDeferredNavigationInternal(
-      NavigationThrottle::ThrottleCheckResult result);
-
   // Helper function to run and reset the |complete_callback_|. This marks the
   // end of a round of NavigationThrottleChecks.
   void RunCompleteCallback(NavigationThrottle::ThrottleCheckResult result);
 
+  void SetCompleteCallback(ThrottleChecksFinishedCallback callback) {
+    complete_callback_ = std::move(callback);
+  }
+
   NavigationRequest::NavigationHandleState state() const {
     return navigation_request_->handle_state();
   }
 
-  // Checks for attempts to navigate to a page that is already referenced more
-  // than once in the frame's ancestors.  This is a helper function used by
-  // WillStartRequest and WillRedirectRequest to prevent the navigation.
-  bool IsSelfReferentialURL();
-
   // Called if READY_TO_COMMIT -> COMMIT state transition takes an unusually
   // long time.
   void OnCommitTimeout();
@@ -440,9 +378,6 @@
   // Embedder data from the IO thread tied to this navigation.
   std::unique_ptr<NavigationData> navigation_data_;
 
-  // Embedder data from the UI thread tied to this navigation.
-  std::unique_ptr<NavigationUIData> navigation_ui_data_;
-
   // The unique id to identify this to navigation with.
   int64_t navigation_id_;
 
@@ -469,10 +404,6 @@
   // TODO(clamy): Clean this up once the architecture of unit tests is better.
   scoped_refptr<net::HttpResponseHeaders> response_headers_for_testing_;
 
-  // Owns the NavigationThrottles associated with this navigation, and is
-  // responsible for notifying them about the various navigation events.
-  NavigationThrottleRunner throttle_runner_;
-
 #if defined(OS_ANDROID)
   // For each C++ NavigationHandle, there is a Java counterpart. It is the JNI
   // bridge in between the two.
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc
index d450671..2ad6c98 100644
--- a/content/browser/frame_host/navigation_handle_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -77,11 +77,9 @@
     RenderViewHostImplTestHarness::TearDown();
   }
 
-  void Resume() { test_handle()->throttle_runner_.CallResumeForTesting(); }
-
   void CancelDeferredNavigation(
       NavigationThrottle::ThrottleCheckResult result) {
-    test_handle()->CancelDeferredNavigationInternal(result);
+    request_->CancelDeferredNavigationInternal(result);
   }
 
   // Helper function to call WillStartRequest on |handle|. If this function
@@ -93,7 +91,7 @@
 
     // It's safe to use base::Unretained since the NavigationHandle is owned by
     // the NavigationHandleImplTest.
-    test_handle()->WillStartRequest(
+    request_->WillStartRequest(
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
   }
@@ -109,7 +107,7 @@
 
     // It's safe to use base::Unretained since the NavigationHandle is owned by
     // the NavigationHandleImplTest.
-    test_handle()->WillRedirectRequest(
+    request_->WillRedirectRequest(
         GURL(), nullptr,
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
@@ -127,7 +125,7 @@
 
     // It's safe to use base::Unretained since the NavigationHandle is owned by
     // the NavigationHandleImplTest.
-    test_handle()->WillFailRequest(
+    request_->WillFailRequest(
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
   }
@@ -145,7 +143,7 @@
     // by the NavigationHandleImplTest. The ConnectionInfo is different from
     // that sent to WillRedirectRequest to verify that it's correctly plumbed
     // in both cases.
-    test_handle()->WillProcessResponse(
+    request_->WillProcessResponse(
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
   }
@@ -220,6 +218,8 @@
     return test_throttle;
   }
 
+  // TODO(zetamoo): Use NavigationSimulator instead of creating
+  // NavigationRequest and NavigationHandleImpl.
   void CreateNavigationHandle() {
     scoped_refptr<FrameNavigationEntry> frame_entry(new FrameNavigationEntry());
     request_ = NavigationRequest::CreateBrowserInitiated(
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index ed9d688..28b2e1b 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -766,9 +766,8 @@
     // the NavigationHandle where the callback will be stored.
     // TODO(clamy): pass the method to the NavigationHandle instead of a
     // boolean.
-    navigation_handle_->WillStartRequest(
-        base::Bind(&NavigationRequest::OnStartChecksComplete,
-                   base::Unretained(this)));
+    WillStartRequest(base::Bind(&NavigationRequest::OnStartChecksComplete,
+                                base::Unretained(this)));
     return;
   }
 
@@ -832,8 +831,7 @@
   handle_state_ = NavigationRequest::INITIAL;
   std::unique_ptr<NavigationHandleImpl> navigation_handle =
       base::WrapUnique(new NavigationHandleImpl(
-          this, redirect_chain, nav_entry_id_, std::move(navigation_ui_data_),
-          std::move(headers),
+          this, redirect_chain, nav_entry_id_, std::move(headers),
           Referrer::SanitizeForRequest(common_params_.url,
                                        common_params_.referrer)));
 
@@ -844,6 +842,11 @@
   }
 
   navigation_handle_ = std::move(navigation_handle);
+
+  throttle_runner_ = base::WrapUnique(
+      new NavigationThrottleRunner(this, navigation_handle_.get()));
+
+  GetDelegate()->DidStartNavigation(navigation_handle_.get());
 }
 
 void NavigationRequest::ResetForCrossDocumentRestart() {
@@ -1078,10 +1081,9 @@
 
   // It's safe to use base::Unretained because this NavigationRequest owns the
   // NavigationHandle where the callback will be stored.
-  navigation_handle_->WillRedirectRequest(
-      common_params_.referrer.url, expected_process,
-      base::Bind(&NavigationRequest::OnRedirectChecksComplete,
-                 base::Unretained(this)));
+  WillRedirectRequest(common_params_.referrer.url, expected_process,
+                      base::Bind(&NavigationRequest::OnRedirectChecksComplete,
+                                 base::Unretained(this)));
 }
 
 void NavigationRequest::OnResponseStarted(
@@ -1294,7 +1296,7 @@
   }
 
   // Check if the navigation should be allowed to proceed.
-  navigation_handle_->WillProcessResponse(
+  WillProcessResponse(
       base::Bind(&NavigationRequest::OnWillProcessResponseChecksComplete,
                  base::Unretained(this)));
 }
@@ -1414,8 +1416,8 @@
     CommitErrorPage(error_page_content);
   } else {
     // Check if the navigation should be allowed to proceed.
-    navigation_handle_->WillFailRequest(base::BindOnce(
-        &NavigationRequest::OnFailureChecksComplete, base::Unretained(this)));
+    WillFailRequest(base::BindOnce(&NavigationRequest::OnFailureChecksComplete,
+                                   base::Unretained(this)));
   }
 }
 
@@ -2270,4 +2272,262 @@
       rfh, blink::mojom::WebFeature::kDownloadPostPolicyCheck);
 }
 
+void NavigationRequest::OnNavigationEventProcessed(
+    NavigationThrottleRunner::Event event,
+    NavigationThrottle::ThrottleCheckResult result) {
+  DCHECK_NE(NavigationThrottle::DEFER, result.action());
+  switch (event) {
+    case NavigationThrottleRunner::Event::WillStartRequest:
+      OnWillStartRequestProcessed(result);
+      return;
+    case NavigationThrottleRunner::Event::WillRedirectRequest:
+      OnWillRedirectRequestProcessed(result);
+      return;
+    case NavigationThrottleRunner::Event::WillFailRequest:
+      OnWillFailRequestProcessed(result);
+      return;
+    case NavigationThrottleRunner::Event::WillProcessResponse:
+      OnWillProcessResponseProcessed(result);
+      return;
+    default:
+      NOTREACHED();
+  }
+  NOTREACHED();
+}
+
+void NavigationRequest::OnWillStartRequestProcessed(
+    NavigationThrottle::ThrottleCheckResult result) {
+  DCHECK_EQ(PROCESSING_WILL_START_REQUEST, handle_state_);
+  DCHECK_NE(NavigationThrottle::BLOCK_RESPONSE, result.action());
+  if (result.action() == NavigationThrottle::PROCEED)
+    handle_state_ = WILL_START_REQUEST;
+  else
+    handle_state_ = CANCELING;
+
+  // TODO(zetamoo): Remove CompleteCallback from NavigationHandleImpl, and call
+  // the NavigationRequest methods directly.
+  navigation_handle_->RunCompleteCallback(result);
+}
+
+void NavigationRequest::OnWillRedirectRequestProcessed(
+    NavigationThrottle::ThrottleCheckResult result) {
+  DCHECK_EQ(PROCESSING_WILL_REDIRECT_REQUEST, handle_state_);
+  DCHECK_NE(NavigationThrottle::BLOCK_RESPONSE, result.action());
+  if (result.action() == NavigationThrottle::PROCEED) {
+    handle_state_ = WILL_REDIRECT_REQUEST;
+
+    // Notify the delegate that a redirect was encountered and will be followed.
+    if (GetDelegate())
+      GetDelegate()->DidRedirectNavigation(navigation_handle_.get());
+  } else {
+    handle_state_ = NavigationRequest::CANCELING;
+  }
+  navigation_handle_->RunCompleteCallback(result);
+}
+
+void NavigationRequest::OnWillFailRequestProcessed(
+    NavigationThrottle::ThrottleCheckResult result) {
+  DCHECK_EQ(NavigationRequest::PROCESSING_WILL_FAIL_REQUEST, handle_state_);
+  DCHECK_NE(NavigationThrottle::BLOCK_RESPONSE, result.action());
+  if (result.action() == NavigationThrottle::PROCEED) {
+    handle_state_ = WILL_FAIL_REQUEST;
+    result = NavigationThrottle::ThrottleCheckResult(
+        NavigationThrottle::PROCEED, navigation_handle_->GetNetErrorCode());
+  } else {
+    handle_state_ = CANCELING;
+  }
+  navigation_handle_->RunCompleteCallback(result);
+}
+
+void NavigationRequest::OnWillProcessResponseProcessed(
+    NavigationThrottle::ThrottleCheckResult result) {
+  DCHECK_EQ(NavigationRequest::PROCESSING_WILL_PROCESS_RESPONSE, handle_state_);
+  DCHECK_NE(NavigationThrottle::BLOCK_REQUEST, result.action());
+  DCHECK_NE(NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE, result.action());
+  if (result.action() == NavigationThrottle::PROCEED) {
+    handle_state_ = WILL_PROCESS_RESPONSE;
+    // If the navigation is done processing the response, then it's ready to
+    // commit. Inform observers that the navigation is now ready to commit,
+    // unless it is not set to commit (204/205s/downloads).
+    if (render_frame_host_)
+      navigation_handle_->ReadyToCommitNavigation(false);
+  } else {
+    handle_state_ = NavigationRequest::CANCELING;
+  }
+  navigation_handle_->RunCompleteCallback(result);
+}
+
+NavigatorDelegate* NavigationRequest::GetDelegate() const {
+  return frame_tree_node()->navigator()->GetDelegate();
+}
+
+void NavigationRequest::Resume(NavigationThrottle* resuming_throttle) {
+  DCHECK(resuming_throttle);
+  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this,
+                               "Resume");
+  throttle_runner_->ResumeProcessingNavigationEvent(resuming_throttle);
+  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
+  // by the previous call.
+}
+
+void NavigationRequest::CancelDeferredNavigation(
+    NavigationThrottle* cancelling_throttle,
+    NavigationThrottle::ThrottleCheckResult result) {
+  DCHECK(cancelling_throttle);
+  DCHECK_EQ(cancelling_throttle, throttle_runner_->GetDeferringThrottle());
+  CancelDeferredNavigationInternal(result);
+}
+
+void NavigationRequest::CallResumeForTesting() {
+  throttle_runner_->CallResumeForTesting();
+}
+
+void NavigationRequest::RegisterThrottleForTesting(
+    std::unique_ptr<NavigationThrottle> navigation_throttle) {
+  throttle_runner_->AddThrottle(std::move(navigation_throttle));
+}
+bool NavigationRequest::IsDeferredForTesting() {
+  return throttle_runner_->GetDeferringThrottle() != nullptr;
+}
+
+void NavigationRequest::CancelDeferredNavigationInternal(
+    NavigationThrottle::ThrottleCheckResult result) {
+  DCHECK(handle_state_ == PROCESSING_WILL_START_REQUEST ||
+         handle_state_ == PROCESSING_WILL_REDIRECT_REQUEST ||
+         handle_state_ == PROCESSING_WILL_FAIL_REQUEST ||
+         handle_state_ == PROCESSING_WILL_PROCESS_RESPONSE);
+  DCHECK(result.action() == NavigationThrottle::CANCEL_AND_IGNORE ||
+         result.action() == NavigationThrottle::CANCEL ||
+         result.action() == NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE);
+  DCHECK(result.action() != NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE ||
+         handle_state_ == PROCESSING_WILL_START_REQUEST ||
+         handle_state_ == PROCESSING_WILL_REDIRECT_REQUEST);
+
+  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this,
+                               "CancelDeferredNavigation");
+  handle_state_ = NavigationRequest::CANCELING;
+  navigation_handle_->RunCompleteCallback(result);
+}
+
+void NavigationRequest::WillStartRequest(
+    ThrottleChecksFinishedCallback callback) {
+  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this,
+                               "WillStartRequest");
+  // WillStartRequest should only be called once.
+  if (handle_state_ != INITIAL) {
+    handle_state_ = CANCELING;
+    navigation_handle_->RunCompleteCallback(NavigationThrottle::CANCEL);
+    return;
+  }
+
+  handle_state_ = PROCESSING_WILL_START_REQUEST;
+  navigation_handle_->SetCompleteCallback(std::move(callback));
+
+  if (IsSelfReferentialURL()) {
+    handle_state_ = CANCELING;
+    navigation_handle_->RunCompleteCallback(NavigationThrottle::CANCEL);
+    return;
+  }
+
+  throttle_runner_->RegisterNavigationThrottles();
+
+  // If the content/ embedder did not pass the NavigationUIData at the beginning
+  // of the navigation, ask for it now.
+  if (!navigation_ui_data_) {
+    navigation_ui_data_ =
+        GetDelegate()->GetNavigationUIData(navigation_handle_.get());
+  }
+
+  // Notify each throttle of the request.
+  throttle_runner_->ProcessNavigationEvent(
+      NavigationThrottleRunner::Event::WillStartRequest);
+  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
+  // by the previous call.
+}
+
+void NavigationRequest::WillRedirectRequest(
+    const GURL& new_referrer_url,
+    RenderProcessHost* post_redirect_process,
+    ThrottleChecksFinishedCallback callback) {
+  TRACE_EVENT_ASYNC_STEP_INTO1("navigation", "NavigationRequest", this,
+                               "WillRedirectRequest", "url",
+                               common_params_.url.possibly_invalid_spec());
+  navigation_handle_->UpdateStateFollowingRedirect(new_referrer_url,
+                                                   std::move(callback));
+  UpdateSiteURL(post_redirect_process);
+
+  if (IsSelfReferentialURL()) {
+    handle_state_ = CANCELING;
+    navigation_handle_->RunCompleteCallback(NavigationThrottle::CANCEL);
+    return;
+  }
+
+  // Notify each throttle of the request.
+  throttle_runner_->ProcessNavigationEvent(
+      NavigationThrottleRunner::Event::WillRedirectRequest);
+  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
+  // by the previous call.
+}
+
+void NavigationRequest::WillFailRequest(
+    ThrottleChecksFinishedCallback callback) {
+  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this,
+                               "WillFailRequest");
+
+  navigation_handle_->SetCompleteCallback(std::move(callback));
+  handle_state_ = PROCESSING_WILL_FAIL_REQUEST;
+
+  // Notify each throttle of the request.
+  throttle_runner_->ProcessNavigationEvent(
+      NavigationThrottleRunner::Event::WillFailRequest);
+  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
+  // by the previous call.
+}
+
+void NavigationRequest::WillProcessResponse(
+    ThrottleChecksFinishedCallback callback) {
+  TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this,
+                               "WillProcessResponse");
+
+  handle_state_ = PROCESSING_WILL_PROCESS_RESPONSE;
+  navigation_handle_->SetCompleteCallback(std::move(callback));
+
+  // Notify each throttle of the response.
+  throttle_runner_->ProcessNavigationEvent(
+      NavigationThrottleRunner::Event::WillProcessResponse);
+  // DO NOT ADD CODE AFTER THIS, as the NavigationHandle might have been deleted
+  // by the previous call.
+}
+
+bool NavigationRequest::IsSelfReferentialURL() {
+  // about: URLs should be exempted since they are reserved for other purposes
+  // and cannot be the source of infinite recursion.
+  // See https://crbug.com/341858 .
+  if (common_params_.url.SchemeIs(url::kAboutScheme))
+    return false;
+
+  // Browser-triggered navigations should be exempted.
+  if (browser_initiated())
+    return false;
+
+  // Some sites rely on constructing frame hierarchies where frames are loaded
+  // via POSTs with the same URLs, so exempt POST requests.
+  // See https://crbug.com/710008.
+  if (common_params_.method == "POST")
+    return false;
+
+  // We allow one level of self-reference because some sites depend on that,
+  // but we don't allow more than one.
+  bool found_self_reference = false;
+  for (const FrameTreeNode* node = frame_tree_node()->parent(); node;
+       node = node->parent()) {
+    if (node->current_url().EqualsIgnoringRef(common_params_.url)) {
+      if (found_self_reference)
+        return true;
+      found_self_reference = true;
+    }
+  }
+  return false;
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 0c2470c..0104f604 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -14,6 +14,7 @@
 #include "base/optional.h"
 #include "base/strings/string_util.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
+#include "content/browser/frame_host/navigation_throttle_runner.h"
 #include "content/browser/initiator_csp_context.h"
 #include "content/browser/loader/navigation_url_loader_delegate.h"
 #include "content/common/content_export.h"
@@ -37,6 +38,7 @@
 class NavigationURLLoader;
 class NavigationData;
 class NavigationUIData;
+class NavigatorDelegate;
 class SiteInstanceImpl;
 struct SubresourceLoaderParams;
 
@@ -45,7 +47,8 @@
 // ResourceDispatcherHost (that lives on the IO thread).
 // TODO(clamy): Describe the interactions between the UI and IO thread during
 // the navigation following its refactoring.
-class CONTENT_EXPORT NavigationRequest : public NavigationURLLoaderDelegate {
+class CONTENT_EXPORT NavigationRequest : public NavigationURLLoaderDelegate,
+                                         NavigationThrottleRunner::Delegate {
  public:
   // Keeps track of the various stages of a NavigationRequest.
   enum NavigationState {
@@ -314,7 +317,39 @@
 
   NavigationHandleState handle_state() { return handle_state_; }
 
+  NavigationUIData* navigation_ui_data() const {
+    return navigation_ui_data_.get();
+  }
+
+  // Resume and CancelDeferredNavigation must only be called by the
+  // NavigationThrottle that is currently deferring the navigation.
+  // |resuming_throttle| and |cancelling_throttle| are the throttles calling
+  // these methods.
+  void Resume(NavigationThrottle* resuming_throttle);
+  void CancelDeferredNavigation(NavigationThrottle* cancelling_throttle,
+                                NavigationThrottle::ThrottleCheckResult result);
+
+  // Simulates the navigation resuming. Most callers should just let the
+  // deferring NavigationThrottle do the resuming.
+  void CallResumeForTesting();
+
+  void RegisterThrottleForTesting(
+      std::unique_ptr<NavigationThrottle> navigation_throttle);
+
+  bool IsDeferredForTesting();
+
+  typedef base::OnceCallback<void(NavigationThrottle::ThrottleCheckResult)>
+      ThrottleChecksFinishedCallback;
+
+  NavigationThrottle* GetDeferringThrottleForTesting() const {
+    return throttle_runner_->GetDeferringThrottle();
+  }
+
  private:
+  // TODO(clamy): Transform NavigationHandleImplTest into NavigationRequestTest
+  // once NavigationHandleImpl has become a wrapper around NavigationRequest.
+  friend class NavigationHandleImplTest;
+
   NavigationRequest(FrameTreeNode* frame_tree_node,
                     const CommonNavigationParams& common_params,
                     mojom::BeginNavigationParamsPtr begin_params,
@@ -478,6 +513,64 @@
   // filtered by download_policy.
   void RecordDownloadUseCountersPostPolicyCheck();
 
+  // NavigationThrottleRunner::Delegate:
+  void OnNavigationEventProcessed(
+      NavigationThrottleRunner::Event event,
+      NavigationThrottle::ThrottleCheckResult result) override;
+
+  void OnWillStartRequestProcessed(
+      NavigationThrottle::ThrottleCheckResult result);
+  void OnWillRedirectRequestProcessed(
+      NavigationThrottle::ThrottleCheckResult result);
+  void OnWillFailRequestProcessed(
+      NavigationThrottle::ThrottleCheckResult result);
+  void OnWillProcessResponseProcessed(
+      NavigationThrottle::ThrottleCheckResult result);
+
+  NavigatorDelegate* GetDelegate() const;
+
+  void CancelDeferredNavigationInternal(
+      NavigationThrottle::ThrottleCheckResult result);
+
+  // TODO(zetamoo): Remove the Will* methods and fold them into their callers.
+
+  // Called when the URLRequest will start in the network stack. |callback| will
+  // be called when all throttle checks have completed. This will allow the
+  // caller to cancel the navigation or let it proceed.
+  void WillStartRequest(ThrottleChecksFinishedCallback callback);
+
+  // Called when the URLRequest will be redirected in the network stack.
+  // |callback| will be called when all throttles check have completed. This
+  // will allow the caller to cancel the navigation or let it proceed.
+  // This will also inform the delegate that the request was redirected.
+  //
+  // |post_redirect_process| is the renderer process we expect to use to commit
+  // the navigation now that it has been redirected. It can be null if there is
+  // no live process that can be used. In that case, a suitable renderer process
+  // will be created at commit time.
+  void WillRedirectRequest(const GURL& new_referrer_url,
+                           RenderProcessHost* post_redirect_process,
+                           ThrottleChecksFinishedCallback callback);
+
+  // Called when the URLRequest will fail. |callback| will be called when all
+  // throttles check have completed. This will allow the caller to explicitly
+  // cancel the navigation (with a custom error code and/or custom error page
+  // HTML) or let the failure proceed as normal.
+  void WillFailRequest(ThrottleChecksFinishedCallback callback);
+
+  // Called when the URLRequest has delivered response headers and metadata.
+  // |callback| will be called when all throttle checks have completed,
+  // allowing the caller to cancel the navigation or let it proceed.
+  // NavigationHandle will not call |callback| with a result of DEFER.
+  // If the result is PROCEED, then 'ReadyToCommitNavigation' will be called
+  // just before calling |callback|.
+  void WillProcessResponse(ThrottleChecksFinishedCallback callback);
+
+  // Checks for attempts to navigate to a page that is already referenced more
+  // than once in the frame's ancestors.  This is a helper function used by
+  // WillStartRequest and WillRedirectRequest to prevent the navigation.
+  bool IsSelfReferentialURL();
+
   FrameTreeNode* frame_tree_node_;
 
   RenderFrameHostImpl* render_frame_host_ = nullptr;
@@ -606,6 +699,10 @@
   // TODO(zetamoo): Merge |handle_state_| with |state_|.
   NavigationHandleState handle_state_ = NOT_CREATED;
 
+  // Owns the NavigationThrottles associated with this navigation, and is
+  // responsible for notifying them about the various navigation events.
+  std::unique_ptr<NavigationThrottleRunner> throttle_runner_;
+
   base::WeakPtrFactory<NavigationRequest> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(NavigationRequest);
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index ed291227..ef2a97c 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -138,11 +138,6 @@
             touch_action = white_listed_touch_action_;
           } else {
             gesture_sequence_.append("B");
-            static auto* crash_key = base::debug::AllocateCrashKeyString(
-                "scrollbegin-gestures", base::debug::CrashKeySize::Size256);
-            base::debug::SetCrashKeyString(crash_key, gesture_sequence_);
-            base::debug::DumpWithoutCrashing();
-            gesture_sequence_.clear();
             SetTouchAction(cc::kTouchActionAuto);
             touch_action = cc::kTouchActionAuto;
           }
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index de48c61..a47da70 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -111,7 +111,7 @@
 // registering with DevTools. Called on the UI thread. Calls |callback| on the
 // IO thread. |context| and |weak_context| are only for passing to DevTools and
 // must not be dereferenced here on the UI thread.
-// S13nServiceWorker:
+//
 // This also sets up two URLLoaderFactoryBundles, one for
 // ServiceWorkerScriptLoaderFactory and the other is for passing to the
 // renderer. These bundles include factories for non-network URLs like
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index c1e2129..1384ab8 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -293,7 +293,6 @@
 
   bool is_incognito() const { return is_incognito_; }
 
-  // S13nServiceWorker:
   // Used for starting a shared worker. Returns a provider host for the shared
   // worker and fills |out_provider_info| with info to send to the renderer to
   // connect to the host. The host stays alive as long as this info stays alive
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index 7890ca4..51b693d9 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -648,92 +648,7 @@
            std::move(timing), version_);
 }
 
-// Non-S13nServiceWorker
 bool ServiceWorkerFetchDispatcher::MaybeStartNavigationPreload(
-    net::URLRequest* original_request,
-    base::OnceClosure on_response) {
-  if (resource_type_ != RESOURCE_TYPE_MAIN_FRAME &&
-      resource_type_ != RESOURCE_TYPE_SUB_FRAME) {
-    return false;
-  }
-  if (!version_->navigation_preload_state().enabled)
-    return false;
-  // TODO(horo): Currently NavigationPreload doesn't support request body.
-  if (request_->blob)
-    return false;
-
-  ResourceRequestInfoImpl* original_info =
-      ResourceRequestInfoImpl::ForRequest(original_request);
-  ResourceRequesterInfo* requester_info = original_info->requester_info();
-  DCHECK(requester_info->IsBrowserSideNavigation());
-  auto url_loader_factory = std::make_unique<URLLoaderFactoryImpl>(
-      ResourceRequesterInfo::CreateForNavigationPreload(
-          requester_info,
-          const_cast<net::URLRequestContext*>(original_request->context())));
-
-  network::ResourceRequest request;
-  request.method = original_request->method();
-  request.url = original_request->url();
-  request.site_for_cookies = original_request->site_for_cookies();
-  request.request_initiator =
-      original_request->initiator().has_value()
-          ? original_request->initiator()
-          : url::Origin::Create(original_request->url());
-  request.referrer = GURL(original_request->referrer());
-  request.referrer_policy =
-      Referrer::ReferrerPolicyForUrlRequest(original_info->GetReferrerPolicy());
-  request.is_prerendering = original_info->IsPrerendering();
-  request.load_flags = original_request->load_flags();
-  request.resource_type = RESOURCE_TYPE_NAVIGATION_PRELOAD;
-  request.priority = original_request->priority();
-  request.skip_service_worker = true;
-  request.do_not_prompt_for_login = true;
-  request.render_frame_id = original_info->GetRenderFrameID();
-  request.is_main_frame = original_info->IsMainFrame();
-  request.enable_load_timing = original_info->is_load_timing_enabled();
-  request.report_raw_headers = original_info->ShouldReportRawHeaders();
-  request.throttling_profile_id =
-      network::ThrottlingController::GetProfileIDForNetLogSource(
-          original_request->net_log().source().id);
-
-  DCHECK(net::HttpUtil::IsValidHeaderValue(
-      version_->navigation_preload_state().header));
-  ServiceWorkerMetrics::RecordNavigationPreloadRequestHeaderSize(
-      version_->navigation_preload_state().header.length());
-  request.headers.CopyFrom(original_request->extra_request_headers());
-  request.headers.SetHeader("Service-Worker-Navigation-Preload",
-                            version_->navigation_preload_state().header);
-
-  const int request_id = ResourceDispatcherHostImpl::Get()->MakeRequestID();
-  DCHECK_LT(request_id, -1);
-
-  preload_handle_ = blink::mojom::FetchEventPreloadHandle::New();
-  network::mojom::URLLoaderClientPtr inner_url_loader_client;
-  preload_handle_->url_loader_client_request =
-      mojo::MakeRequest(&inner_url_loader_client);
-  auto url_loader_client = std::make_unique<DelegatingURLLoaderClient>(
-      std::move(inner_url_loader_client), std::move(on_response), request);
-  network::mojom::URLLoaderClientPtr url_loader_client_to_pass;
-  url_loader_client->Bind(&url_loader_client_to_pass);
-  network::mojom::URLLoaderPtr url_loader;
-
-  url_loader_factory->CreateLoaderAndStart(
-      mojo::MakeRequest(&url_loader), original_info->GetRouteID(), request_id,
-      network::mojom::kURLLoadOptionNone, request,
-      std::move(url_loader_client_to_pass),
-      net::MutableNetworkTrafficAnnotationTag(
-          original_request->traffic_annotation()));
-
-  preload_handle_->url_loader = url_loader.PassInterface();
-
-  DCHECK(!url_loader_assets_);
-  url_loader_assets_ = base::MakeRefCounted<URLLoaderAssets>(
-      std::move(url_loader_factory), std::move(url_loader_client));
-  return true;
-}
-
-// S13nServiceWorker
-bool ServiceWorkerFetchDispatcher::MaybeStartNavigationPreloadWithURLLoader(
     const network::ResourceRequest& original_request,
     URLLoaderFactoryGetter* url_loader_factory_getter,
     scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h
index cbf37a3d..f978d39 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.h
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -26,10 +26,6 @@
 #include "third_party/blink/public/mojom/fetch/fetch_api_response.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h"
 
-namespace net {
-class URLRequest;
-}  // namespace net
-
 namespace content {
 
 class ServiceWorkerContextWrapper;
@@ -67,11 +63,7 @@
   // If appropriate, starts the navigation preload request and creates
   // |preload_handle_|. Returns true if it started navigation preload.
   // |on_response| is invoked in OnReceiveResponse().
-  bool MaybeStartNavigationPreload(net::URLRequest* original_request,
-                                   base::OnceClosure on_response);
-  // S13nServiceWorker
-  // Same as above but for S13N.
-  bool MaybeStartNavigationPreloadWithURLLoader(
+  bool MaybeStartNavigationPreload(
       const network::ResourceRequest& original_request,
       URLLoaderFactoryGetter* url_loader_factory_getter,
       scoped_refptr<ServiceWorkerContextWrapper> context_wrapper,
diff --git a/content/browser/service_worker/service_worker_installed_script_loader.h b/content/browser/service_worker/service_worker_installed_script_loader.h
index 18733b0c..a23cadd 100644
--- a/content/browser/service_worker/service_worker_installed_script_loader.h
+++ b/content/browser/service_worker/service_worker_installed_script_loader.h
@@ -18,9 +18,8 @@
 
 class ServiceWorkerVersion;
 
-// S13nServiceWorker: A URLLoader that loads an installed service worker script
-// for a service worker that doesn't have a
-// ServiceWorkerInstalledScriptsManager.
+// A URLLoader that loads an installed service worker script for a service
+// worker that doesn't have a ServiceWorkerInstalledScriptsManager.
 //
 // Some cases where this happens:
 // - a new (non-installed) service worker requests a script that it already
diff --git a/content/browser/service_worker/service_worker_navigation_loader.cc b/content/browser/service_worker/service_worker_navigation_loader.cc
index 580f7bb..e5f5d22 100644
--- a/content/browser/service_worker/service_worker_navigation_loader.cc
+++ b/content/browser/service_worker/service_worker_navigation_loader.cc
@@ -224,11 +224,10 @@
                      active_worker->running_status()),
       base::BindOnce(&ServiceWorkerNavigationLoader::DidDispatchFetchEvent,
                      weak_factory_.GetWeakPtr()));
-  did_navigation_preload_ =
-      fetch_dispatcher_->MaybeStartNavigationPreloadWithURLLoader(
-          resource_request_, url_loader_factory_getter_.get(),
-          std::move(context), provider_host_->web_contents_getter(),
-          base::DoNothing(/* TODO(crbug/762357): metrics? */));
+  did_navigation_preload_ = fetch_dispatcher_->MaybeStartNavigationPreload(
+      resource_request_, url_loader_factory_getter_.get(), std::move(context),
+      provider_host_->web_contents_getter(),
+      base::DoNothing(/* TODO(crbug/762357): metrics? */));
 
   // Record worker start time here as |fetch_dispatcher_| will start a service
   // worker if there is no running service worker.
diff --git a/content/browser/service_worker/service_worker_navigation_loader.h b/content/browser/service_worker/service_worker_navigation_loader.h
index 8c657f3..18b82555 100644
--- a/content/browser/service_worker/service_worker_navigation_loader.h
+++ b/content/browser/service_worker/service_worker_navigation_loader.h
@@ -30,7 +30,6 @@
 class ServiceWorkerVersion;
 class ServiceWorkerProviderHost;
 
-// S13nServiceWorker:
 // ServiceWorkerNavigationLoader is the URLLoader used for main resource
 // requests (i.e., navigation and shared worker requests) that (potentially) go
 // through a service worker. This loader is only used for the main resource
diff --git a/content/browser/service_worker/service_worker_new_script_loader.h b/content/browser/service_worker/service_worker_new_script_loader.h
index 532b50d97..ff2018fe 100644
--- a/content/browser/service_worker/service_worker_new_script_loader.h
+++ b/content/browser/service_worker/service_worker_new_script_loader.h
@@ -21,7 +21,6 @@
 class ServiceWorkerVersion;
 struct HttpResponseInfoIOBuffer;
 
-// S13nServiceWorker:
 // This is the URLLoader used for loading scripts for a new (installing) service
 // worker. It fetches the script (the main script or imported script) from
 // network, and returns the response to |client|, while also writing the
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index 88f4f18..2596395 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -144,7 +144,6 @@
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr*
           out_provider_info);
 
-  // S13nServiceWorker:
   // Used for starting a shared worker. Returns a provider host for the shared
   // worker and fills |out_provider_info| with info to send to the renderer to
   // connect to the host. The host stays alive as long as this info stays alive
@@ -210,7 +209,6 @@
     return running_hosted_version_.get();
   }
 
-  // S13nServiceWorker:
   // For service worker clients. Similar to EnsureControllerServiceWorker, but
   // this returns a bound Mojo ptr which is supposed to be sent to clients. The
   // controller ptr passed to the clients will be used to intercept requests
@@ -406,7 +404,6 @@
   // cache.
   void NotifyControllerLost();
 
-  // S13nServiceWorker:
   // For service worker clients. Called when |version| is the active worker upon
   // the main resource request for this client. Remembers |version| as needing
   // a Soft Update. To avoid affecting page load performance, the update occurs
@@ -420,10 +417,6 @@
   //
   // This can be called multiple times due to redirects during a main resource
   // load. All service workers are updated.
-  //
-  // For non-S13nServiceWorker: The update logic is controlled entirely by
-  // ServiceWorkerControlleeRequestHandler, which sees all resource request
-  // activity and schedules an update at a convenient time.
   void AddServiceWorkerToUpdate(scoped_refptr<ServiceWorkerVersion> version);
 
   // For service worker clients. |callback| is called when this client becomes
diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc
index a7a8098..028b022a0 100644
--- a/content/browser/service_worker/service_worker_registration_unittest.cc
+++ b/content/browser/service_worker/service_worker_registration_unittest.cc
@@ -549,11 +549,11 @@
 
   // Remove the controllee. Since there is an in-flight request,
   // activation should not yet happen.
-  // When S13nServiceWorker is on, the idle timer living in the renderer is
-  // requested to notify the browser the idle state ASAP.
   version_1->RemoveControllee(controllee()->client_uuid());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(version_1.get(), reg->active_version());
+  // The idle timer living in the renderer is requested to notify the idle state
+  // to the browser ASAP.
   EXPECT_TRUE(version_1_service_worker()->is_zero_idle_timer_delay());
 
   // Finish the request. Activation should happen.
@@ -582,10 +582,8 @@
   EXPECT_EQ(version_1.get(), reg->active_version());
   EXPECT_TRUE(version_1_service_worker()->is_zero_idle_timer_delay());
 
-  // Finish the request.
-  // non-S13nServiceWorker: The service worker becomes idle.
-  // S13nServiceWorker: FinishRequest() doesn't immediately make the worker
-  // "no work" state. It needs to be notfied the idle state by
+  // Finish the request. FinishRequest() doesn't immediately make the worker
+  // reach the "no work" state. It needs to be notfied of the idle state by
   // RequestTermination().
   version_1->FinishRequest(inflight_request_id(), true /* was_handled */);
 
@@ -612,10 +610,7 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(version_1.get(), reg->active_version());
 
-  // Call skipWaiting.
-  // non-S13nServiceWorker: Activation should happen.
-  // S13nServiceWorker: Activation should happen after RequestTermination is
-  // triggered.
+  // Call skipWaiting. Activation happens after RequestTermination is triggered.
   base::Optional<bool> result;
   base::RunLoop skip_waiting_loop;
   SimulateSkipWaitingWithCallback(version_2.get(), &result,
diff --git a/content/browser/service_worker/service_worker_script_loader_factory.h b/content/browser/service_worker/service_worker_script_loader_factory.h
index 62f54b0..adb571a 100644
--- a/content/browser/service_worker/service_worker_script_loader_factory.h
+++ b/content/browser/service_worker/service_worker_script_loader_factory.h
@@ -22,7 +22,6 @@
 class ServiceWorkerContextCore;
 class ServiceWorkerProviderHost;
 
-// S13nServiceWorker:
 // Created per one running service worker for loading its scripts. This is kept
 // alive while the WebServiceWorkerNetworkProvider in the renderer process is
 // alive.
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 8625cd74..ba36f0a 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -602,10 +602,9 @@
   if (expiration_time > max_request_expiration_time_)
     max_request_expiration_time_ = expiration_time;
 
-  // S13nServiceWorker:
   // Even if the worker is in the idle state, the new event which is about to
   // be dispatched will reset the idle status. That means the worker can receive
-  // events directly from any clients, so we cannot trigger OnNoWork after this
+  // events directly from any client, so we cannot trigger OnNoWork after this
   // point.
   worker_is_idle_on_renderer_ = false;
   return request_id;
@@ -1635,7 +1634,6 @@
   if (!pause_after_download())
     InitializeGlobalScope();
 
-  // S13nServiceWorker:
   if (!controller_request_.is_pending()) {
     DCHECK(!controller_ptr_.is_bound());
     controller_request_ = mojo::MakeRequest(&controller_ptr_);
diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h
index 800ac889..7e346b38 100644
--- a/content/browser/service_worker/service_worker_version.h
+++ b/content/browser/service_worker/service_worker_version.h
@@ -258,7 +258,6 @@
   // soon after the service worker becomes idle if a waiting worker exists.
   void TriggerIdleTerminationAsap();
 
-  // S13nServiceWorker:
   // Called when the renderer notifies the browser that the worker is now idle.
   // Returns true if the worker will be terminated and the worker should not
   // handle any events dispatched directly from clients (e.g. FetchEvents for
@@ -344,10 +343,8 @@
     return service_worker_ptr_.get();
   }
 
-  // S13nServiceWorker:
-  // Returns the 'controller' interface ptr of this worker. It is expected
-  // that the worker is already starting or running, or is going to be started
-  // soon.
+  // Returns the 'controller' interface ptr of this worker. It is expected that
+  // the worker is already starting or running, or is going to be started soon.
   // TODO(kinuko): Relying on the callsites to start the worker when it's
   // not running is a bit sketchy, maybe this should queue a task to check
   // if the pending request is pending too long? https://crbug.com/797222
@@ -723,13 +720,10 @@
   // ping.
   void StopWorkerIfIdle();
 
-  // Non-S13nServiceWorker: returns true if the service worker has work to do:
-  // it has inflight requests, in-progress streaming URLRequestJobs, or pending
-  // start callbacks.
+  // Returns true if the service worker is known to have work to do because the
+  // browser process initiated a request to the service worker which isn't done
+  // yet.
   //
-  // S13nServiceWorker: returns true if the service worker has work to do:
-  // because the browser process initiated a request to the service worker which
-  // isn't done yet.
   // Note that this method may return false even when the service worker still
   // has work to do; clients may dispatch events to the service worker directly.
   // You can ensure no inflight requests exist when HasWorkInBrowser() returns
@@ -789,10 +783,9 @@
   void CleanUpExternalRequest(const std::string& request_uuid,
                               blink::ServiceWorkerStatusCode status);
 
-  // Called if no inflight events exist on the browser process.
-  // Non-S13nServiceWorker: Triggers OnNoWork().
-  // S13nServiceWorker: Triggers OnNoWork() if the renderer-side idle timeout
-  // has been fired or the worker has been stopped.
+  // Called if no inflight events exist on the browser process. Triggers
+  // OnNoWork() if the renderer-side idle timeout has been fired or the worker
+  // has been stopped.
   void OnNoWorkInBrowser();
 
   bool IsStartWorkerAllowed() const;
@@ -852,7 +845,7 @@
   // Connected to ServiceWorkerContextClient while the worker is running.
   blink::mojom::ServiceWorkerPtr service_worker_ptr_;
 
-  // S13nServiceWorker: connected to the controller service worker.
+  // Connection to the controller service worker.
   // |controller_request_| is non-null only when the |controller_ptr_| is
   // requested before the worker is started, it is passed to the worker (and
   // becomes null) once it's started.
@@ -873,14 +866,13 @@
   // an inflight response.
   int inflight_stream_response_count_ = 0;
 
-  // S13nServiceWorker:
   // Set to true if the worker has no inflight events and the idle timer has
   // been triggered. Set back to false if another event starts since the worker
   // is no longer idle.
   bool worker_is_idle_on_renderer_ = true;
 
-  // S13nServiceWorker: Set to true when the worker needs to be terminated as
-  // soon as possible (e.g. activation).
+  // Set to true when the worker needs to be terminated as soon as possible
+  // (e.g. activation).
   bool needs_to_be_terminated_asap_ = false;
 
   // Keeps track of the provider hosting this running service worker for this
diff --git a/content/browser/worker_host/worker_script_loader_factory.h b/content/browser/worker_host/worker_script_loader_factory.h
index 7ff56fd..27a53af 100644
--- a/content/browser/worker_host/worker_script_loader_factory.h
+++ b/content/browser/worker_host/worker_script_loader_factory.h
@@ -20,7 +20,6 @@
 class ResourceContext;
 class WorkerScriptLoader;
 
-// S13nServiceWorker:
 // WorkerScriptLoaderFactory creates a WorkerScriptLoader to load the main
 // script for a web worker (dedicated worker or shared worker), which follows
 // redirects and sets the controller service worker on the web worker if needed.
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
index 0c55f93..07ff7c6 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/scheduler/UiThreadSchedulerTest.java
@@ -28,6 +28,7 @@
 import org.chromium.content_public.browser.BrowserTaskExecutor;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.test.NativeLibraryTestRule;
+import org.chromium.content_public.browser.test.util.UiThreadSchedulerTestUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -43,6 +44,8 @@
 
     @Before
     public void setUp() {
+        // We don't load the browser process since we want tests to control when content
+        // is started and hence the native secheduler is ready.
         mNativeLibraryTestRule.loadNativeLibraryNoBrowserProcess();
         ThreadUtils.setUiThread(null);
         ThreadUtils.setWillOverrideUiThread(true);
@@ -52,10 +55,12 @@
         BrowserTaskExecutor.register();
         BrowserTaskExecutor.setShouldPrioritizeBootstrapTasks(true);
         mHandler = new Handler(mUiThread.getLooper());
+        UiThreadSchedulerTestUtils.postBrowserMainLoopStartupTasks(false);
     }
 
     @After
     public void tearDown() {
+        UiThreadSchedulerTestUtils.postBrowserMainLoopStartupTasks(true);
         mUiThread.quitSafely();
         ThreadUtils.setUiThread(null);
         ThreadUtils.setWillOverrideUiThread(false);
@@ -157,6 +162,8 @@
     public void testTaskNotRunOnUiThreadWithoutUiThreadTaskTraits() throws Exception {
         TaskRunner uiThreadTaskRunner = PostTask.createSingleThreadTaskRunner(new TaskTraits());
         try {
+            // Test times out without this.
+            UiThreadSchedulerTestUtils.postBrowserMainLoopStartupTasks(true);
             startContentMainOnUiThread();
 
             uiThreadTaskRunner.postTask(new Runnable() {
diff --git a/content/public/browser/navigation_throttle.cc b/content/public/browser/navigation_throttle.cc
index 45ff97e..552b1af 100644
--- a/content/public/browser/navigation_throttle.cc
+++ b/content/public/browser/navigation_throttle.cc
@@ -85,7 +85,9 @@
     resume_callback_.Run();
     return;
   }
-  static_cast<NavigationHandleImpl*>(navigation_handle_)->Resume(this);
+  static_cast<NavigationHandleImpl*>(navigation_handle_)
+      ->navigation_request()
+      ->Resume(this);
 }
 
 void NavigationThrottle::CancelDeferredNavigation(
@@ -95,6 +97,7 @@
     return;
   }
   static_cast<NavigationHandleImpl*>(navigation_handle_)
+      ->navigation_request()
       ->CancelDeferredNavigation(this, result);
 }
 
diff --git a/content/public/test/android/BUILD.gn b/content/public/test/android/BUILD.gn
index 6705870d..9f36f4c 100644
--- a/content/public/test/android/BUILD.gn
+++ b/content/public/test/android/BUILD.gn
@@ -55,6 +55,7 @@
     "javatests/src/org/chromium/content_public/browser/test/util/TestTouchUtils.java",
     "javatests/src/org/chromium/content_public/browser/test/util/TestWebContentsObserver.java",
     "javatests/src/org/chromium/content_public/browser/test/util/TouchCommon.java",
+    "javatests/src/org/chromium/content_public/browser/test/util/UiThreadSchedulerTestUtils.java",
     "javatests/src/org/chromium/content_public/browser/test/util/UiUtils.java",
     "javatests/src/org/chromium/content_public/browser/test/util/WebContentsUtils.java",
   ]
@@ -67,6 +68,7 @@
     "javatests/src/org/chromium/content_public/browser/test/InterstitialPageDelegateAndroid.java",
     "javatests/src/org/chromium/content_public/browser/test/RenderFrameHostTestExt.java",
     "javatests/src/org/chromium/content_public/browser/test/util/DOMUtils.java",
+    "javatests/src/org/chromium/content_public/browser/test/util/UiThreadSchedulerTestUtils.java",
     "javatests/src/org/chromium/content_public/browser/test/util/WebContentsUtils.java",
   ]
 }
@@ -86,6 +88,7 @@
     "interstitial_page_delegate_android.h",
     "render_frame_host_test_ext.cc",
     "render_frame_host_test_ext.h",
+    "ui_thread_scheduler_test_utils.cc",
     "web_contents_utils.cc",
   ]
   deps = [
diff --git a/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/UiThreadSchedulerTestUtils.java b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/UiThreadSchedulerTestUtils.java
new file mode 100644
index 0000000..ab73af9
--- /dev/null
+++ b/content/public/test/android/javatests/src/org/chromium/content_public/browser/test/util/UiThreadSchedulerTestUtils.java
@@ -0,0 +1,24 @@
+// 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.content_public.browser.test.util;
+
+import org.chromium.base.annotations.JNINamespace;
+
+/**
+ * Helper methods for testing the UiThreadScheduler
+ */
+@JNINamespace("content")
+public class UiThreadSchedulerTestUtils {
+    /**
+     * @param enabled Whether or not BrowserMainLoop::CreateStartupTasks should post tasks. This
+     *        is useful because they will crash in in some testing scenarios despite not being
+     *        needed for the test.
+     */
+    public static void postBrowserMainLoopStartupTasks(boolean enabled) {
+        nativePostBrowserMainLoopStartupTasks(enabled);
+    }
+
+    private static native void nativePostBrowserMainLoopStartupTasks(boolean enabled);
+}
diff --git a/content/public/test/android/ui_thread_scheduler_test_utils.cc b/content/public/test/android/ui_thread_scheduler_test_utils.cc
new file mode 100644
index 0000000..fa022bd3
--- /dev/null
+++ b/content/public/test/android/ui_thread_scheduler_test_utils.cc
@@ -0,0 +1,17 @@
+// 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 "content/browser/browser_main_loop.h"
+#include "jni/UiThreadSchedulerTestUtils_jni.h"
+
+namespace content {
+
+void JNI_UiThreadSchedulerTestUtils_PostBrowserMainLoopStartupTasks(
+    JNIEnv* env,
+    jboolean enabled) {
+  BrowserMainLoop::EnableStartupTasks(enabled);
+}
+
+}  // namespace content
diff --git a/content/public/test/hit_test_region_observer.cc b/content/public/test/hit_test_region_observer.cc
index 06ae46f..a97c28f 100644
--- a/content/public/test/hit_test_region_observer.cc
+++ b/content/public/test/hit_test_region_observer.cc
@@ -11,6 +11,7 @@
 #include "components/viz/host/hit_test/hit_test_query.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
+#include "components/viz/service/surfaces/surface.h"
 #include "components/viz/service/surfaces/surface_manager.h"
 #include "content/browser/compositor/surface_utils.h"
 #include "content/browser/frame_host/cross_process_frame_connector.h"
diff --git a/content/renderer/loader/web_url_request_util.h b/content/renderer/loader/web_url_request_util.h
index 58d5763..213dbfb 100644
--- a/content/renderer/loader/web_url_request_util.h
+++ b/content/renderer/loader/web_url_request_util.h
@@ -49,7 +49,7 @@
     std::vector<blink::mojom::BlobPtrInfo> blob_ptrs);
 
 // Takes a ResourceRequestBody and gets blob pointers for Blob entries.
-// Used only in non-NetworkService cases but with S13nServiceWorker.
+// Used only in non-NetworkService case.
 // TODO(kinuko): Remove this once Network Service is shipped.
 std::vector<blink::mojom::BlobPtrInfo> GetBlobPtrsForRequestBody(
     const network::ResourceRequestBody& input);
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.h b/content/renderer/loader/web_worker_fetch_context_impl.h
index f49055e..e7561d9 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.h
+++ b/content/renderer/loader/web_worker_fetch_context_impl.h
@@ -59,7 +59,6 @@
   // security reasons like sandboxed iframes, insecure origins etc.
   // |loader_factory_info| is used for regular loading by the worker.
   //
-  // S13nServiceWorker:
   // If the worker is controlled by a service worker, this class makes another
   // loader factory which sends requests to the service worker, and passes
   // |fallback_factory_info| to that factory to use for network fallback.
@@ -171,7 +170,6 @@
 
   bool Send(IPC::Message* message);
 
-  // S13nServiceWorker:
   // Resets the service worker url loader factory of a URLLoaderFactoryImpl
   // which was passed to Blink. The url loader factory is connected to the
   // controller service worker. Sets nullptr if the worker context is not
@@ -205,14 +203,12 @@
   blink::mojom::ControllerServiceWorkerMode is_controlled_by_service_worker_ =
       blink::mojom::ControllerServiceWorkerMode::kNoController;
 
-  // S13nServiceWorker:
   // Initialized on the worker thread when InitializeOnWorkerThread() is called.
   // This can be null if the |provider_context| passed to Create() was null or
   // already being destructed (see
   // ServiceWorkerProviderContext::OnNetworkProviderDestroyed()).
   blink::mojom::ServiceWorkerContainerHostPtr service_worker_container_host_;
 
-  // S13nServiceWorker:
   // The Client#id value of the shared worker or dedicated worker (since
   // dedicated workers are not yet service worker clients, it is the parent
   // document's id in that case). Passed to ControllerServiceWorkerConnector.
@@ -223,17 +219,15 @@
 
   // Initialized on the worker thread when InitializeOnWorkerThread() is called.
   // |loader_factory_| is used for regular loading by the worker. In
-  // S13nServiceWorker, if the worker is controlled by a service worker, it
-  // creates a ServiceWorkerSubresourceLoaderFactory instead.
+  // If the worker is controlled by a service worker, it creates a
+  // ServiceWorkerSubresourceLoaderFactory instead.
   scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;
 
   // Initialized on the worker thread when InitializeOnWorkerThread() is called.
-  // S13nServiceWorker: If the worker is controlled by a service worker, it
-  // passes this factory to ServiceWorkerSubresourceLoaderFactory to use for
-  // network fallback.
+  // If the worker is controlled by a service worker, it passes this factory to
+  // ServiceWorkerSubresourceLoaderFactory to use for network fallback.
   scoped_refptr<network::SharedURLLoaderFactory> fallback_factory_;
 
-  // S13nServiceWorker:
   // Initialized on the worker thread when InitializeOnWorkerThread() is called.
   scoped_refptr<base::RefCountedData<blink::mojom::BlobRegistryPtr>>
       blob_registry_;
diff --git a/content/renderer/p2p/ipc_socket_factory.cc b/content/renderer/p2p/ipc_socket_factory.cc
index 135f41c8..78816b1a 100644
--- a/content/renderer/p2p/ipc_socket_factory.cc
+++ b/content/renderer/p2p/ipc_socket_factory.cc
@@ -601,12 +601,8 @@
   in_flight_packet_records_.pop_front();
   TraceSendThrottlingState();
 
-  int64_t send_time_ms = -1;
-  if (send_metrics.rtc_packet_id >= 0) {
-    send_time_ms = send_metrics.send_time.since_origin().InMilliseconds();
-  }
   SignalSentPacket(this, rtc::SentPacket(send_metrics.rtc_packet_id,
-                                         send_time_ms));
+                                         send_metrics.send_time_ms));
 
   if (writable_signal_expected_ && send_bytes_available_ > 0) {
     WebRtcLogMessage(base::StringPrintf(
diff --git a/content/renderer/service_worker/controller_service_worker_impl.h b/content/renderer/service_worker/controller_service_worker_impl.h
index c05ab0b..d2e56d8 100644
--- a/content/renderer/service_worker/controller_service_worker_impl.h
+++ b/content/renderer/service_worker/controller_service_worker_impl.h
@@ -19,7 +19,6 @@
 
 class ServiceWorkerContextClient;
 
-// S13nServiceWorker:
 // An instance of this class is created on the service worker thread
 // when ServiceWorkerContextClient's WorkerContextData is created.
 // This implements blink::mojom::ControllerServiceWorker and its Mojo endpoint
@@ -29,8 +28,8 @@
 // Fetch events via the Mojo endpoints.
 //
 // TODO(kinuko): Implement self-killing timer, that does something similar to
-// what ServiceWorkerVersion::StopWorkerIfIdle does in the browser process in
-// non-S13n code.
+// what ServiceWorkerVersion::StopWorkerIfIdle did in the browser process in
+// the non-S13n codepath.
 class ControllerServiceWorkerImpl
     : public blink::mojom::ControllerServiceWorker {
  public:
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index 88e444a..c2b72a6 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -264,12 +264,10 @@
   // Inflight navigation preload requests.
   base::IDMap<std::unique_ptr<NavigationPreloadRequest>> preload_requests;
 
-  // S13nServiceWorker
   // Timer triggered when the service worker considers it should be stopped or
   // an event should be aborted.
   std::unique_ptr<ServiceWorkerTimeoutTimer> timeout_timer;
 
-  // S13nServiceWorker
   // |controller_impl| should be destroyed before |timeout_timer| since the
   // pipe needs to be disconnected before callbacks passed by
   // DispatchSomeEvent() get destructed, which may be stored in |timeout_timer|
@@ -1544,7 +1542,6 @@
           .To<blink::WebServiceWorkerObjectInfo>());
 }
 
-// S13nServiceWorker
 void ServiceWorkerContextClient::DispatchFetchEvent(
     blink::mojom::DispatchFetchEventParamsPtr params,
     blink::mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 9621091d..440ebcfb 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -427,7 +427,6 @@
   // startup completes.
   blink::mojom::EmbeddedWorkerStartTimingPtr start_timing_;
 
-  // S13nServiceWorker:
   // A URLLoaderFactory instance used for subresource loading.
   scoped_refptr<HostChildURLLoaderFactoryBundle> loader_factories_;
 
diff --git a/content/renderer/service_worker/service_worker_network_provider_for_frame.cc b/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
index 31af349..3742531 100644
--- a/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
+++ b/content/renderer/service_worker/service_worker_network_provider_for_frame.cc
@@ -110,11 +110,7 @@
     request.SetSkipServiceWorker(true);
   }
 
-  // Inject this frame's fetch window id into the request. This is really only
-  // needed for subresource requests in S13nServiceWorker. For main resource
-  // requests or non-S13nSW case, the browser process sets the id on the
-  // request when dispatching the fetch event to the service worker. But it
-  // doesn't hurt to set it always.
+  // Inject this frame's fetch window id into the request.
   if (context())
     request.SetFetchWindowId(context()->fetch_request_window_id());
 }
diff --git a/content/renderer/service_worker/service_worker_network_provider_for_frame.h b/content/renderer/service_worker/service_worker_network_provider_for_frame.h
index 3338fc2..c70b045 100644
--- a/content/renderer/service_worker/service_worker_network_provider_for_frame.h
+++ b/content/renderer/service_worker/service_worker_network_provider_for_frame.h
@@ -23,7 +23,6 @@
  public:
   // Creates a network provider for |frame|.
   //
-  // For S13nServiceWorker:
   // |controller_info| contains the endpoint and object info that is needed to
   // set up the controller service worker for the client.
   // |fallback_loader_factory| is a default loader factory for fallback
diff --git a/content/renderer/service_worker/service_worker_provider_context.h b/content/renderer/service_worker/service_worker_provider_context.h
index 835b6dd..c58cfc0 100644
--- a/content/renderer/service_worker/service_worker_provider_context.h
+++ b/content/renderer/service_worker/service_worker_provider_context.h
@@ -69,10 +69,9 @@
   // the content::ServiceWorkerProviderHost that notifies of changes to the
   // registration's and workers' status. |request| is bound with |binding_|.
   //
-  // |controller_info| contains the endpoint (which is non-null only when
-  // S13nServiceWorker is enabled) and object info that is needed to set up the
-  // controller service worker for the context.
-  // For S13nServiceWorker:
+  // |controller_info| contains the endpoint and object info that is needed to
+  // set up the controller service worker for the context.
+  //
   // |fallback_loader_factory| is a default loader factory for fallback
   // requests, and is used when we create a subresource loader for controllees.
   // This is non-null only if the provider is created for controllees, and if
@@ -101,7 +100,6 @@
   // any, otherwise returns nullptr.
   blink::mojom::ServiceWorkerObjectInfoPtr TakeController();
 
-  // S13nServiceWorker:
   // Returns URLLoaderFactory for loading subresources with the controller
   // ServiceWorker, or nullptr if no controller is attached.
   network::mojom::URLLoaderFactory* GetSubresourceLoaderFactory();
@@ -109,7 +107,6 @@
   // Returns the feature usage of the controller service worker.
   const std::set<blink::mojom::WebFeature>& used_features() const;
 
-  // S13nServiceWorker:
   // The Client#id value of the client.
   const std::string& client_id() const;
 
@@ -131,7 +128,6 @@
   void CloneWorkerClientRegistry(
       blink::mojom::ServiceWorkerWorkerClientRegistryRequest request) override;
 
-  // S13nServiceWorker:
   // Returns a ServiceWorkerContainerHostPtrInfo to this context's container
   // host. This can return null after OnNetworkProviderDestroyed() is called
   // (in which case |this| will be destroyed soon).
@@ -194,7 +190,6 @@
                            blink::TransferableMessage message) override;
   void CountFeature(blink::mojom::WebFeature feature) override;
 
-  // S13nServiceWorker:
   // A convenient utility method to tell if a subresource loader factory
   // can be created for this context.
   bool CanCreateSubresourceLoaderFactory() const;
diff --git a/content/renderer/service_worker/service_worker_provider_context_unittest.cc b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
index 0a7bf9f1..cf3722e 100644
--- a/content/renderer/service_worker/service_worker_provider_context_unittest.cc
+++ b/content/renderer/service_worker/service_worker_provider_context_unittest.cc
@@ -126,8 +126,8 @@
   std::set<blink::mojom::WebFeature> used_features_;
 };
 
-// S13nServiceWorker: a fake URLLoaderFactory implementation that basically
-// does nothing but records the requests.
+// A fake URLLoaderFactory implementation that basically does nothing but
+// records the requests.
 class FakeURLLoaderFactory final : public network::mojom::URLLoaderFactory {
  public:
   FakeURLLoaderFactory() = default;
@@ -173,8 +173,8 @@
   DISALLOW_COPY_AND_ASSIGN(FakeURLLoaderFactory);
 };
 
-// S13nServiceWorker: a fake ControllerServiceWorker implementation that
-// basically does nothing but records DispatchFetchEvent calls.
+// A fake ControllerServiceWorker implementation that basically does nothing but
+// records DispatchFetchEvent calls.
 class FakeControllerServiceWorker
     : public blink::mojom::ControllerServiceWorker {
  public:
@@ -264,7 +264,7 @@
  public:
   ServiceWorkerProviderContextTest() = default;
 
-  void EnableS13nServiceWorker() {
+  void EnableNetworkService() {
     scoped_feature_list_.InitAndEnableFeature(
         network::features::kNetworkService);
     network::mojom::URLLoaderFactoryPtr fake_loader_factory;
@@ -294,8 +294,6 @@
 
  protected:
   base::test::ScopedTaskEnvironment task_environment;
-
-  // S13nServiceWorker:
   base::test::ScopedFeatureList scoped_feature_list_;
   FakeURLLoaderFactory fake_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;
@@ -414,10 +412,10 @@
   EXPECT_TRUE(client->was_set_controller_called());
 }
 
-// S13nServiceWorker: Test that SetController correctly sets (or resets)
-// the controller service worker for clients.
+// Test that SetController correctly sets (or resets) the controller service
+// worker for clients.
 TEST_F(ServiceWorkerProviderContextTest, SetControllerServiceWorker) {
-  EnableS13nServiceWorker();
+  EnableNetworkService();
   const int kProviderId = 10;
 
   // Make the ServiceWorkerContainerHost implementation and
@@ -608,7 +606,7 @@
 }
 
 TEST_F(ServiceWorkerProviderContextTest, ControllerWithoutFetchHandler) {
-  EnableS13nServiceWorker();
+  EnableNetworkService();
   const int kProviderId = 10;
   auto object_host =
       std::make_unique<MockServiceWorkerObjectHost>(200 /* version_id */);
diff --git a/content/renderer/service_worker/service_worker_provider_state_for_client.h b/content/renderer/service_worker/service_worker_provider_state_for_client.h
index 9d0d216..d07d11c 100644
--- a/content/renderer/service_worker/service_worker_provider_state_for_client.h
+++ b/content/renderer/service_worker/service_worker_provider_state_for_client.h
@@ -36,16 +36,13 @@
   // Keeps version id of the current controller service worker object.
   int64_t controller_version_id = blink::mojom::kInvalidServiceWorkerVersionId;
 
-  // S13nServiceWorker:
   // Used to intercept requests from the controllee and dispatch them
   // as events to the controller ServiceWorker.
   network::mojom::URLLoaderFactoryPtr subresource_loader_factory;
 
-  // S13nServiceWorker:
   // Used when we create |subresource_loader_factory|.
   scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory;
 
-  // S13nServiceWorker:
   // The Client#id value of the client.
   std::string client_id;
 
@@ -75,7 +72,6 @@
   mojo::BindingSet<blink::mojom::ServiceWorkerWorkerClientRegistry>
       worker_client_registry_bindings;
 
-  // S13nServiceWorker
   // Used in |subresource_loader_factory| to get the connection to the
   // controller service worker.
   //
diff --git a/content/renderer/service_worker/service_worker_subresource_loader.h b/content/renderer/service_worker/service_worker_subresource_loader.h
index 1fe2aed..9ada5a12 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader.h
+++ b/content/renderer/service_worker/service_worker_subresource_loader.h
@@ -30,7 +30,6 @@
 
 class ControllerServiceWorkerConnector;
 
-// S13nServiceWorker:
 // A custom URLLoader implementation used by Service Worker controllees
 // for loading subresources via the controller Service Worker.
 // Currently an instance of this class is created and used only on
@@ -198,7 +197,6 @@
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerSubresourceLoader);
 };
 
-// S13nServiceWorker:
 // A custom URLLoaderFactory implementation used by Service Worker controllees
 // for loading subresources via the controller Service Worker.
 // Self destroys when no more bindings exist.
diff --git a/content/test/data/accessibility/event/text-changed-contenteditable-expected-auralinux.txt b/content/test/data/accessibility/event/text-changed-contenteditable-expected-auralinux.txt
new file mode 100644
index 0000000..1d49e7e7
--- /dev/null
+++ b/content/test/data/accessibility/event/text-changed-contenteditable-expected-auralinux.txt
@@ -0,0 +1,24 @@
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+STATE-CHANGE:DEFUNCT:TRUE role=ROLE_INVALID name='(null)' DEFUNCT
+TEXT-INSERT (start=0 length=16 'Before Div After') role=ROLE_SECTION name='(null)' EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT
+TEXT-INSERT (start=0 length=22 'Before Paragraph After') role=ROLE_PARAGRAPH name='(null)' EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT
+TEXT-INSERT (start=0 length=9 'Modified ') role=ROLE_PARAGRAPH name='(null)' EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT
+TEXT-INSERT (start=0 length=9 'Modified ') role=ROLE_SECTION name='(null)' EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT
+TEXT-REMOVE (start=0 length=3 'Div') role=ROLE_SECTION name='(null)' EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT
+TEXT-REMOVE (start=0 length=3 'Div') role=ROLE_SECTION name='(null)' EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT
+TEXT-REMOVE (start=0 length=9 'Paragraph') role=ROLE_PARAGRAPH name='(null)' EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT
+TEXT-REMOVE (start=0 length=9 'Paragraph') role=ROLE_PARAGRAPH name='(null)' EDITABLE,ENABLED,FOCUSABLE,MULTI-LINE,SENSITIVE,SHOWING,VISIBLE,SELECTABLE-TEXT
diff --git a/content/test/data/accessibility/event/text-changed-contenteditable.html b/content/test/data/accessibility/event/text-changed-contenteditable.html
new file mode 100644
index 0000000..db0f3248
--- /dev/null
+++ b/content/test/data/accessibility/event/text-changed-contenteditable.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<body>
+<p id="p1" contenteditable>Paragraph</p>
+<p id="p2" contenteditable>Paragraph</p>
+<p id="p3" contenteditable>Paragraph</p>
+<div id="d1" contenteditable>Div</div>
+<div id="d2" contenteditable>Div</div>
+<div id="d3" contenteditable>Div</div>
+<script>
+  function go() {
+    document.querySelector('#p1').textContent = 'Modified Paragraph';
+    document.querySelector('#p2').textContent = '';
+    document.querySelector('#p3').textContent = 'Before Paragraph After';
+    document.querySelector('#d1').textContent = 'Modified Div';
+    document.querySelector('#d2').textContent = '';
+    document.querySelector('#d3').textContent = 'Before Div After';
+  }
+</script>
+</body>
+</html>
diff --git a/docs/clangd.md b/docs/clangd.md
new file mode 100644
index 0000000..e8bfc29
--- /dev/null
+++ b/docs/clangd.md
@@ -0,0 +1,62 @@
+# Clangd
+
+## Introduction
+
+[clangd](https://clang.llvm.org/extra/clangd/) is a clang-based [language server](http://langserver.org/).
+It brings IDE features (e.g. diagnostics, code completion, code navigations) to
+your editor.
+
+## Getting clangd
+
+See [instructions](https://clang.llvm.org/extra/clangd/Installation.html#installing-clangd).
+
+**Googlers:** clangd has been installed on your glinux by default, just use
+`/usr/bin/clangd`.
+
+Alternative: use the following command to build clangd from LLVM source, and you
+will get the binary at
+`out/Release/tools/clang/third_party/llvm/build/bin/clangd`.
+
+```
+tools/clang/scripts/build_clang_tools_extra.py --fetch out/Release clangd
+```
+
+## Setting Up
+
+1. Build chrome normally. This step is required to make sure we have all
+generated files.
+
+```
+ninja -C out/Release chrome
+```
+
+2. Generate the compilation database, clangd needs it to know how to build a
+source file.
+
+```
+tools/clang/scripts/generate_compdb.py -p out/Release > compile_commands.json
+```
+
+Note: the compilation database is not re-generated automatically, you'd need to
+regenerate it manually when you have new files checked in.
+
+3. Use clangd in your favourite editor, see detailed [instructions](
+https://clang.llvm.org/extra/clangd/Installation.html#getting-started-with-clangd).
+
+## Index
+
+By default, clangd only knows the files you are currently editing. To provide
+project-wide code navigations (e.g. find references), clangd neesds a
+project-wide index.
+
+You can pass an **experimental** `--background-index` command line argument to
+clangd, clangd will incrementally build an index of Chromium in the background.
+Note: the first index time may take hours (for reference, it took 2~3 hours on
+a 48-core, 64GB machine).
+
+A full index of Chromium (including v8, blink) takes ~550 MB disk space and ~2.7
+GB memory in clangd.
+
+## Questions
+
+If you have any questions, reach out to clangd-dev@lists.llvm.org.
diff --git a/infra/config/cr-buildbucket.cfg b/infra/config/cr-buildbucket.cfg
index bbda8344..3345a58 100644
--- a/infra/config/cr-buildbucket.cfg
+++ b/infra/config/cr-buildbucket.cfg
@@ -1379,6 +1379,11 @@
     }
 
     builders {
+      name: "Linux FYI SkiaRenderer Vulkan (NVIDIA)"
+      mixins: "linux-gpu-fyi-ci"
+    }
+
+    builders {
       name: "Linux FYI Release (Intel HD 630)"
       mixins: "linux-gpu-fyi-ci"
     }
diff --git a/infra/config/luci-milo.cfg b/infra/config/luci-milo.cfg
index 776dd4ed..79a77ad 100644
--- a/infra/config/luci-milo.cfg
+++ b/infra/config/luci-milo.cfg
@@ -3263,6 +3263,11 @@
     short_name: "dqp"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Linux FYI SkiaRenderer Vulkan (NVIDIA)"
+    category: "Linux|Nvidia"
+    short_name: "skv"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Linux FYI GPU TSAN Release"
     category: "Linux"
     short_name: "tsn"
diff --git a/infra/config/luci-scheduler.cfg b/infra/config/luci-scheduler.cfg
index 72b78ae..5c6d9817 100644
--- a/infra/config/luci-scheduler.cfg
+++ b/infra/config/luci-scheduler.cfg
@@ -1548,6 +1548,17 @@
 }
 
 job {
+  id: "Linux FYI SkiaRenderer Vulkan (NVIDIA)"
+  # Triggered by "GPU FYI Linux Builder".
+  acl_sets: "triggered-by-parent-builders"
+  buildbucket: {
+    server: "cr-buildbucket.appspot.com"
+    bucket: "luci.chromium.ci"
+    builder: "Linux FYI SkiaRenderer Vulkan (NVIDIA)"
+  }
+}
+
+job {
   id: "Optional Linux Release (Intel HD 630)"
   # Triggered by "GPU FYI Linux Builder".
   acl_sets: "triggered-by-parent-builders"
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index d360d3fb..368072f 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -20,6 +20,7 @@
 #include "components/autofill/core/common/password_form_fill_data.h"
 #include "components/password_manager/core/browser/log_manager.h"
 #include "components/password_manager/core/browser/mock_password_store.h"
+#include "components/password_manager/core/browser/new_password_form_manager.h"
 #include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/password_manager/core/browser/stub_password_manager_client.h"
 #include "components/password_manager/core/common/password_manager_pref_names.h"
@@ -203,6 +204,14 @@
 
   void SetUp() override {
     ChromeWebTest::SetUp();
+
+    // When waiting for predictions is on, it makes tests more complicated.
+    // Disable wating, since most tests have nothing to do with predictions. All
+    // tests that test working with prediction should explicitly turn
+    // predictions on.
+    password_manager::NewPasswordFormManager::
+        set_wait_for_server_predictions_for_filling(false);
+
     passwordController_ =
         CreatePasswordController(web_state(), store_.get(), &weak_client_);
     @autoreleasepool {
@@ -1188,8 +1197,10 @@
   password_manager::PasswordStore::FormDigest expected_form_digest(
       autofill::PasswordForm::SCHEME_HTML, "https://chromium.test/",
       GURL("https://chromium.test/"));
+  // TODO(crbug.com/949519): replace WillRepeatedly with WillOnce when the old
+  // parser is gone.
   EXPECT_CALL(*store_, GetLogins(expected_form_digest, _))
-      .WillOnce(testing::Invoke(
+      .WillRepeatedly(testing::Invoke(
           [&get_logins_called](
               const password_manager::PasswordStore::FormDigest&,
               password_manager::PasswordStoreConsumer*) {
@@ -1315,8 +1326,10 @@
 
     if (store_has_credentials) {
       PasswordForm form(CreatePasswordForm(BaseUrl().c_str(), "user", "pw"));
+      // TODO(crbug.com/949519): replace WillRepeatedly with WillOnce when the
+      // old parser is gone.
       EXPECT_CALL(*store_, GetLogins(_, _))
-          .WillOnce(WithArg<1>(InvokeConsumer(form)));
+          .WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
     } else {
       EXPECT_CALL(*store_, GetLogins(_, _))
           .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
@@ -1357,8 +1370,10 @@
   __block BOOL completion_handler_called = NO;
 
   PasswordForm form(CreatePasswordForm(BaseUrl().c_str(), "user", "pw"));
+  // TODO(crbug.com/949519): replace WillRepeatedly with WillOnce when the old
+  // parser is gone.
   EXPECT_CALL(*store_, GetLogins(_, _))
-      .WillOnce(WithArg<1>(InvokeConsumer(form)));
+      .WillRepeatedly(WithArg<1>(InvokeConsumer(form)));
   std::string mainFrameID = web::GetMainWebFrameId(web_state());
   [passwordController_
       checkIfSuggestionsAvailableForForm:@"dynamic_form"
diff --git a/ios/chrome/browser/search_engines/search_engines_util.cc b/ios/chrome/browser/search_engines/search_engines_util.cc
index 5adec2f..54fccbb 100644
--- a/ios/chrome/browser/search_engines/search_engines_util.cc
+++ b/ios/chrome/browser/search_engines/search_engines_util.cc
@@ -29,26 +29,32 @@
   DCHECK(service);
   DCHECK(service->loaded());
   std::vector<TemplateURL*> old_engines = service->GetTemplateURLs();
-  size_t default_engine;
+  size_t default_engine_index;
   std::vector<std::unique_ptr<TemplateURLData>> new_engines =
       TemplateURLPrepopulateData::GetPrepopulatedEngines(nullptr,
-                                                         &default_engine);
-  DCHECK(default_engine == 0);
+                                                         &default_engine_index);
+  DCHECK(default_engine_index == 0);
   DCHECK(new_engines[0]->prepopulate_id == kGoogleEnginePrepopulatedId);
   // The aim is to replace the old search engines with the new ones.
   // It is not possible to remove all of them, because removing the current
   // selected engine is not allowed.
   // It is not possible to add all the new ones first, because the service gets
   // confused when a prepopulated engine is there more than once.
-  // Instead, this will in a first pass makes google as the default engine. In
-  // a second pass, it will remove all other search engines. At last, in a third
-  // pass, it will add all new engines but google.
-  for (auto* engine : old_engines) {
-    if (engine->prepopulate_id() == kGoogleEnginePrepopulatedId)
-      service->SetUserSelectedDefaultSearchProvider(engine);
+  // Instead, this will in a first pass make Google as the default engine if
+  // the currently selected engine is neither Google nor custom engine. Then it
+  // will remove all prepopulated search engines except Google. At last, in a
+  // third pass, it will add all new engines except Google.
+  const TemplateURL* default_engine = service->GetDefaultSearchProvider();
+  if (default_engine->prepopulate_id() > kGoogleEnginePrepopulatedId) {
+    for (auto* engine : old_engines) {
+      if (engine->prepopulate_id() == kGoogleEnginePrepopulatedId) {
+        service->SetUserSelectedDefaultSearchProvider(engine);
+        break;
+      }
+    }
   }
   for (auto* engine : old_engines) {
-    if (engine->prepopulate_id() != kGoogleEnginePrepopulatedId)
+    if (engine->prepopulate_id() > kGoogleEnginePrepopulatedId)
       service->Remove(engine);
   }
   for (const auto& engine : new_engines) {
diff --git a/ios/chrome/browser/ui/autofill/save_card_infobar_view.mm b/ios/chrome/browser/ui/autofill/save_card_infobar_view.mm
index c80d03fe..d464537 100644
--- a/ios/chrome/browser/ui/autofill/save_card_infobar_view.mm
+++ b/ios/chrome/browser/ui/autofill/save_card_infobar_view.mm
@@ -12,7 +12,6 @@
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
 #import "ios/chrome/browser/ui/util/label_link_controller.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
@@ -226,11 +225,7 @@
 
 - (void)setupSubviews {
   [self setAccessibilityViewIsModal:YES];
-  if (IsUIRefreshPhase1Enabled()) {
-    self.backgroundColor = UIColorFromRGB(kInfobarBackgroundColor);
-  } else {
-    self.backgroundColor = [UIColor whiteColor];
-  }
+  self.backgroundColor = UIColorFromRGB(kInfobarBackgroundColor);
   id<LayoutGuideProvider> safeAreaLayoutGuide = self.safeAreaLayoutGuide;
 
   // Add the icon. The icon is fixed to the top leading corner of the infobar.
diff --git a/ios/chrome/browser/ui/elements/BUILD.gn b/ios/chrome/browser/ui/elements/BUILD.gn
index 2af5b38..0213e73 100644
--- a/ios/chrome/browser/ui/elements/BUILD.gn
+++ b/ios/chrome/browser/ui/elements/BUILD.gn
@@ -5,6 +5,8 @@
 source_set("elements") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
+    "extended_touch_target_button.h",
+    "extended_touch_target_button.mm",
     "selector_coordinator.h",
     "selector_coordinator.mm",
     "selector_picker_presentation_controller.h",
diff --git a/ios/chrome/browser/ui/location_bar/extended_touch_target_button.h b/ios/chrome/browser/ui/elements/extended_touch_target_button.h
similarity index 63%
rename from ios/chrome/browser/ui/location_bar/extended_touch_target_button.h
rename to ios/chrome/browser/ui/elements/extended_touch_target_button.h
index bd5de47..04fad4f 100644
--- a/ios/chrome/browser/ui/location_bar/extended_touch_target_button.h
+++ b/ios/chrome/browser/ui/elements/extended_touch_target_button.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 IOS_CHROME_BROWSER_UI_LOCATION_BAR_EXTENDED_TOUCH_TARGET_BUTTON_H_
-#define IOS_CHROME_BROWSER_UI_LOCATION_BAR_EXTENDED_TOUCH_TARGET_BUTTON_H_
+#ifndef IOS_CHROME_BROWSER_UI_ELEMENTS_EXTENDED_TOUCH_TARGET_BUTTON_H_
+#define IOS_CHROME_BROWSER_UI_ELEMENTS_EXTENDED_TOUCH_TARGET_BUTTON_H_
 
 #import <UIKit/UIKit.h>
 
@@ -12,4 +12,4 @@
 @interface ExtendedTouchTargetButton : UIButton
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_LOCATION_BAR_EXTENDED_TOUCH_TARGET_BUTTON_H_
+#endif  // IOS_CHROME_BROWSER_UI_ELEMENTS_EXTENDED_TOUCH_TARGET_BUTTON_H_
diff --git a/ios/chrome/browser/ui/location_bar/extended_touch_target_button.mm b/ios/chrome/browser/ui/elements/extended_touch_target_button.mm
similarity index 92%
rename from ios/chrome/browser/ui/location_bar/extended_touch_target_button.mm
rename to ios/chrome/browser/ui/elements/extended_touch_target_button.mm
index 06707976..a8504ac 100644
--- a/ios/chrome/browser/ui/location_bar/extended_touch_target_button.mm
+++ b/ios/chrome/browser/ui/elements/extended_touch_target_button.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/location_bar/extended_touch_target_button.h"
+#import "ios/chrome/browser/ui/elements/extended_touch_target_button.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm b/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
index d8c6d167..e08aa41 100644
--- a/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
+++ b/ios/chrome/browser/ui/infobars/confirm_infobar_view.mm
@@ -17,7 +17,6 @@
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
 #import "ios/chrome/browser/ui/util/label_link_controller.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
 #import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
@@ -771,11 +770,7 @@
 }
 
 - (void)resetBackground {
-  if (IsUIRefreshPhase1Enabled()) {
-    self.backgroundColor = UIColorFromRGB(kInfobarBackgroundColor);
-  } else {
-    self.backgroundColor = [UIColor whiteColor];
-  }
+  self.backgroundColor = UIColorFromRGB(kInfobarBackgroundColor);
   CGFloat shadowY = 0;
   shadowY = -[shadow_ image].size.height;  // Shadow above the infobar.
   [shadow_ setFrame:CGRectMake(0, shadowY, self.bounds.size.width,
diff --git a/ios/chrome/browser/ui/location_bar/BUILD.gn b/ios/chrome/browser/ui/location_bar/BUILD.gn
index 7671f33e..40702c59 100644
--- a/ios/chrome/browser/ui/location_bar/BUILD.gn
+++ b/ios/chrome/browser/ui/location_bar/BUILD.gn
@@ -5,8 +5,6 @@
 source_set("location_bar") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
-    "extended_touch_target_button.h",
-    "extended_touch_target_button.mm",
     "location_bar_consumer.h",
     "location_bar_coordinator.h",
     "location_bar_coordinator.mm",
@@ -47,6 +45,7 @@
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/fullscreen",
     "//ios/chrome/browser/ui/fullscreen:ui",
     "//ios/chrome/browser/ui/infobars:feature_flags",
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
index 5cca7773..103dc1c 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_steady_view.mm
@@ -5,8 +5,8 @@
 #import "ios/chrome/browser/ui/location_bar/location_bar_steady_view.h"
 
 #include "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/ui/elements/extended_touch_target_button.h"
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
-#import "ios/chrome/browser/ui/location_bar/extended_touch_target_button.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #import "ios/chrome/browser/ui/util/dynamic_type_util.h"
diff --git a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
index a69ac85..71209a57 100644
--- a/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
+++ b/ios/chrome/browser/ui/omnibox/popup/BUILD.gn
@@ -68,6 +68,7 @@
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/app/theme",
     "//ios/chrome/browser/ui/commands",
+    "//ios/chrome/browser/ui/elements",
     "//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
     "//ios/chrome/browser/ui/toolbar/buttons",
     "//ios/chrome/browser/ui/toolbar/public",
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h
index 0fa9792..c12c282bb 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.h
@@ -8,6 +8,7 @@
 #import <UIKit/UIKit.h>
 
 @protocol AutocompleteSuggestion;
+@protocol ImageRetriever;
 @class OmniboxPopupRowCell;
 
 namespace {
@@ -33,6 +34,8 @@
 
 @property(nonatomic, weak) id<OmniboxPopupRowCellDelegate> delegate;
 
+@property(nonatomic, weak) id<ImageRetriever> imageRetriever;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_OMNIBOX_POPUP_OMNIBOX_POPUP_ROW_CELL_H_
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm
index 2cb3127..4db7a8f 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_row_cell.mm
@@ -7,13 +7,16 @@
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "components/omnibox/common/omnibox_features.h"
+#import "ios/chrome/browser/ui/elements/extended_touch_target_button.h"
 #import "ios/chrome/browser/ui/omnibox/popup/autocomplete_suggestion.h"
+#import "ios/chrome/browser/ui/omnibox/popup/image_retriever.h"
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_truncating_label.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/chrome/grit/ios_theme_resources.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -24,10 +27,9 @@
 const CGFloat kLeadingMarginIpad = 183;
 const CGFloat kTextTopMargin = 6;
 const CGFloat kTextLeadingMargin = 10;
-const CGFloat kTextTrailingMargin = -12;
 const CGFloat kImageViewSize = 28;
 const CGFloat kImageViewCornerRadius = 7;
-const CGFloat kTrailingButtonSize = 48;
+const CGFloat kTrailingButtonSize = 24;
 const CGFloat kTrailingButtonTrailingMargin = 4;
 
 NSString* const kOmniboxPopupRowSwitchTabAccessibilityIdentifier =
@@ -53,8 +55,11 @@
 @property(nonatomic, strong) UILabel* detailAnswerLabel;
 // Image view for the leading image (only appears on iPad).
 @property(nonatomic, strong) UIImageView* leadingImageView;
-// Trailing button for appending suggestion into omnibox.
-@property(nonatomic, strong) UIButton* trailingButton;
+// Trailing button for appending suggestion into omnibox or switching to open
+// tab.
+@property(nonatomic, strong) ExtendedTouchTargetButton* trailingButton;
+// Trailing image view for images from suggestions (e.g. weather).
+@property(nonatomic, strong) UIImageView* answerImageView;
 
 @end
 
@@ -88,15 +93,20 @@
     _leadingImageView.contentMode = UIViewContentModeCenter;
     _leadingImageView.layer.cornerRadius = kImageViewCornerRadius;
 
-    _trailingButton = [UIButton buttonWithType:UIButtonTypeCustom];
+    _trailingButton =
+        [ExtendedTouchTargetButton buttonWithType:UIButtonTypeCustom];
     _trailingButton.translatesAutoresizingMaskIntoConstraints = NO;
     [_trailingButton addTarget:self
                         action:@selector(trailingButtonTapped)
               forControlEvents:UIControlEventTouchUpInside];
-    [_trailingButton setContentMode:UIViewContentModeRight];
-    // The trailing button also shows answer images. In that case, the
-    // button will be disabled, but the image shouldn't be dimmed.
-    _trailingButton.adjustsImageWhenDisabled = NO;
+
+    _answerImageView = [[UIImageView alloc] initWithImage:nil];
+    _answerImageView.translatesAutoresizingMaskIntoConstraints = NO;
+    [_answerImageView
+        setContentHuggingPriority:UILayoutPriorityDefaultHigh
+                          forAxis:UILayoutConstraintAxisHorizontal];
+    [_answerImageView setContentHuggingPriority:UILayoutPriorityDefaultHigh
+                                        forAxis:UILayoutConstraintAxisVertical];
 
     _incognito = NO;
 
@@ -135,7 +145,7 @@
         constraintEqualToAnchor:self.leadingImageView.trailingAnchor
                        constant:kTextLeadingMargin],
     // Use greater than or equal constraints because there may be a trailing
-    // button here.
+    // view here.
     [self.contentView.trailingAnchor
         constraintGreaterThanOrEqualToAnchor:self.textStackView.trailingAnchor],
     // Top space should be at least the given top margin, but can be more if
@@ -146,24 +156,42 @@
     [self.textStackView.centerYAnchor
         constraintEqualToAnchor:self.contentView.centerYAnchor],
   ]];
+
+  // If optional views have internal constraints (height is constant, etc.),
+  // set those up here.
+  [NSLayoutConstraint activateConstraints:@[
+    [self.trailingButton.heightAnchor
+        constraintEqualToConstant:kTrailingButtonSize],
+    [self.trailingButton.widthAnchor
+        constraintEqualToAnchor:self.trailingButton.heightAnchor],
+  ]];
 }
 
 // Add the trailing button as a subview and setup its constraints.
 - (void)setupTrailingButtonLayout {
   [self.contentView addSubview:self.trailingButton];
   [NSLayoutConstraint activateConstraints:@[
-    [self.trailingButton.heightAnchor
-        constraintEqualToConstant:kTrailingButtonSize],
-    [self.trailingButton.widthAnchor
-        constraintEqualToConstant:kTrailingButtonSize],
     [self.trailingButton.centerYAnchor
         constraintEqualToAnchor:self.contentView.centerYAnchor],
     [self.contentView.trailingAnchor
         constraintEqualToAnchor:self.trailingButton.trailingAnchor
                        constant:kTrailingButtonTrailingMargin],
     [self.trailingButton.leadingAnchor
-        constraintEqualToAnchor:self.textStackView.trailingAnchor
-                       constant:kTextTrailingMargin],
+        constraintEqualToAnchor:self.textStackView.trailingAnchor],
+  ]];
+}
+
+// Add the answer image view as a subview and setup its constraints.
+- (void)setupAnswerImageViewLayout {
+  [self.contentView addSubview:self.answerImageView];
+  [NSLayoutConstraint activateConstraints:@[
+    [self.answerImageView.centerYAnchor
+        constraintEqualToAnchor:self.contentView.centerYAnchor],
+    [self.contentView.trailingAnchor
+        constraintEqualToAnchor:self.answerImageView.trailingAnchor
+                       constant:kTrailingButtonTrailingMargin],
+    [self.answerImageView.leadingAnchor
+        constraintEqualToAnchor:self.textStackView.trailingAnchor],
   ]];
 }
 
@@ -181,6 +209,8 @@
   // Remove optional views.
   [self.trailingButton setImage:nil forState:UIControlStateNormal];
   [self.trailingButton removeFromSuperview];
+  self.answerImageView.image = nil;
+  [self.answerImageView removeFromSuperview];
   [self.detailTruncatingLabel removeFromSuperview];
   [self.detailAnswerLabel removeFromSuperview];
 
@@ -216,11 +246,17 @@
     }
   }
 
-  if (self.suggestion.hasImage || self.suggestion.isAppendable ||
-      self.suggestion.isTabMatch) {
+  [self setupLeadingImageView];
+
+  if (self.suggestion.hasImage) {
+    [self setupAnswerImageView];
+  } else if (self.suggestion.isAppendable || self.suggestion.isTabMatch) {
     [self setupTrailingButton];
   }
+}
 
+// Populate the leading image view with the correct icon and color.
+- (void)setupLeadingImageView {
   self.leadingImageView.image = self.suggestion.suggestionTypeIcon;
   self.leadingImageView.backgroundColor =
       self.incognito ? [UIColor colorWithWhite:1 alpha:0.05]
@@ -230,16 +266,28 @@
                                         : [UIColor colorWithWhite:0 alpha:0.33];
 }
 
+// Setup the answer image view. This includes both setting up its layout and
+// populating the image correctly.
+- (void)setupAnswerImageView {
+  [self setupAnswerImageViewLayout];
+  __weak OmniboxPopupRowCell* weakSelf = self;
+  GURL imageURL = self.suggestion.imageURL;
+  [self.imageRetriever fetchImage:imageURL
+                       completion:^(UIImage* image) {
+                         // Make sure cell is still displaying the same
+                         // suggestion.
+                         if (weakSelf.suggestion.imageURL != imageURL) {
+                           return;
+                         }
+                         weakSelf.answerImageView.image = image;
+                       }];
+}
+
+// Setup the trailing button. This includes both setting up the button's layout
+// and popuplating it with the correct image and color.
 - (void)setupTrailingButton {
   [self setupTrailingButtonLayout];
 
-  // If there's an image, put it in the button, but disable interaction.
-  if (self.suggestion.hasImage) {
-    self.trailingButton.enabled = NO;
-    [self.trailingButton setImage:nil forState:UIControlStateNormal];
-    return;
-  }
-
   // Show append button for search history/search suggestions or
   // switch-to-open-tab as the right control element (aka an accessory element
   // of a table view cell).
@@ -276,7 +324,6 @@
   trailingButtonImage = [trailingButtonImage
       imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
 
-  self.trailingButton.enabled = YES;
   [self.trailingButton setImage:trailingButtonImage
                        forState:UIControlStateNormal];
   if (base::FeatureList::IsEnabled(omnibox::kOmniboxTabSwitchSuggestions)) {
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
index 7208d8e..a984dd68 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -65,6 +65,7 @@
   [cell setupWithAutocompleteSuggestion:self.currentResult[indexPath.row]
                               incognito:self.incognito];
   cell.delegate = self;
+  cell.imageRetriever = self.imageRetriever;
 
   return cell;
 }
diff --git a/ios/chrome/browser/ui/settings/BUILD.gn b/ios/chrome/browser/ui/settings/BUILD.gn
index c088c07..c50247b 100644
--- a/ios/chrome/browser/ui/settings/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/BUILD.gn
@@ -126,6 +126,7 @@
     "//ios/chrome/browser/browser_state:browser_state_impl",
     "//ios/chrome/browser/browsing_data",
     "//ios/chrome/browser/content_settings",
+    "//ios/chrome/browser/favicon",
     "//ios/chrome/browser/feature_engagement",
     "//ios/chrome/browser/history",
     "//ios/chrome/browser/mailto:feature_flags",
@@ -170,6 +171,7 @@
     "//ios/chrome/browser/voice",
     "//ios/chrome/browser/web:web",
     "//ios/chrome/common",
+    "//ios/chrome/common/favicon",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/images",
     "//ios/public/provider/chrome/browser/mailto",
diff --git a/ios/chrome/browser/ui/settings/cells/BUILD.gn b/ios/chrome/browser/ui/settings/cells/BUILD.gn
index b9ebeb3..88f3b8e9 100644
--- a/ios/chrome/browser/ui/settings/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/cells/BUILD.gn
@@ -18,6 +18,8 @@
     "copied_to_chrome_item.mm",
     "passphrase_error_item.h",
     "passphrase_error_item.mm",
+    "search_engine_item.h",
+    "search_engine_item.mm",
     "settings_image_detail_text_cell.h",
     "settings_image_detail_text_cell.mm",
     "settings_image_detail_text_item.h",
@@ -45,6 +47,7 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/browsing_data",
     "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/collection_view/cells",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/icons",
@@ -79,6 +82,7 @@
     "clear_browsing_data_item_unittest.mm",
     "copied_to_chrome_item_unittest.mm",
     "passphrase_error_item_unittest.mm",
+    "search_engine_item_unittest.mm",
     "settings_multiline_detail_item_unittest.mm",
     "version_item_unittest.mm",
   ]
@@ -95,9 +99,11 @@
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/browsing_data",
     "//ios/chrome/browser/browsing_data:counters",
+    "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/collection_view/cells",
     "//ios/chrome/browser/ui/collection_view/cells:test_support",
     "//ios/chrome/browser/ui/table_view:styler",
+    "//ios/chrome/browser/ui/table_view/cells",
     "//ios/web/public/test:test",
     "//testing/gtest",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/settings/cells/search_engine_item.h b/ios/chrome/browser/ui/settings/cells/search_engine_item.h
new file mode 100644
index 0000000..d5835b5
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/cells/search_engine_item.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 IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_SEARCH_ENGINE_ITEM_H_
+#define IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_SEARCH_ENGINE_ITEM_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/table_view/cells/table_view_item.h"
+
+class GURL;
+
+// SearchEngineItem contains the model data for a TableViewURLCell.
+@interface SearchEngineItem : TableViewItem
+
+// The text for the title.
+@property(nonatomic, readwrite, copy) NSString* text;
+// The text for the subtitle.
+@property(nonatomic, readwrite, copy) NSString* detailText;
+// The URL to fetch the favicon. This can be the favicon's URL, or a "fake" web
+// page URL created by filling empty query word into the search engine's
+// searchable URL template(e.g. "http://www.google.com/?q=").
+@property(nonatomic, assign) GURL URL;
+// Identifier to match a URLItem with its URLCell.
+@property(nonatomic, readonly, copy) NSString* uniqueIdentifier;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_SETTINGS_CELLS_SEARCH_ENGINE_ITEM_H_
diff --git a/ios/chrome/browser/ui/settings/cells/search_engine_item.mm b/ios/chrome/browser/ui/settings/cells/search_engine_item.mm
new file mode 100644
index 0000000..dabaa8b
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/cells/search_engine_item.mm
@@ -0,0 +1,86 @@
+// 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 "ios/chrome/browser/ui/settings/cells/search_engine_item.h"
+
+#include "base/mac/foundation_util.h"
+#include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_cells_constants.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
+#include "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+#pragma mark - SearchEngineItem
+
+@interface SearchEngineItem ()
+
+// Redefined as read write.
+@property(nonatomic, readwrite, copy) NSString* uniqueIdentifier;
+
+@end
+
+@implementation SearchEngineItem
+
+- (instancetype)initWithType:(NSInteger)type {
+  self = [super initWithType:type];
+  if (self) {
+    // TODO(crbug.com/910525): Remove usage of TableViewDetailTextCell after the
+    // feature is launched.
+    if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
+      self.cellClass = TableViewURLCell.class;
+    } else {
+      self.cellClass = TableViewDetailTextCell.class;
+    }
+  }
+  return self;
+}
+
+- (void)configureCell:(TableViewCell*)tableCell
+           withStyler:(ChromeTableViewStyler*)styler {
+  [super configureCell:tableCell withStyler:styler];
+
+  self.uniqueIdentifier = base::SysUTF8ToNSString(self.URL.host());
+
+  // TODO(crbug.com/910525): Remove usage of TableViewDetailTextCell after the
+  // feature is launched.
+  if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
+    TableViewURLCell* cell =
+        base::mac::ObjCCastStrict<TableViewURLCell>(tableCell);
+
+    cell.titleLabel.text = self.text;
+    cell.URLLabel.text = self.detailText;
+    cell.cellUniqueIdentifier = self.uniqueIdentifier;
+    cell.accessibilityTraits |= UIAccessibilityTraitButton;
+
+    if (styler.cellTitleColor)
+      cell.titleLabel.textColor = styler.cellTitleColor;
+
+    [cell configureUILayout];
+  } else {
+    TableViewDetailTextCell* cell =
+        base::mac::ObjCCastStrict<TableViewDetailTextCell>(tableCell);
+
+    cell.textLabel.text = self.text;
+    cell.detailTextLabel.text = self.detailText;
+    cell.accessibilityTraits |= UIAccessibilityTraitButton;
+
+    if (styler.cellTitleColor) {
+      cell.textLabel.textColor = styler.cellTitleColor;
+    } else {
+      cell.textLabel.textColor =
+          UIColorFromRGB(kTableViewTextLabelColorLightGrey);
+    }
+    cell.detailTextLabel.textColor =
+        UIColorFromRGB(kTableViewSecondaryLabelLightGrayTextColor);
+  }
+}
+
+@end
diff --git a/ios/chrome/browser/ui/settings/cells/search_engine_item_unittest.mm b/ios/chrome/browser/ui/settings/cells/search_engine_item_unittest.mm
new file mode 100644
index 0000000..de2aac1
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/cells/search_engine_item_unittest.mm
@@ -0,0 +1,72 @@
+// 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 "ios/chrome/browser/ui/settings/cells/search_engine_item.h"
+
+#include "base/mac/foundation_util.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
+#import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
+#include "net/base/mac/url_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+using SearchEngineItemTest = PlatformTest;
+}  // namespace
+
+// Tests that the UILabels are set properly after a call to |configureCell:|.
+TEST_F(SearchEngineItemTest, BasicProperties) {
+  NSString* text = @"Title text";
+  NSString* detailText = @"www.google.com";
+  GURL URL = net::GURLWithNSURL([NSURL URLWithString:detailText]);
+
+  SearchEngineItem* item = [[SearchEngineItem alloc] initWithType:0];
+  item.text = text;
+  item.detailText = detailText;
+  item.URL = URL;
+  item.accessoryType = UITableViewCellAccessoryCheckmark;
+
+  id cell = [[[item cellClass] alloc] init];
+  // TODO(crbug.com/910525): Remove usage of TableViewDetailTextCell after the
+  // feature is launched.
+  if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
+    ASSERT_TRUE([cell isMemberOfClass:[TableViewURLCell class]]);
+
+    TableViewURLCell* URLCell =
+        base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+    EXPECT_FALSE(URLCell.titleLabel.text);
+    EXPECT_FALSE(URLCell.URLLabel.text);
+    EXPECT_EQ(item.uniqueIdentifier, URLCell.cellUniqueIdentifier);
+    EXPECT_EQ(UITableViewCellAccessoryNone, URLCell.accessoryType);
+
+    ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
+    [item configureCell:URLCell withStyler:styler];
+    EXPECT_NSEQ(text, URLCell.titleLabel.text);
+    EXPECT_NSEQ(detailText, URLCell.URLLabel.text);
+    EXPECT_EQ(item.uniqueIdentifier, URLCell.cellUniqueIdentifier);
+    EXPECT_EQ(UITableViewCellAccessoryCheckmark, URLCell.accessoryType);
+  } else {
+    ASSERT_TRUE([cell isMemberOfClass:[TableViewDetailTextCell class]]);
+
+    TableViewDetailTextCell* textCell =
+        base::mac::ObjCCastStrict<TableViewDetailTextCell>(cell);
+    EXPECT_FALSE(textCell.textLabel.text);
+    EXPECT_FALSE(textCell.detailTextLabel.text);
+    EXPECT_EQ(UITableViewCellAccessoryNone, textCell.accessoryType);
+
+    ChromeTableViewStyler* styler = [[ChromeTableViewStyler alloc] init];
+    [item configureCell:textCell withStyler:styler];
+    EXPECT_NSEQ(text, textCell.textLabel.text);
+    EXPECT_NSEQ(detailText, textCell.detailTextLabel.text);
+    EXPECT_EQ(UITableViewCellAccessoryCheckmark, textCell.accessoryType);
+  }
+}
diff --git a/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm b/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
index 0c3de9da..9876650 100644
--- a/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/search_engine_table_view_controller.mm
@@ -12,11 +12,15 @@
 #include "components/search_engines/template_url_service.h"
 #include "components/search_engines/template_url_service_observer.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/favicon/favicon_loader.h"
+#include "ios/chrome/browser/favicon/ios_chrome_favicon_loader_factory.h"
 #import "ios/chrome/browser/search_engines/search_engine_observer_bridge.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
+#import "ios/chrome/browser/ui/settings/cells/search_engine_item.h"
 #import "ios/chrome/browser/ui/table_view/cells/table_view_text_header_footer_item.h"
+#import "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "ios/chrome/common/favicon/favicon_view.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -25,18 +29,19 @@
 #endif
 
 namespace {
-
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
-  SectionIdentifierPriorSearchEngines = kSectionIdentifierEnumZero,
-  SectionIdentifierCustomSearchEngines,
+  SectionIdentifierFirstList = kSectionIdentifierEnumZero,
+  SectionIdentifierSecondList,
 };
 
 typedef NS_ENUM(NSInteger, ItemType) {
-  ItemTypePriorSearchEnginesEngine = kItemTypeEnumZero,
-  ItemTypeCustomSearchEnginesEngineHeader,
-  ItemTypeCustomSearchEnginesEngine,
+  ItemTypePrepopulatedEngine = kItemTypeEnumZero,
+  ItemTypeHeader,
+  ItemTypeCustomEngine,
 };
 
+const CGFloat kTableViewSeparatorLeadingInset = 56;
+const CGFloat kTableViewSeparatorTrailingInset = 16;
 constexpr base::TimeDelta kMaxVisitAge = base::TimeDelta::FromDays(2);
 const size_t kMaxcustomSearchEngines = 3;
 const char kUmaSelectDefaultSearchEngine[] =
@@ -55,10 +60,13 @@
   // The first list in the page which contains prepopulted search engines and
   // search engines that are created by policy, and possibly one custom search
   // engine if it's selected as default search engine.
-  std::vector<TemplateURL*> _priorSearchEngines;
+  std::vector<TemplateURL*> _firstList;
   // The second list in the page which contains all remaining custom search
   // engines.
-  std::vector<TemplateURL*> _customSearchEngines;
+  std::vector<TemplateURL*> _secondList;
+  // FaviconLoader is a keyed service that uses LargeIconService to retrieve
+  // favicon images.
+  FaviconLoader* _faviconLoader;
 }
 
 #pragma mark Initialization
@@ -76,6 +84,8 @@
     _observer =
         std::make_unique<SearchEngineObserverBridge>(self, _templateURLService);
     _templateURLService->Load();
+    _faviconLoader =
+        IOSChromeFaviconLoaderFactory::GetForBrowserState(browserState);
     [self setTitle:l10n_util::GetNSString(IDS_IOS_SEARCH_ENGINE_SETTING_TITLE)];
   }
   return self;
@@ -86,6 +96,12 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
+  if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
+    self.tableView.separatorInset =
+        UIEdgeInsetsMake(0, kTableViewSeparatorLeadingInset, 0,
+                         kTableViewSeparatorTrailingInset);
+  }
+
   [self loadModel];
 }
 
@@ -97,44 +113,30 @@
   [self loadSearchEngines];
 
   // Add prior search engines.
-  if (_priorSearchEngines.size() > 0) {
-    [model addSectionWithIdentifier:SectionIdentifierPriorSearchEngines];
+  if (_firstList.size() > 0) {
+    [model addSectionWithIdentifier:SectionIdentifierFirstList];
 
-    for (TemplateURL* url : _priorSearchEngines) {
-      TableViewDetailTextItem* engine = [[TableViewDetailTextItem alloc]
-          initWithType:ItemTypePriorSearchEnginesEngine];
-      engine.text = base::SysUTF16ToNSString(url->short_name());
-      engine.detailText = base::SysUTF16ToNSString(url->keyword());
-      if (url == _templateURLService->GetDefaultSearchProvider()) {
-        [engine setAccessoryType:UITableViewCellAccessoryCheckmark];
-      }
-      [model addItem:engine
-          toSectionWithIdentifier:SectionIdentifierPriorSearchEngines];
+    for (const TemplateURL* templateURL : _firstList) {
+      [model addItem:[self createSearchEngineItemFromTemplateURL:templateURL]
+          toSectionWithIdentifier:SectionIdentifierFirstList];
     }
   }
 
   // Add custom search engines.
-  if (_customSearchEngines.size() > 0) {
-    [model addSectionWithIdentifier:SectionIdentifierCustomSearchEngines];
+  if (_secondList.size() > 0) {
+    [model addSectionWithIdentifier:SectionIdentifierSecondList];
 
     TableViewTextHeaderFooterItem* header =
-        [[TableViewTextHeaderFooterItem alloc]
-            initWithType:ItemTypeCustomSearchEnginesEngineHeader];
+        [[TableViewTextHeaderFooterItem alloc] initWithType:ItemTypeHeader];
     header.text = l10n_util::GetNSString(
         IDS_IOS_SEARCH_ENGINE_SETTING_CUSTOM_SECTION_HEADER);
     [model setHeader:header
-        forSectionWithIdentifier:SectionIdentifierCustomSearchEngines];
+        forSectionWithIdentifier:SectionIdentifierSecondList];
 
-    for (TemplateURL* url : _customSearchEngines) {
-      TableViewDetailTextItem* engine = [[TableViewDetailTextItem alloc]
-          initWithType:ItemTypeCustomSearchEnginesEngine];
-      engine.text = base::SysUTF16ToNSString(url->short_name());
-      engine.detailText = base::SysUTF16ToNSString(url->keyword());
-      if (url == _templateURLService->GetDefaultSearchProvider()) {
-        [engine setAccessoryType:UITableViewCellAccessoryCheckmark];
-      }
-      [model addItem:engine
-          toSectionWithIdentifier:SectionIdentifierCustomSearchEngines];
+    for (const TemplateURL* templateURL : _secondList) {
+      DCHECK(templateURL->prepopulate_id() == 0);
+      [model addItem:[self createSearchEngineItemFromTemplateURL:templateURL]
+          toSectionWithIdentifier:SectionIdentifierSecondList];
     }
   }
 }
@@ -146,100 +148,147 @@
   [super tableView:tableView didSelectRowAtIndexPath:indexPath];
   [tableView deselectRowAtIndexPath:indexPath animated:YES];
   TableViewModel* model = self.tableViewModel;
-
-  // Only handle taps on search engine items.
   TableViewItem* selectedItem = [model itemAtIndexPath:indexPath];
-  if (selectedItem.type != ItemTypePriorSearchEnginesEngine &&
-      selectedItem.type != ItemTypeCustomSearchEnginesEngine) {
-    return;
-  }
+
+  // Only search engine items can be selected.
+  DCHECK(selectedItem.type == ItemTypePrepopulatedEngine ||
+         selectedItem.type == ItemTypeCustomEngine);
 
   // Do nothing if the tapped engine was already the default.
-  TableViewDetailTextItem* selectedTextItem =
-      base::mac::ObjCCastStrict<TableViewDetailTextItem>(selectedItem);
+  SearchEngineItem* selectedTextItem =
+      base::mac::ObjCCastStrict<SearchEngineItem>(selectedItem);
   if (selectedTextItem.accessoryType == UITableViewCellAccessoryCheckmark) {
+    [tableView deselectRowAtIndexPath:indexPath animated:YES];
     return;
   }
 
-  NSMutableArray* modifiedItems = [NSMutableArray array];
-
   // Iterate through the engines and remove the checkmark from any that have it.
-  if ([model
-          hasSectionForSectionIdentifier:SectionIdentifierPriorSearchEngines]) {
+  if ([model hasSectionForSectionIdentifier:SectionIdentifierFirstList]) {
     for (TableViewItem* item in
-         [model itemsInSectionWithIdentifier:
-                    SectionIdentifierPriorSearchEngines]) {
-      if (item.type != ItemTypePriorSearchEnginesEngine) {
-        continue;
-      }
-      TableViewDetailTextItem* textItem =
-          base::mac::ObjCCastStrict<TableViewDetailTextItem>(item);
+         [model itemsInSectionWithIdentifier:SectionIdentifierFirstList]) {
+      SearchEngineItem* textItem =
+          base::mac::ObjCCastStrict<SearchEngineItem>(item);
       if (textItem.accessoryType == UITableViewCellAccessoryCheckmark) {
         textItem.accessoryType = UITableViewCellAccessoryNone;
-        [modifiedItems addObject:textItem];
+        UITableViewCell* cell =
+            [tableView cellForRowAtIndexPath:[model indexPathForItem:item]];
+        cell.accessoryType = UITableViewCellAccessoryNone;
       }
     }
   }
-  if ([model hasSectionForSectionIdentifier:
-                 SectionIdentifierCustomSearchEngines]) {
+  if ([model hasSectionForSectionIdentifier:SectionIdentifierSecondList]) {
     for (TableViewItem* item in
-         [model itemsInSectionWithIdentifier:
-                    SectionIdentifierCustomSearchEngines]) {
-      if (item.type != ItemTypeCustomSearchEnginesEngine) {
-        continue;
-      }
-      TableViewDetailTextItem* textItem =
-          base::mac::ObjCCastStrict<TableViewDetailTextItem>(item);
+         [model itemsInSectionWithIdentifier:SectionIdentifierSecondList]) {
+      DCHECK(item.type == ItemTypeCustomEngine);
+      SearchEngineItem* textItem =
+          base::mac::ObjCCastStrict<SearchEngineItem>(item);
       if (textItem.accessoryType == UITableViewCellAccessoryCheckmark) {
         textItem.accessoryType = UITableViewCellAccessoryNone;
-        [modifiedItems addObject:textItem];
+        UITableViewCell* cell =
+            [tableView cellForRowAtIndexPath:[model indexPathForItem:item]];
+        cell.accessoryType = UITableViewCellAccessoryNone;
       }
     }
   }
 
   // Show the checkmark on the new default engine.
-  TableViewDetailTextItem* newDefaultEngine =
-      base::mac::ObjCCastStrict<TableViewDetailTextItem>(
+
+  SearchEngineItem* newDefaultEngine =
+      base::mac::ObjCCastStrict<SearchEngineItem>(
           [model itemAtIndexPath:indexPath]);
   newDefaultEngine.accessoryType = UITableViewCellAccessoryCheckmark;
-  [modifiedItems addObject:newDefaultEngine];
+  UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
+  cell.accessoryType = UITableViewCellAccessoryCheckmark;
 
   // Set the new engine as the default.
-  if (selectedItem.type == ItemTypePriorSearchEnginesEngine)
-    [self setDefaultToPriorSearchEngineAtIndex:
-              [model indexInItemTypeForIndexPath:indexPath]];
-  else
-    [self setDefaultToCustomSearchEngineAtIndex:
-              [model indexInItemTypeForIndexPath:indexPath]];
-
-  [self reconfigureCellsForItems:modifiedItems];
+  _updatingBackend = YES;
+  if (indexPath.section ==
+      [model sectionForSectionIdentifier:SectionIdentifierFirstList]) {
+    _templateURLService->SetUserSelectedDefaultSearchProvider(
+        _firstList[indexPath.row]);
+  } else {
+    _templateURLService->SetUserSelectedDefaultSearchProvider(
+        _secondList[indexPath.row]);
+  }
+  [self recordUmaOfDefaultSearchEngine];
+  _updatingBackend = NO;
 }
 
-#pragma mark Internal methods
+#pragma mark - UITableViewDataSource
+
+- (UITableViewCell*)tableView:(UITableView*)tableView
+        cellForRowAtIndexPath:(NSIndexPath*)indexPath {
+  UITableViewCell* cell = [super tableView:tableView
+                     cellForRowAtIndexPath:indexPath];
+  TableViewItem* item = [self.tableViewModel itemAtIndexPath:indexPath];
+  DCHECK(item.type == ItemTypePrepopulatedEngine ||
+         item.type == ItemTypeCustomEngine);
+  if (base::FeatureList::IsEnabled(kDisplaySearchEngineFavicon)) {
+    SearchEngineItem* engineItem =
+        base::mac::ObjCCastStrict<SearchEngineItem>(item);
+    TableViewURLCell* urlCell =
+        base::mac::ObjCCastStrict<TableViewURLCell>(cell);
+
+    FaviconAttributes* cachedAttributes = nil;
+    if (item.type == ItemTypePrepopulatedEngine) {
+      cachedAttributes = _faviconLoader->FaviconForPageUrl(
+          engineItem.URL, kFaviconPreferredSize, kFaviconMinSize,
+          /*fallback_to_google_server=*/YES, ^(FaviconAttributes* attributes) {
+            // Only set favicon if the cell hasn't been reused.
+            if (urlCell.cellUniqueIdentifier == engineItem.uniqueIdentifier) {
+              DCHECK(attributes);
+              [urlCell.faviconView configureWithAttributes:attributes];
+            }
+          });
+    } else {
+      cachedAttributes = _faviconLoader->FaviconForIconUrl(
+          engineItem.URL, kFaviconPreferredSize, kFaviconMinSize,
+          ^(FaviconAttributes* attributes) {
+            // Only set favicon if the cell hasn't been reused.
+            if (urlCell.cellUniqueIdentifier == engineItem.uniqueIdentifier) {
+              DCHECK(attributes);
+              [urlCell.faviconView configureWithAttributes:attributes];
+            }
+          });
+    }
+    DCHECK(cachedAttributes);
+    [urlCell.faviconView configureWithAttributes:cachedAttributes];
+  }
+  return cell;
+}
+
+#pragma mark - SearchEngineObserving
+
+- (void)searchEngineChanged {
+  if (!_updatingBackend)
+    [self reloadData];
+}
+
+#pragma mark - Private methods
 
 // Loads all TemplateURLs from TemplateURLService and classifies them into
-// |_priorSearchEngines| and |_customSearchEngines|. If a TemplateURL is
+// |_firstList| and |_secondList|. If a TemplateURL is
 // prepopulated, created by policy or the default search engine, it will get
 // into the first list, otherwise the second list.
 - (void)loadSearchEngines {
   std::vector<TemplateURL*> urls = _templateURLService->GetTemplateURLs();
-  _priorSearchEngines.clear();
-  _priorSearchEngines.reserve(urls.size());
-  _customSearchEngines.clear();
-  _customSearchEngines.reserve(urls.size());
+  _firstList.clear();
+  _firstList.reserve(urls.size());
+  _secondList.clear();
+  _secondList.reserve(urls.size());
 
   // Classify TemplateURLs.
   for (TemplateURL* url : urls) {
     if (_templateURLService->IsPrepopulatedOrCreatedByPolicy(url) ||
         url == _templateURLService->GetDefaultSearchProvider())
-      _priorSearchEngines.push_back(url);
+      _firstList.push_back(url);
     else
-      _customSearchEngines.push_back(url);
+      _secondList.push_back(url);
   }
   // Sort |fixedCutomeSearchEngines_| by TemplateURL's prepopulate_id. If
   // prepopulated_id == 0, it's a custom search engine and should be put in the
   // end of the list.
-  std::sort(_priorSearchEngines.begin(), _priorSearchEngines.end(),
+  std::sort(_firstList.begin(), _firstList.end(),
             [](const TemplateURL* lhs, const TemplateURL* rhs) {
               if (lhs->prepopulate_id() == 0)
                 return false;
@@ -248,11 +297,10 @@
               return lhs->prepopulate_id() < rhs->prepopulate_id();
             });
 
-  // Partially sort |_customSearchEngines| by TemplateURL's last_visited time.
-  auto begin = _customSearchEngines.begin();
-  auto end = _customSearchEngines.end();
-  auto pivot =
-      begin + std::min(kMaxcustomSearchEngines, _customSearchEngines.size());
+  // Partially sort |_secondList| by TemplateURL's last_visited time.
+  auto begin = _secondList.begin();
+  auto end = _secondList.end();
+  auto pivot = begin + std::min(kMaxcustomSearchEngines, _secondList.size());
   std::partial_sort(begin, pivot, end,
                     [](const TemplateURL* lhs, const TemplateURL* rhs) {
                       return lhs->last_visited() > rhs->last_visited();
@@ -263,29 +311,33 @@
   auto cutBegin = std::find_if(begin, pivot, [cutoff](const TemplateURL* url) {
     return url->last_visited() < cutoff;
   });
-  _customSearchEngines.erase(cutBegin, end);
+  _secondList.erase(cutBegin, end);
 }
 
-// Sets the search engine at |index| in prior section as default search engine.
-- (void)setDefaultToPriorSearchEngineAtIndex:(NSUInteger)index {
-  DCHECK_GE(index, 0U);
-  DCHECK_LT(index, _priorSearchEngines.size());
-  _updatingBackend = YES;
-  _templateURLService->SetUserSelectedDefaultSearchProvider(
-      _priorSearchEngines[index]);
-  [self recordUmaOfDefaultSearchEngine];
-  _updatingBackend = NO;
-}
-
-// Sets the search engine at |index| in custom section as default search engine.
-- (void)setDefaultToCustomSearchEngineAtIndex:(NSUInteger)index {
-  DCHECK_GE(index, 0U);
-  DCHECK_LT(index, _customSearchEngines.size());
-  _updatingBackend = YES;
-  _templateURLService->SetUserSelectedDefaultSearchProvider(
-      _customSearchEngines[index]);
-  [self recordUmaOfDefaultSearchEngine];
-  _updatingBackend = NO;
+// Creates a SearchEngineItem for |templateURL|.
+- (SearchEngineItem*)createSearchEngineItemFromTemplateURL:
+    (const TemplateURL*)templateURL {
+  SearchEngineItem* item = nil;
+  if (templateURL->prepopulate_id() > 0) {
+    item = [[SearchEngineItem alloc] initWithType:ItemTypePrepopulatedEngine];
+    // Fake up a page URL for favicons of prepopulated search engines, since
+    // favicons may be fetched from Google server which doesn't suppoprt
+    // icon URL.
+    std::string emptyPageUrl = templateURL->url_ref().ReplaceSearchTerms(
+        TemplateURLRef::SearchTermsArgs(base::string16()),
+        _templateURLService->search_terms_data());
+    item.URL = GURL(emptyPageUrl);
+  } else {
+    item = [[SearchEngineItem alloc] initWithType:ItemTypeCustomEngine];
+    // Use icon URL for favicons of custom search engines.
+    item.URL = templateURL->favicon_url();
+  }
+  item.text = base::SysUTF16ToNSString(templateURL->short_name());
+  item.detailText = base::SysUTF16ToNSString(templateURL->keyword());
+  if (templateURL == _templateURLService->GetDefaultSearchProvider()) {
+    [item setAccessoryType:UITableViewCellAccessoryCheckmark];
+  }
+  return item;
 }
 
 // Records the type of the selected default search engine.
@@ -297,9 +349,4 @@
       SEARCH_ENGINE_MAX);
 }
 
-- (void)searchEngineChanged {
-  if (!_updatingBackend)
-    [self reloadData];
-}
-
 @end
diff --git a/ios/chrome/browser/ui/settings/search_engine_table_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/search_engine_table_view_controller_unittest.mm
index beefa3f..3a1d739 100644
--- a/ios/chrome/browser/ui/settings/search_engine_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/search_engine_table_view_controller_unittest.mm
@@ -17,7 +17,7 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/search_engines/template_url_service_factory.h"
-#import "ios/chrome/browser/ui/table_view/cells/table_view_detail_text_item.h"
+#import "ios/chrome/browser/ui/settings/cells/search_engine_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller_test.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -35,6 +35,24 @@
 const char kUmaSelectDefaultSearchEngine[] =
     "Search.iOS.SelectDefaultSearchEngine";
 
+// Prepopulated search engines.
+const std::string kEngineP1Name = "prepopulated-1";
+const GURL kEngineP1Url = GURL("https://p1.com?q={searchTerms}");
+const std::string kEngineP2Name = "prepopulated-2";
+const GURL kEngineP2Url = GURL("https://p2.com?q={searchTerms}");
+const std::string kEngineP3Name = "prepopulated-3";
+const GURL kEngineP3Url = GURL("https://p3.com?q={searchTerms}");
+
+// Custom search engines.
+const std::string kEngineC1Name = "custom-1";
+const GURL kEngineC1Url = GURL("https://c1.com?q={searchTerms}");
+const std::string kEngineC2Name = "custom-2";
+const GURL kEngineC2Url = GURL("https://c2.com?q={searchTerms}");
+const std::string kEngineC3Name = "custom-3";
+const GURL kEngineC3Url = GURL("https://c3.com?q={searchTerms}");
+const std::string kEngineC4Name = "custom-4";
+const GURL kEngineC4Url = GURL("https://c4.com?q={searchTerms}");
+
 class SearchEngineTableViewControllerTest
     : public ChromeTableViewControllerTest {
  protected:
@@ -57,14 +75,20 @@
   }
 
   // Adds a prepopulated search engine to TemplateURLService.
+  // |prepopulate_id| should be big enough (>1000) to avoid collision with real
+  // prepopulated search engines. The collision happens when
+  // TemplateURLService::SetUserSelectedDefaultSearchProvider is called, in the
+  // callback of PrefService the DefaultSearchManager will update the searchable
+  // URL of default search engine from prepopulated search engines list.
   TemplateURL* AddPriorSearchEngine(const std::string& short_name,
-                                    const std::string& keyword,
+                                    const GURL& searchable_url,
                                     int prepopulate_id,
                                     bool set_default) {
     TemplateURLData data;
     data.SetShortName(base::ASCIIToUTF16(short_name));
-    data.SetURL("https://chromium.test/index.php?q={searchTerms}");
-    data.SetKeyword(base::ASCIIToUTF16(keyword));
+    data.SetKeyword(base::ASCIIToUTF16(short_name));
+    data.SetURL(searchable_url.possibly_invalid_spec());
+    data.favicon_url = TemplateURL::GenerateFaviconURL(searchable_url);
     data.prepopulate_id = prepopulate_id;
     TemplateURL* url =
         template_url_service_->Add(std::make_unique<TemplateURL>(data));
@@ -75,13 +99,14 @@
 
   // Adds a custom search engine to TemplateURLService.
   TemplateURL* AddCustomSearchEngine(const std::string& short_name,
-                                     const std::string& keyword,
+                                     const GURL& searchable_url,
                                      base::Time last_visited_time,
                                      bool set_default) {
     TemplateURLData data;
     data.SetShortName(base::ASCIIToUTF16(short_name));
-    data.SetURL("https://chromium.test/index.php?q={searchTerms}");
-    data.SetKeyword(base::ASCIIToUTF16(keyword));
+    data.SetKeyword(base::ASCIIToUTF16(short_name));
+    data.SetURL(searchable_url.possibly_invalid_spec());
+    data.favicon_url = TemplateURL::GenerateFaviconURL(searchable_url);
     data.last_visited = last_visited_time;
     TemplateURL* url =
         template_url_service_->Add(std::make_unique<TemplateURL>(data));
@@ -90,15 +115,77 @@
     return url;
   }
 
-  // Checks if a text cell in the TableView has a check mark.
-  void CheckTextCellChecked(bool expect_checked, int section, int item) {
-    TableViewDetailTextItem* text_item =
-        base::mac::ObjCCastStrict<TableViewDetailTextItem>(
-            GetTableViewItem(section, item));
-    ASSERT_TRUE(text_item);
-    EXPECT_EQ(expect_checked ? UITableViewCellAccessoryCheckmark
-                             : UITableViewCellAccessoryNone,
-              text_item.accessoryType);
+  void CheckItem(NSString* expected_text,
+                 NSString* expected_detail_text,
+                 const GURL& expected_url,
+                 bool expected_checked,
+                 int section,
+                 int row) {
+    SearchEngineItem* item = base::mac::ObjCCastStrict<SearchEngineItem>(
+        GetTableViewItem(section, row));
+    EXPECT_NSEQ(expected_text, item.text);
+    EXPECT_NSEQ(expected_detail_text, item.detailText);
+    EXPECT_EQ(expected_url, item.URL);
+    EXPECT_EQ(expected_checked ? UITableViewCellAccessoryCheckmark
+                               : UITableViewCellAccessoryNone,
+              item.accessoryType);
+  }
+
+  // Checks a SearchEngineItem with data from a fabricated TemplateURL. The
+  // SearchEngineItem in the |row| of |section| should contain a title and a
+  // subtitle that are equal to |expected_text| and an URL which can be
+  // generated by filling empty query word into |expected_searchable_url|. If
+  // |expected_checked| is true, the SearchEngineItem should have a
+  // UITableViewCellAccessoryCheckmark.
+  void CheckPrepopulatedItem(const std::string& expected_text,
+                             const GURL& expected_searchable_url,
+                             bool expected_checked,
+                             int section,
+                             int row) {
+    TemplateURLData data;
+    data.SetURL(expected_searchable_url.possibly_invalid_spec());
+    const std::string expected_url =
+        TemplateURL(data).url_ref().ReplaceSearchTerms(
+            TemplateURLRef::SearchTermsArgs(base::string16()),
+            template_url_service_->search_terms_data());
+    CheckItem(base::SysUTF8ToNSString(expected_text),
+              base::SysUTF8ToNSString(expected_text), GURL(expected_url),
+              expected_checked, section, row);
+  }
+
+  // Checks a SearchEngineItem with data from a fabricated TemplateURL. The
+  // SearchEngineItem in the |row| of |section| should contain a title and a
+  // subtitle that are equal to |expected_text| and an URL
+  // which can be generated from |expected_searchable_url| by
+  // TemplateURL::GenerateFaviconURL. If |expected_checked| is true, the
+  // SearchEngineItem should have a UITableViewCellAccessoryCheckmark.
+  void CheckCustomItem(const std::string& expected_text,
+                       const GURL& expected_searchable_url,
+                       bool expected_checked,
+                       int section,
+                       int row) {
+    CheckItem(base::SysUTF8ToNSString(expected_text),
+              base::SysUTF8ToNSString(expected_text),
+              TemplateURL::GenerateFaviconURL(expected_searchable_url),
+              expected_checked, section, row);
+  }
+
+  // Checks a SearchEngineItem with data from a real prepopulated
+  // TemplateURL. The SearchEngineItem in the |row| of |section| should
+  // contain a title equal to |expected_text|, a subtitle equal to
+  // |expected_detail_text|, and an URL equal to |expected_favicon_url|. If
+  // |expected_checked| is true, the SearchEngineItem should have a
+  // UITableViewCellAccessoryCheckmark.
+  void CheckRealItem(const TemplateURL* turl,
+                     bool expected_checked,
+                     int section,
+                     int row) {
+    CheckItem(base::SysUTF16ToNSString(turl->short_name()),
+              base::SysUTF16ToNSString(turl->keyword()),
+              GURL(turl->url_ref().ReplaceSearchTerms(
+                  TemplateURLRef::SearchTermsArgs(base::string16()),
+                  template_url_service_->search_terms_data())),
+              expected_checked, section, row);
   }
 
   web::TestWebThreadBundle thread_bundle_;
@@ -118,20 +205,20 @@
 // and a prepopulated search engine is selected as default.
 TEST_F(SearchEngineTableViewControllerTest,
        TestUrlsLoadedWithPrepopulatedSearchEngineAsDefault) {
-  AddPriorSearchEngine("prepopulated-3", "p3.com", 3, false);
-  AddPriorSearchEngine("prepopulated-1", "p1.com", 1, false);
-  AddPriorSearchEngine("prepopulated-2", "p2.com", 2, true);
+  AddPriorSearchEngine(kEngineP3Name, kEngineP3Url, 1003, false);
+  AddPriorSearchEngine(kEngineP1Name, kEngineP1Url, 1001, false);
+  AddPriorSearchEngine(kEngineP2Name, kEngineP2Url, 1002, true);
 
-  AddCustomSearchEngine("custom-4", "c4.com",
+  AddCustomSearchEngine(kEngineC4Name, kEngineC4Url,
                         base::Time::Now() - base::TimeDelta::FromDays(10),
                         false);
-  AddCustomSearchEngine("custom-1", "c1.com",
+  AddCustomSearchEngine(kEngineC1Name, kEngineC1Url,
                         base::Time::Now() - base::TimeDelta::FromSeconds(10),
                         false);
-  AddCustomSearchEngine("custom-3", "c3.com",
+  AddCustomSearchEngine(kEngineC3Name, kEngineC3Url,
                         base::Time::Now() - base::TimeDelta::FromHours(10),
                         false);
-  AddCustomSearchEngine("custom-2", "c2.com",
+  AddCustomSearchEngine(kEngineC2Name, kEngineC2Url,
                         base::Time::Now() - base::TimeDelta::FromMinutes(10),
                         false);
 
@@ -140,40 +227,34 @@
 
   ASSERT_EQ(2, NumberOfSections());
   ASSERT_EQ(3, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(@"prepopulated-1", @"p1.com", 0, 0);
-  CheckTextCellChecked(false, 0, 0);
-  CheckTextCellTextAndDetailText(@"prepopulated-2", @"p2.com", 0, 1);
-  CheckTextCellChecked(true, 0, 1);
-  CheckTextCellTextAndDetailText(@"prepopulated-3", @"p3.com", 0, 2);
-  CheckTextCellChecked(false, 0, 2);
+  CheckPrepopulatedItem(kEngineP1Name, kEngineP1Url, false, 0, 0);
+  CheckPrepopulatedItem(kEngineP2Name, kEngineP2Url, true, 0, 1);
+  CheckPrepopulatedItem(kEngineP3Name, kEngineP3Url, false, 0, 2);
 
   ASSERT_EQ(3, NumberOfItemsInSection(1));
-  CheckTextCellTextAndDetailText(@"custom-1", @"c1.com", 1, 0);
-  CheckTextCellChecked(false, 1, 0);
-  CheckTextCellTextAndDetailText(@"custom-2", @"c2.com", 1, 1);
-  CheckTextCellChecked(false, 1, 1);
-  CheckTextCellTextAndDetailText(@"custom-3", @"c3.com", 1, 2);
-  CheckTextCellChecked(false, 1, 2);
+  CheckCustomItem(kEngineC1Name, kEngineC1Url, false, 1, 0);
+  CheckCustomItem(kEngineC2Name, kEngineC2Url, false, 1, 1);
+  CheckCustomItem(kEngineC3Name, kEngineC3Url, false, 1, 2);
 }
 
 // Tests that items are displayed correctly when TemplateURLService is filled
 // and a custom search engine is selected as default.
 TEST_F(SearchEngineTableViewControllerTest,
        TestUrlsLoadedWithCustomSearchEngineAsDefault) {
-  AddPriorSearchEngine("prepopulated-3", "p3.com", 3, false);
-  AddPriorSearchEngine("prepopulated-1", "p1.com", 1, false);
-  AddPriorSearchEngine("prepopulated-2", "p2.com", 2, false);
+  AddPriorSearchEngine(kEngineP3Name, kEngineP3Url, 1003, false);
+  AddPriorSearchEngine(kEngineP1Name, kEngineP1Url, 1001, false);
+  AddPriorSearchEngine(kEngineP2Name, kEngineP2Url, 1002, false);
 
-  AddCustomSearchEngine("custom-4", "c4.com",
+  AddCustomSearchEngine(kEngineC4Name, kEngineC4Url,
                         base::Time::Now() - base::TimeDelta::FromDays(10),
                         false);
-  AddCustomSearchEngine("custom-1", "c1.com",
+  AddCustomSearchEngine(kEngineC1Name, kEngineC1Url,
                         base::Time::Now() - base::TimeDelta::FromSeconds(10),
                         false);
-  AddCustomSearchEngine("custom-3", "c3.com",
+  AddCustomSearchEngine(kEngineC3Name, kEngineC3Url,
                         base::Time::Now() - base::TimeDelta::FromHours(10),
                         false);
-  AddCustomSearchEngine("custom-2", "c2.com",
+  AddCustomSearchEngine(kEngineC2Name, kEngineC2Url,
                         base::Time::Now() - base::TimeDelta::FromMinutes(10),
                         true);
 
@@ -182,62 +263,50 @@
 
   ASSERT_EQ(2, NumberOfSections());
   ASSERT_EQ(4, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(@"prepopulated-1", @"p1.com", 0, 0);
-  CheckTextCellChecked(false, 0, 0);
-  CheckTextCellTextAndDetailText(@"prepopulated-2", @"p2.com", 0, 1);
-  CheckTextCellChecked(false, 0, 1);
-  CheckTextCellTextAndDetailText(@"prepopulated-3", @"p3.com", 0, 2);
-  CheckTextCellChecked(false, 0, 2);
-  CheckTextCellTextAndDetailText(@"custom-2", @"c2.com", 0, 3);
-  CheckTextCellChecked(true, 0, 3);
+  CheckPrepopulatedItem(kEngineP1Name, kEngineP1Url, false, 0, 0);
+  CheckPrepopulatedItem(kEngineP2Name, kEngineP2Url, false, 0, 1);
+  CheckPrepopulatedItem(kEngineP3Name, kEngineP3Url, false, 0, 2);
+  CheckCustomItem(kEngineC2Name, kEngineC2Url, true, 0, 3);
 
   ASSERT_EQ(2, NumberOfItemsInSection(1));
-  CheckTextCellTextAndDetailText(@"custom-1", @"c1.com", 1, 0);
-  CheckTextCellChecked(false, 1, 0);
-  CheckTextCellTextAndDetailText(@"custom-3", @"c3.com", 1, 1);
-  CheckTextCellChecked(false, 1, 1);
+  CheckCustomItem(kEngineC1Name, kEngineC1Url, false, 1, 0);
+  CheckCustomItem(kEngineC3Name, kEngineC3Url, false, 1, 1);
 }
 
 // Tests that when TemplateURLService add or remove TemplateURLs, or update
 // default search engine, the controller will update the displayed items.
 TEST_F(SearchEngineTableViewControllerTest, TestUrlModifiedByService) {
   TemplateURL* url_p1 =
-      AddPriorSearchEngine("prepopulated-1", "p1.com", 1, true);
+      AddPriorSearchEngine(kEngineP1Name, kEngineP1Url, 1001, true);
 
   CreateController();
   CheckController();
 
   ASSERT_EQ(1, NumberOfSections());
   ASSERT_EQ(1, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(@"prepopulated-1", @"p1.com", 0, 0);
-  CheckTextCellChecked(true, 0, 0);
+  CheckPrepopulatedItem(kEngineP1Name, kEngineP1Url, true, 0, 0);
 
   TemplateURL* url_p2 =
-      AddPriorSearchEngine("prepopulated-2", "p2.com", 2, false);
+      AddPriorSearchEngine(kEngineP2Name, kEngineP2Url, 1002, false);
 
   ASSERT_EQ(1, NumberOfSections());
   ASSERT_EQ(2, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(@"prepopulated-1", @"p1.com", 0, 0);
-  CheckTextCellChecked(true, 0, 0);
-  CheckTextCellTextAndDetailText(@"prepopulated-2", @"p2.com", 0, 1);
-  CheckTextCellChecked(false, 0, 1);
+  CheckPrepopulatedItem(kEngineP1Name, kEngineP1Url, true, 0, 0);
+  CheckPrepopulatedItem(kEngineP2Name, kEngineP2Url, false, 0, 1);
 
   template_url_service_->SetUserSelectedDefaultSearchProvider(url_p2);
 
   ASSERT_EQ(1, NumberOfSections());
   ASSERT_EQ(2, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(@"prepopulated-1", @"p1.com", 0, 0);
-  CheckTextCellChecked(false, 0, 0);
-  CheckTextCellTextAndDetailText(@"prepopulated-2", @"p2.com", 0, 1);
-  CheckTextCellChecked(true, 0, 1);
+  CheckPrepopulatedItem(kEngineP1Name, kEngineP1Url, false, 0, 0);
+  CheckPrepopulatedItem(kEngineP2Name, kEngineP2Url, true, 0, 1);
 
   template_url_service_->SetUserSelectedDefaultSearchProvider(url_p1);
   template_url_service_->Remove(url_p2);
 
   ASSERT_EQ(1, NumberOfSections());
   ASSERT_EQ(1, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(@"prepopulated-1", @"p1.com", 0, 0);
-  CheckTextCellChecked(true, 0, 0);
+  CheckPrepopulatedItem(kEngineP1Name, kEngineP1Url, true, 0, 0);
 }
 
 // Tests that when user change default search engine, all items can be displayed
@@ -268,9 +337,9 @@
     std::swap(url_p1_index, url_p2_index);
 
   // Also add some custom search engines.
-  TemplateURL* url_c1 =
-      AddCustomSearchEngine("custom-1", "c1.com", base::Time::Now(), false);
-  AddCustomSearchEngine("custom-2", "c2.com",
+  TemplateURL* url_c1 = AddCustomSearchEngine(kEngineC1Name, kEngineC1Url,
+                                              base::Time::Now(), false);
+  AddCustomSearchEngine(kEngineC2Name, kEngineC2Url,
                         base::Time::Now() - base::TimeDelta::FromSeconds(10),
                         false);
 
@@ -285,20 +354,12 @@
   ASSERT_EQ(2, NumberOfSections());
   // Check first list.
   ASSERT_EQ(2, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(base::SysUTF16ToNSString(url_p1->short_name()),
-                                 base::SysUTF16ToNSString(url_p1->keyword()), 0,
-                                 url_p1_index);
-  CheckTextCellChecked(true, 0, url_p1_index);
-  CheckTextCellTextAndDetailText(base::SysUTF16ToNSString(url_p2->short_name()),
-                                 base::SysUTF16ToNSString(url_p2->keyword()), 0,
-                                 url_p2_index);
-  CheckTextCellChecked(false, 0, url_p2_index);
+  CheckRealItem(url_p1, true, 0, url_p1_index);
+  CheckRealItem(url_p2, false, 0, url_p2_index);
   // Check second list.
   ASSERT_EQ(2, NumberOfItemsInSection(1));
-  CheckTextCellTextAndDetailText(@"custom-1", @"c1.com", 1, 0);
-  CheckTextCellChecked(false, 1, 0);
-  CheckTextCellTextAndDetailText(@"custom-2", @"c2.com", 1, 1);
-  CheckTextCellChecked(false, 1, 1);
+  CheckCustomItem(kEngineC1Name, kEngineC1Url, false, 1, 0);
+  CheckCustomItem(kEngineC2Name, kEngineC2Url, false, 1, 1);
   // Check default search engine.
   EXPECT_EQ(url_p1, template_url_service_->GetDefaultSearchProvider());
   // Check UMA.
@@ -314,20 +375,12 @@
   ASSERT_EQ(2, NumberOfSections());
   // Check first list.
   ASSERT_EQ(2, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(base::SysUTF16ToNSString(url_p1->short_name()),
-                                 base::SysUTF16ToNSString(url_p1->keyword()), 0,
-                                 url_p1_index);
-  CheckTextCellChecked(false, 0, url_p1_index);
-  CheckTextCellTextAndDetailText(base::SysUTF16ToNSString(url_p2->short_name()),
-                                 base::SysUTF16ToNSString(url_p2->keyword()), 0,
-                                 url_p2_index);
-  CheckTextCellChecked(true, 0, url_p2_index);
+  CheckRealItem(url_p1, false, 0, url_p1_index);
+  CheckRealItem(url_p2, true, 0, url_p2_index);
   // Check second list.
   ASSERT_EQ(2, NumberOfItemsInSection(1));
-  CheckTextCellTextAndDetailText(@"custom-1", @"c1.com", 1, 0);
-  CheckTextCellChecked(false, 1, 0);
-  CheckTextCellTextAndDetailText(@"custom-2", @"c2.com", 1, 1);
-  CheckTextCellChecked(false, 1, 1);
+  CheckCustomItem(kEngineC1Name, kEngineC1Url, false, 1, 0);
+  CheckCustomItem(kEngineC2Name, kEngineC2Url, false, 1, 1);
   // Check default search engine.
   EXPECT_EQ(url_p2, template_url_service_->GetDefaultSearchProvider());
   // Check UMA.
@@ -346,20 +399,12 @@
   ASSERT_EQ(2, NumberOfSections());
   // Check first list.
   ASSERT_EQ(2, NumberOfItemsInSection(0));
-  CheckTextCellTextAndDetailText(base::SysUTF16ToNSString(url_p1->short_name()),
-                                 base::SysUTF16ToNSString(url_p1->keyword()), 0,
-                                 url_p1_index);
-  CheckTextCellChecked(false, 0, url_p1_index);
-  CheckTextCellTextAndDetailText(base::SysUTF16ToNSString(url_p2->short_name()),
-                                 base::SysUTF16ToNSString(url_p2->keyword()), 0,
-                                 url_p2_index);
-  CheckTextCellChecked(false, 0, url_p2_index);
+  CheckRealItem(url_p1, false, 0, url_p1_index);
+  CheckRealItem(url_p2, false, 0, url_p2_index);
   // Check second list.
   ASSERT_EQ(2, NumberOfItemsInSection(1));
-  CheckTextCellTextAndDetailText(@"custom-1", @"c1.com", 1, 0);
-  CheckTextCellChecked(true, 1, 0);
-  CheckTextCellTextAndDetailText(@"custom-2", @"c2.com", 1, 1);
-  CheckTextCellChecked(false, 1, 1);
+  CheckCustomItem(kEngineC1Name, kEngineC1Url, true, 1, 0);
+  CheckCustomItem(kEngineC2Name, kEngineC2Url, false, 1, 1);
   // Check default search engine.
   EXPECT_EQ(url_c1, template_url_service_->GetDefaultSearchProvider());
   // Check UMA.
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
index 8a45471..bc4a91e 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
@@ -23,7 +23,6 @@
 #import "ios/chrome/browser/tabs/tab_title_util.h"
 #import "ios/chrome/browser/ui/tab_grid/grid/grid_consumer.h"
 #import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer_bridge.h"
@@ -401,16 +400,10 @@
   if (IsURLNtp(webState->GetVisibleURL())) {
     return;
   }
-  UIImage* defaultFavicon;
-  if (IsUIRefreshPhase1Enabled()) {
-    if (webState->GetBrowserState()->IsOffTheRecord()) {
-      defaultFavicon = [UIImage imageNamed:@"default_world_favicon_incognito"];
-    } else {
-      defaultFavicon = [UIImage imageNamed:@"default_world_favicon_regular"];
-    }
-  } else {
-    defaultFavicon = [UIImage imageNamed:@"default_favicon"];
-  }
+  UIImage* defaultFavicon =
+      webState->GetBrowserState()->IsOffTheRecord()
+          ? [UIImage imageNamed:@"default_world_favicon_incognito"]
+          : [UIImage imageNamed:@"default_world_favicon_regular"];
   completion(defaultFavicon);
 
   favicon::FaviconDriver* faviconDriver =
diff --git a/ios/chrome/browser/ui/tabs/tab_view.mm b/ios/chrome/browser/ui/tabs/tab_view.mm
index 99110798..ed25877 100644
--- a/ios/chrome/browser/ui/tabs/tab_view.mm
+++ b/ios/chrome/browser/ui/tabs/tab_view.mm
@@ -16,7 +16,6 @@
 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
 #import "ios/chrome/browser/ui/image_util/image_util.h"
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/highlight_button.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
@@ -386,26 +385,13 @@
 }
 
 - (void)updateCloseButtonImages {
-  if (IsUIRefreshPhase1Enabled()) {
-    [_closeButton
-        setImage:[[UIImage imageNamed:@"grid_cell_close_button"]
-                     imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]
-        forState:UIControlStateNormal];
-    _closeButton.tintColor = _incognitoStyle
-                                 ? UIColorFromRGB(kTabCloseTintIncognito)
-                                 : UIColorFromRGB(kTabCloseTint);
-  } else {
-    NSString* incognito = self.incognitoStyle ? @"_incognito" : @"";
-    UIImage* normalImage = [UIImage
-        imageNamed:[NSString stringWithFormat:@"tabstrip_tab_close%@_legacy",
-                                              incognito]];
-    UIImage* pressedImage = [UIImage
-        imageNamed:[NSString
-                       stringWithFormat:@"tabstrip_tab_close%@_pressed_legacy",
-                                        incognito]];
-    [_closeButton setImage:normalImage forState:UIControlStateNormal];
-    [_closeButton setImage:pressedImage forState:UIControlStateHighlighted];
-  }
+  [_closeButton
+      setImage:[[UIImage imageNamed:@"grid_cell_close_button"]
+                   imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]
+      forState:UIControlStateNormal];
+  _closeButton.tintColor = _incognitoStyle
+                               ? UIColorFromRGB(kTabCloseTintIncognito)
+                               : UIColorFromRGB(kTabCloseTint);
 }
 
 - (UIImage*)defaultFaviconImage {
diff --git a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller_unittest.mm b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller_unittest.mm
index 335ee70..73acdfd 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive_toolbar_view_controller_unittest.mm
@@ -15,7 +15,6 @@
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button_factory.h"
 #import "ios/chrome/browser/ui/toolbar/primary_toolbar_view_controller.h"
 #import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #import "third_party/ocmock/OCMock/OCMock.h"
@@ -49,9 +48,6 @@
 };
 
 TEST_F(AdaptiveToolbarViewControllerTest, DetectForceTouch) {
-  if (!IsUIRefreshPhase1Enabled())
-    return;
-
   id dispatcher = OCMProtocolMock(@protocol(PopupMenuCommands));
   id longPressDelegate = OCMProtocolMock(@protocol(PopupMenuLongPressDelegate));
   ToolbarButtonFactory* factory =
diff --git a/ios/chrome/browser/ui/translate/translate_infobar_view.mm b/ios/chrome/browser/ui/translate/translate_infobar_view.mm
index f6017b5..a4e4740 100644
--- a/ios/chrome/browser/ui/translate/translate_infobar_view.mm
+++ b/ios/chrome/browser/ui/translate/translate_infobar_view.mm
@@ -18,7 +18,6 @@
 #import "ios/chrome/browser/ui/util/label_link_controller.h"
 #import "ios/chrome/browser/ui/util/layout_guide_names.h"
 #import "ios/chrome/browser/ui/util/named_guide.h"
-#include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -214,11 +213,7 @@
                                     a11yAnnoucement);
   }
 
-  if (IsUIRefreshPhase1Enabled()) {
-    self.backgroundColor = UIColorFromRGB(kInfobarBackgroundColor);
-  } else {
-    self.backgroundColor = [UIColor whiteColor];
-  }
+  self.backgroundColor = UIColorFromRGB(kInfobarBackgroundColor);
   id<LayoutGuideProvider> safeAreaLayoutGuide = self.safeAreaLayoutGuide;
 
   // The Content view. Holds all the other subviews.
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index 2b4c3a0..5cda4d9 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -30,7 +30,7 @@
                                      base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kDisplaySearchEngineFavicon{
-    "DisplaySearchEngineFavicon", base::FEATURE_DISABLED_BY_DEFAULT};
+    "DisplaySearchEngineFavicon", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kNewOmniboxPopupLayout{"NewOmniboxPopupLayout",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index bd169d8..567629c 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -111,8 +111,10 @@
   [ChromeEarlGrey waitForPageToFinishLoading];
 
   web::WebState* webState = chrome_test_util::GetCurrentWebState();
-  if (webState->ContentIsHTML())
-    web::WaitUntilWindowIdInjected(webState);
+  if (webState->ContentIsHTML()) {
+    GREYAssert(web::WaitUntilWindowIdInjected(webState),
+               @"WindowID failed to inject");
+  }
 }
 
 + (void)reload {
diff --git a/ios/third_party/material_components_ios/BUILD.gn b/ios/third_party/material_components_ios/BUILD.gn
index 85c03fb..f204498 100644
--- a/ios/third_party/material_components_ios/BUILD.gn
+++ b/ios/third_party/material_components_ios/BUILD.gn
@@ -14,6 +14,7 @@
     "src/components/Buttons/src/MaterialButtons.h",
     "src/components/Buttons/src/ShapeThemer/MaterialButtons+ShapeThemer.h",
     "src/components/Buttons/src/TypographyThemer/MaterialButtons+TypographyThemer.h",
+    "src/components/Cards/src/MaterialCards.h",
     "src/components/Dialogs/src/MaterialDialogs.h",
     "src/components/ShadowElevations/src/MaterialShadowElevations.h",
     "src/components/schemes/Color/src/MaterialColorScheme.h",
diff --git a/ios/web/public/test/earl_grey/js_test_util.h b/ios/web/public/test/earl_grey/js_test_util.h
index 1dfdf80..fe4447b14 100644
--- a/ios/web/public/test/earl_grey/js_test_util.h
+++ b/ios/web/public/test/earl_grey/js_test_util.h
@@ -12,18 +12,9 @@
 namespace web {
 
 // Waits until the Window ID has been injected and the page is thus ready to
-// respond to JavaScript injection. Fails with a GREYAssert on timeout or if
+// respond to JavaScript injection. Returns false on timeout or if an
 // unrecoverable error (such as no web view) occurs.
-void WaitUntilWindowIdInjected(WebState* web_state);
-
-// Executes |javascript| on the given |web_state|, and waits until execution is
-// completed. If |out_error| is not nil, it is set to the error resulting from
-// the execution, if one occurs. The return value is the result of the
-// JavaScript execution. If the script execution is timed out, then this method
-// fails with a GREYAssert.
-id ExecuteJavaScript(WebState* web_state,
-                     NSString* javascript,
-                     NSError* __autoreleasing* out_error);
+bool WaitUntilWindowIdInjected(WebState* web_state) WARN_UNUSED_RESULT;
 
 // Synchronously returns the result of executed JavaScript on interstitial page
 // displayed for |web_state|.
diff --git a/ios/web/public/test/earl_grey/js_test_util.mm b/ios/web/public/test/earl_grey/js_test_util.mm
index 7adf7d8..b754f324 100644
--- a/ios/web/public/test/earl_grey/js_test_util.mm
+++ b/ios/web/public/test/earl_grey/js_test_util.mm
@@ -21,6 +21,32 @@
 
 namespace web {
 
+// Executes |javascript| on the given |web_state|, and waits until execution is
+// completed. If |out_error| is not nil, it is set to the error resulting from
+// the execution, if one occurs. The return value is the result of the
+// JavaScript execution, or nil if script execution timed out.
+id ExecuteJavaScript(WebState* web_state,
+                     NSString* javascript,
+                     NSError* __autoreleasing* out_error) {
+  __block bool did_complete = false;
+  __block id result = nil;
+  CRWJSInjectionReceiver* receiver = web_state->GetJSInjectionReceiver();
+  [receiver executeJavaScript:javascript
+            completionHandler:^(id value, NSError* error) {
+              did_complete = true;
+              result = [value copy];
+              if (out_error)
+                *out_error = [error copy];
+            }];
+
+  // Wait for completion.
+  BOOL succeeded = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
+    return did_complete;
+  });
+
+  return succeeded ? result : nil;
+}
+
 // Evaluates the given |script| on |interstitial|.
 void ExecuteScriptForTesting(web::WebInterstitialImpl* interstitial,
                              NSString* script,
@@ -29,7 +55,7 @@
   interstitial->ExecuteJavaScript(script, handler);
 }
 
-void WaitUntilWindowIdInjected(WebState* web_state) {
+bool WaitUntilWindowIdInjected(WebState* web_state) {
   bool is_window_id_injected = false;
   bool is_timeout = false;
   bool is_unrecoverable_error = false;
@@ -52,31 +78,7 @@
     }
     is_timeout = timeout < timer.Elapsed();
   }
-  GREYAssertFalse(is_timeout, @"windowID injection timed out");
-  GREYAssertFalse(is_unrecoverable_error, @"script execution error");
-}
-
-id ExecuteJavaScript(WebState* web_state,
-                     NSString* javascript,
-                     NSError* __autoreleasing* out_error) {
-  __block bool did_complete = false;
-  __block id result = nil;
-  CRWJSInjectionReceiver* receiver = web_state->GetJSInjectionReceiver();
-  [receiver executeJavaScript:javascript
-            completionHandler:^(id value, NSError* error) {
-              did_complete = true;
-              result = [value copy];
-              if (out_error)
-                *out_error = [error copy];
-            }];
-
-  // Wait for completion.
-  BOOL succeeded = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
-    return did_complete;
-  });
-  GREYAssert(succeeded, @"Script execution timed out");
-
-  return result;
+  return !is_timeout && !is_unrecoverable_error;
 }
 
 id ExecuteScriptOnInterstitial(WebState* web_state, NSString* script) {
@@ -92,8 +94,8 @@
   BOOL succeeded = WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
     return did_finish;
   });
-  GREYAssert(succeeded, @"Script execution timed out");
-  return script_result;
+
+  return succeeded ? script_result : nil;
 }
 
 }  // namespace web
diff --git a/ios/web/public/test/earl_grey/web_view_actions.mm b/ios/web/public/test/earl_grey/web_view_actions.mm
index 0f71aac..3099e4d 100644
--- a/ios/web/public/test/earl_grey/web_view_actions.mm
+++ b/ios/web/public/test/earl_grey/web_view_actions.mm
@@ -197,7 +197,7 @@
       return NO;
     }
 
-    // Run the action.
+    // Run the action and wait for the UI to settle.
     [[EarlGrey selectElementWithMatcher:WebViewInWebState(state)]
         performAction:action
                 error:error];
@@ -205,23 +205,23 @@
     if (*error) {
       return NO;
     }
+    [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
 
     // Wait for the verified to trigger and set |verified|.
     NSString* verification_timeout_message =
         [NSString stringWithFormat:@"The action (%@) on element %@ wasn't "
                                    @"verified before timing out.",
                                    action.name, selector.selectorDescription];
-    GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
-                   kWaitForVerificationTimeout,
-                   ^{
-                     return verified;
-                   }),
-               verification_timeout_message);
+    bool success = base::test::ios::WaitUntilConditionOrTimeout(
+        kWaitForVerificationTimeout, ^{
+          return verified;
+        });
 
-    // If |verified| is not true, the wait condition should have already exited
-    // this control flow, so sanity check that it has in fact been set to
-    // true by this point.
-    DCHECK(verified);
+    if (!success || !verified) {
+      DLOG(WARNING) << base::SysNSStringToUTF8(verification_timeout_message);
+      return NO;
+    }
+
     return YES;
   };
 
diff --git a/ios/web/shell/test/earl_grey/shell_earl_grey.mm b/ios/web/shell/test/earl_grey/shell_earl_grey.mm
index 0dd83d2..4878039 100644
--- a/ios/web/shell/test/earl_grey/shell_earl_grey.mm
+++ b/ios/web/shell/test/earl_grey/shell_earl_grey.mm
@@ -31,8 +31,11 @@
   if (!web::test::WaitForPageToFinishLoading(webState))
     return false;
 
-  if (webState->ContentIsHTML())
-    web::WaitUntilWindowIdInjected(webState);
+  if (webState->ContentIsHTML()) {
+    if (!web::WaitUntilWindowIdInjected(webState)) {
+      return false;
+    }
+  }
 
   // Ensure any UI elements handled by EarlGrey become idle for any subsequent
   // EarlGrey steps.
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 66e8c0c..357e51f 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -617,7 +617,6 @@
       "test:video_player_test_environment",
       "test:video_player_thumbnail_renderer",
       "//media:test_support",
-      "//mojo/core/embedder",
       "//testing/gtest",
     ]
   }
@@ -637,7 +636,6 @@
       "test:video_player",
       "test:video_player_test_environment",
       "//media:test_support",
-      "//mojo/core/embedder",
       "//testing/gtest",
     ]
   }
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index d1590a6..73d65c09 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -182,6 +182,7 @@
       "//base/test:test_config",
       "//base/test:test_support",
       "//media/gpu",
+      "//mojo/core/embedder:embedder",
       "//testing/gtest:gtest",
     ]
     if (use_ozone) {
diff --git a/media/gpu/test/image_processor/image_processor_client.cc b/media/gpu/test/image_processor/image_processor_client.cc
index bb3e2f5..c27e712 100644
--- a/media/gpu/test/image_processor/image_processor_client.cc
+++ b/media/gpu/test/image_processor/image_processor_client.cc
@@ -50,13 +50,13 @@
   LOG_ASSERT(dst_layout.planes().size() == num_planes);
   LOG_ASSERT(src_frame->layout().planes().size() == num_planes);
   for (size_t i = 0; i < num_planes; ++i) {
+    // |width| in libyuv::CopyPlane() is in bytes, not pixels.
+    gfx::Size plane_size = VideoFrame::PlaneSize(dst_frame->format(), i,
+                                                 dst_frame->natural_size());
     libyuv::CopyPlane(
         src_frame->data(i), src_frame->layout().planes()[i].stride,
         dst_frame->data(i), dst_frame->layout().planes()[i].stride,
-        VideoFrame::Columns(i, dst_frame->format(),
-                            dst_frame->natural_size().width()),
-        VideoFrame::Rows(i, dst_frame->format(),
-                         dst_frame->natural_size().height()));
+        plane_size.width(), plane_size.height());
   }
 
   return dst_frame;
@@ -223,7 +223,7 @@
   // VideoFrame should be processed in FIFO order.
   EXPECT_EQ(frame_index, num_processed_frames_);
   for (auto& processor : frame_processors_)
-    processor->ProcessVideoFrame(std::move(frame), frame_index);
+    processor->ProcessVideoFrame(frame, frame_index);
   num_processed_frames_++;
   output_cv_.Signal();
 }
diff --git a/media/gpu/test/video_player/video_player_test_environment.cc b/media/gpu/test/video_player/video_player_test_environment.cc
index 1c40bcf..974ca91a 100644
--- a/media/gpu/test/video_player/video_player_test_environment.cc
+++ b/media/gpu/test/video_player/video_player_test_environment.cc
@@ -4,14 +4,35 @@
 
 #include "media/gpu/test/video_player/video_player_test_environment.h"
 
+#include "base/command_line.h"
+#include "base/test/test_timeouts.h"
+#include "media/gpu/buildflags.h"
+#include "media/gpu/test/video_player/video.h"
+#include "mojo/core/embedder/embedder.h"
+
+#if BUILDFLAG(USE_VAAPI)
+#include "media/gpu/vaapi/vaapi_wrapper.h"
+#endif
+
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
 namespace media {
 namespace test {
 VideoPlayerTestEnvironment::VideoPlayerTestEnvironment(const Video* video)
     : video_(video) {}
 
-VideoPlayerTestEnvironment::~VideoPlayerTestEnvironment() = default;
-
 void VideoPlayerTestEnvironment::SetUp() {
+  // Using shared memory requires mojo to be initialized (crbug.com/849207).
+  mojo::core::Init();
+
+  // Needed to enable DVLOG through --vmodule.
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  LOG_ASSERT(logging::InitLogging(settings));
+
   // Setting up a task environment will create a task runner for the current
   // thread and allow posting tasks to other threads. This is required for the
   // test video player to function correctly.
diff --git a/media/gpu/test/video_player/video_player_test_environment.h b/media/gpu/test/video_player/video_player_test_environment.h
index cb66a573..014e9700 100644
--- a/media/gpu/test/video_player/video_player_test_environment.h
+++ b/media/gpu/test/video_player/video_player_test_environment.h
@@ -6,29 +6,27 @@
 #define MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_PLAYER_TEST_ENVIRONMENT_H_
 
 #include <memory>
+
 #include "base/at_exit.h"
 #include "base/test/scoped_task_environment.h"
-#include "base/test/test_timeouts.h"
-#include "media/gpu/buildflags.h"
-#include "media/gpu/test/video_player/video.h"
-#if BUILDFLAG(USE_VAAPI)
-#include "media/gpu/vaapi/vaapi_wrapper.h"
-#endif
 #include "testing/gtest/include/gtest/gtest.h"
+
 #if defined(USE_OZONE)
-#include "ui/ozone/public/ozone_gpu_test_helper.h"
-#include "ui/ozone/public/ozone_platform.h"
+namespace ui {
+class OzoneGpuTestHelper;
+}
 #endif
 
 namespace media {
 namespace test {
 
+class Video;
+
 // Test environment for video decode tests. Performs setup and teardown once for
 // the entire test run.
 class VideoPlayerTestEnvironment : public ::testing::Environment {
  public:
   explicit VideoPlayerTestEnvironment(const Video* video);
-  ~VideoPlayerTestEnvironment();
 
   // Set up the video decode test environment, only called once.
   void SetUp() override;
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/video_decode_accelerator_perf_tests.cc
index 060c764..b1e51e14 100644
--- a/media/gpu/video_decode_accelerator_perf_tests.cc
+++ b/media/gpu/video_decode_accelerator_perf_tests.cc
@@ -13,7 +13,6 @@
 #include "media/gpu/test/video_player/video_decoder_client.h"
 #include "media/gpu/test/video_player/video_player.h"
 #include "media/gpu/test/video_player/video_player_test_environment.h"
-#include "mojo/core/embedder/embedder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
@@ -182,14 +181,6 @@
   testing::InitGoogleTest(&argc, argv);
   base::CommandLine::Init(argc, argv);
 
-  // Using shared memory requires mojo to be initialized (crbug.com/849207).
-  mojo::core::Init();
-
-  // Needed to enable DVLOG through --vmodule.
-  logging::LoggingSettings settings;
-  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
-  LOG_ASSERT(logging::InitLogging(settings));
-
   // Set the default test data path.
   media::test::Video::SetTestDataPath(media::GetTestDataPath());
 
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index f494f45..72af438 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -13,7 +13,6 @@
 #include "media/gpu/test/video_player/video_decoder_client.h"
 #include "media/gpu/test/video_player/video_player.h"
 #include "media/gpu/test/video_player/video_player_test_environment.h"
-#include "mojo/core/embedder/embedder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
@@ -277,14 +276,6 @@
 
   testing::InitGoogleTest(&argc, argv);
 
-  // Using shared memory requires mojo to be initialized (crbug.com/849207).
-  mojo::core::Init();
-
-  // Needed to enable DVLOG through --vmodule.
-  logging::LoggingSettings settings;
-  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
-  LOG_ASSERT(logging::InitLogging(settings));
-
   // Set the default test data path.
   media::test::Video::SetTestDataPath(media::GetTestDataPath());
 
diff --git a/media/test/data/README.md b/media/test/data/README.md
index 6722053..cc0aa16 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -788,6 +788,16 @@
 #### bear\_320x192.yv12.yuv
 First frame of bear\_320x192\_40frames.yv12.yuv for image\_processor_test.
 
+#### bear\_320x192.rgba
+RAW RGBA format data. This data is created from bear\_320x192.i420.yuv by the
+following command. Alpha channel is always 0xFF because of that.
+`ffmpeg -s 320x192 -pix_fmt yuv420p -i bear_320x192.i420.yuv -vcodec rawvideo -f image2 -pix_fmt rgba bear_320x192.rgba`
+
+#### bear\_320x192.bgra
+RAW BGRA format data. This data is created from bear\_320x192.i420.yuv by the
+following command. Alpha channel is always 0xFF because of that.
+`ffmpeg -s 320x192 -pix_fmt yuv420p -i bear_320x192.i420.yuv -vcodec rawvideo -f image2 -pix_fmt rgba bear_320x192.bgra`
+
 ###  VP9 parser test files:
 
 #### bear-vp9.ivf
diff --git a/media/test/data/bear_320x192.bgra b/media/test/data/bear_320x192.bgra
new file mode 100644
index 0000000..e35e345e
--- /dev/null
+++ b/media/test/data/bear_320x192.bgra
Binary files differ
diff --git a/media/test/data/bear_320x192.bgra.json b/media/test/data/bear_320x192.bgra.json
new file mode 100644
index 0000000..cf088ae
--- /dev/null
+++ b/media/test/data/bear_320x192.bgra.json
@@ -0,0 +1,6 @@
+{
+  "pixel_format": "BRGA",
+  "width": 320,
+  "height": 192,
+  "checksum": "edc7e1721d769695d18cd20c71140ebd"
+}
diff --git a/media/test/data/bear_320x192.rgba b/media/test/data/bear_320x192.rgba
new file mode 100644
index 0000000..f0e5293
--- /dev/null
+++ b/media/test/data/bear_320x192.rgba
Binary files differ
diff --git a/media/test/data/bear_320x192.rgba.json b/media/test/data/bear_320x192.rgba.json
new file mode 100644
index 0000000..ddd9e49
--- /dev/null
+++ b/media/test/data/bear_320x192.rgba.json
@@ -0,0 +1,6 @@
+{
+  "pixel_format": "RGBA",
+  "width": 320,
+  "height": 192,
+  "checksum": "b1277dff463d24af3ae4f02bcfa16a32"
+}
diff --git a/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store.cc b/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store.cc
index f3c2169b..a9fe780c 100644
--- a/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store.cc
+++ b/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store.cc
@@ -122,10 +122,12 @@
   void LoadNELPoliciesAndNotifyInBackground(
       NELPoliciesLoadedCallback loaded_callback);
 
-  // Calls |loaded_callback| with the loaded NEL policies in |loaded_policies|.
-  void NotifyNELPoliciesLoaded(
+  // Calls |loaded_callback| with the loaded NEL policies (which may be empty if
+  // loading was unsuccessful). If loading was successful, also report metrics.
+  void CompleteLoadNELPoliciesAndNotifyInForeground(
       NELPoliciesLoadedCallback loaded_callback,
-      std::vector<NetworkErrorLoggingService::NELPolicy> loaded_policies);
+      std::vector<NetworkErrorLoggingService::NELPolicy> loaded_policies,
+      bool load_success);
 
   // Total number of pending operations (may not match the sum of the number of
   // elements in the pending operations queues, due to operation coalescing).
@@ -470,8 +472,15 @@
         NELPoliciesLoadedCallback loaded_callback) {
   DCHECK(background_task_runner()->RunsTasksInCurrentSequence());
 
-  if (!InitializeDatabase())
+  std::vector<NetworkErrorLoggingService::NELPolicy> loaded_policies;
+  if (!InitializeDatabase()) {
+    PostClientTask(
+        FROM_HERE,
+        base::BindOnce(&Backend::CompleteLoadNELPoliciesAndNotifyInForeground,
+                       this, std::move(loaded_callback),
+                       std::move(loaded_policies), false /* load_success */));
     return;
+  }
 
   sql::Statement smt(db()->GetUniqueStatement(
       "SELECT origin_scheme, origin_host, origin_port, received_ip_address,"
@@ -479,10 +488,14 @@
       "is_include_subdomains, last_access_us_since_epoch FROM nel_policies"));
   if (!smt.is_valid()) {
     Reset();
+    PostClientTask(
+        FROM_HERE,
+        base::BindOnce(&Backend::CompleteLoadNELPoliciesAndNotifyInForeground,
+                       this, std::move(loaded_callback),
+                       std::move(loaded_policies), false /* load_success */));
     return;
   }
 
-  std::vector<NetworkErrorLoggingService::NELPolicy> loaded_policies;
   while (smt.Step()) {
     // Reconstitute a NEL policy from the fields stored in the database.
     NetworkErrorLoggingService::NELPolicy policy;
@@ -504,16 +517,26 @@
     loaded_policies.push_back(std::move(policy));
   }
 
-  PostClientTask(FROM_HERE, base::BindOnce(&Backend::NotifyNELPoliciesLoaded,
-                                           this, std::move(loaded_callback),
-                                           std::move(loaded_policies)));
+  PostClientTask(
+      FROM_HERE,
+      base::BindOnce(&Backend::CompleteLoadNELPoliciesAndNotifyInForeground,
+                     this, std::move(loaded_callback),
+                     std::move(loaded_policies), true /* load_success */));
 }
 
-void SQLitePersistentReportingAndNELStore::Backend::NotifyNELPoliciesLoaded(
-    NELPoliciesLoadedCallback loaded_callback,
-    std::vector<NetworkErrorLoggingService::NELPolicy> loaded_policies) {
+void SQLitePersistentReportingAndNELStore::Backend::
+    CompleteLoadNELPoliciesAndNotifyInForeground(
+        NELPoliciesLoadedCallback loaded_callback,
+        std::vector<NetworkErrorLoggingService::NELPolicy> loaded_policies,
+        bool load_success) {
   DCHECK(client_task_runner()->RunsTasksInCurrentSequence());
 
+  if (load_success) {
+    // TODO(chlily): report metrics
+  } else {
+    DCHECK(loaded_policies.empty());
+  }
+
   std::move(loaded_callback).Run(std::move(loaded_policies));
 }
 
diff --git a/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc b/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
index 44a9dcb..c61007d9 100644
--- a/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
+++ b/net/extras/sqlite/sqlite_persistent_reporting_and_nel_store_unittest.cc
@@ -179,6 +179,20 @@
   EXPECT_TRUE(WithinOneMicrosecond(policy.last_used, policies[0].last_used));
 }
 
+TEST_F(SQLitePersistentReportingAndNELStoreTest, LoadNELPoliciesFailed) {
+  // Inject a db initialization failure by creating a directory where the db
+  // file should be.
+  ASSERT_TRUE(base::CreateDirectory(
+      temp_dir_.GetPath().Append(kReportingAndNELStoreFilename)));
+  store_ = std::make_unique<SQLitePersistentReportingAndNELStore>(
+      temp_dir_.GetPath().Append(kReportingAndNELStoreFilename),
+      client_task_runner_, background_task_runner_);
+
+  // InitializeStore() checks that we receive an empty vector of policies,
+  // signifying the failure to load.
+  InitializeStore();
+}
+
 TEST_F(SQLitePersistentReportingAndNELStoreTest, UpdateNELPolicyAccessTime) {
   CreateStore();
   InitializeStore();
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 50a92fc..a2cde27e 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -11416,7 +11416,13 @@
     : public HTTPSOCSPTest,
       public testing::WithParamInterface<OCSPVerifyTestData> {};
 
-TEST_P(HTTPSOCSPVerifyTest, VerifyResult) {
+// TODO(crbug.com/949958): The test is flaky on Mac
+#if defined(OS_MACOSX)
+#define MAYBE_VerifyResult DISABLED_VerifyResult
+#else
+#define MAYBE_VerifyResult VerifyResult
+#endif
+TEST_P(HTTPSOCSPVerifyTest, MAYBE_VerifyResult) {
   SpawnedTestServer::SSLOptions ssl_options(
       SpawnedTestServer::SSLOptions::CERT_AUTO);
   OCSPVerifyTestData test = GetParam();
diff --git a/remoting/signaling/signaling_address.cc b/remoting/signaling/signaling_address.cc
index 3f4102e..1165610 100644
--- a/remoting/signaling/signaling_address.cc
+++ b/remoting/signaling/signaling_address.cc
@@ -4,9 +4,11 @@
 
 #include "remoting/signaling/signaling_address.h"
 
-#include "base/base64.h"
+#include <string.h>
+
 #include "base/logging.h"
 #include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
 #include "remoting/base/name_value_map.h"
 #include "remoting/base/remoting_bot.h"
 #include "remoting/base/service_urls.h"
@@ -17,7 +19,13 @@
 
 namespace {
 
-const char kJingleNamespace[] = "urn:xmpp:jingle:1";
+constexpr char kJingleNamespace[] = "urn:xmpp:jingle:1";
+constexpr char kLcsResourcePrefix[] = "chromoting_lcs_";
+
+// FTL JID format:
+// user@domain.com/chromoting_ftl_(registration ID)
+// The FTL JID is only used local to the program.
+constexpr char kFtlResourcePrefix[] = "chromoting_ftl_";
 
 // Represents the XML attrbute names for the various address fields in the
 // iq stanza.
@@ -26,6 +34,7 @@
 const NameMapElement<SignalingAddress::Channel> kChannelTypes[] = {
     {SignalingAddress::Channel::LCS, "lcs"},
     {SignalingAddress::Channel::XMPP, "xmpp"},
+    {SignalingAddress::Channel::FTL, "ftl"},
 };
 
 jingle_xmpp::QName GetQNameByField(Field attr, SignalingAddress::Direction direction) {
@@ -50,9 +59,14 @@
 SignalingAddress::Channel GetChannelType(std::string address) {
   std::string bare_jid;
   std::string resource;
-  if (SplitJidResource(address, &bare_jid, &resource) &&
-      resource.find("chromoting_lcs_") == 0) {
-    return SignalingAddress::Channel::LCS;
+  bool has_jid_resource = SplitJidResource(address, &bare_jid, &resource);
+  if (has_jid_resource) {
+    if (resource.find(kLcsResourcePrefix) == 0) {
+      return SignalingAddress::Channel::LCS;
+    }
+    if (resource.find(kFtlResourcePrefix) == 0) {
+      return SignalingAddress::Channel::FTL;
+    }
   }
   return SignalingAddress::Channel::XMPP;
 }
@@ -72,6 +86,9 @@
       endpoint_id_ = NormalizeJid(address);
       jid_ = remoting::ServiceUrls::GetInstance()->directory_bot_jid();
       break;
+    case SignalingAddress::Channel::FTL:
+      jid_ = NormalizeJid(address);
+      break;
     default:
       NOTREACHED();
   }
@@ -99,6 +116,16 @@
   return !(*this == other);
 }
 
+// static
+SignalingAddress SignalingAddress::CreateFtlSignalingAddress(
+    const std::string& username,
+    const std::string& registration_id) {
+  return SignalingAddress(base::StringPrintf("%s/%s%s", username.c_str(),
+                                             kFtlResourcePrefix,
+                                             registration_id.c_str()));
+}
+
+// static
 SignalingAddress SignalingAddress::Parse(const jingle_xmpp::XmlElement* iq,
                                          SignalingAddress::Direction direction,
                                          std::string* error) {
@@ -110,7 +137,7 @@
   const jingle_xmpp::XmlElement* jingle =
       iq->FirstNamed(jingle_xmpp::QName(kJingleNamespace, "jingle"));
 
-  if (!jingle) {
+  if (!jingle || GetChannelType(jid) == SignalingAddress::Channel::FTL) {
     return SignalingAddress(jid);
   }
 
@@ -189,4 +216,18 @@
   }
 }
 
+bool SignalingAddress::GetFtlInfo(std::string* username,
+                                  std::string* registration_id) const {
+  if (channel_ != Channel::FTL) {
+    return false;
+  }
+  std::string resource;
+  bool has_jid_resource = SplitJidResource(jid_, username, &resource);
+  DCHECK(has_jid_resource);
+  size_t ftl_resource_prefix_length = strlen(kFtlResourcePrefix);
+  DCHECK_LT(ftl_resource_prefix_length, resource.length());
+  *registration_id = resource.substr(ftl_resource_prefix_length);
+  return true;
+}
+
 }  // namespace remoting
diff --git a/remoting/signaling/signaling_address.h b/remoting/signaling/signaling_address.h
index 993802c..1508c14e 100644
--- a/remoting/signaling/signaling_address.h
+++ b/remoting/signaling/signaling_address.h
@@ -16,21 +16,34 @@
 // Represents an address of a Chromoting endpoint and its routing channel.
 class SignalingAddress {
  public:
-  enum class Channel { LCS, XMPP };
+  enum class Channel { LCS, XMPP, FTL };
   enum Direction { TO, FROM };
   // Creates an empty SignalingAddress.
   SignalingAddress();
 
   // Creates a SignalingAddress with |jid|, which can either be a valid
-  // XMPP JID or an LCS address in a JID like format.
+  // XMPP JID, or an LCS address in a JID like format, or FTL address in a JID
+  // like format.
   explicit SignalingAddress(const std::string& jid);
 
+  // Creates a SignalingAddress that represents an FTL endpoint. Note that the
+  // FTL SignalingAddress is irrelevant to the FTL server or client. It is just
+  // to make existing Jingle session logic work with the new messaging service.
+  static SignalingAddress CreateFtlSignalingAddress(
+      const std::string& username,
+      const std::string& registration_id);
+
   static SignalingAddress Parse(const jingle_xmpp::XmlElement* iq,
                                 Direction direction,
                                 std::string* error);
 
   void SetInMessage(jingle_xmpp::XmlElement* message, Direction direction) const;
 
+  // Writes FTL info to |username| and |registration_id|. Returns true if the
+  // SIgnalingAddress is a valid FTL address and info is successfully written.
+  // If this returns false then none of the parameters will be touched.
+  bool GetFtlInfo(std::string* username, std::string* registration_id) const;
+
   const std::string& jid() const { return jid_; }
   const std::string& endpoint_id() const { return endpoint_id_; }
   Channel channel() const { return channel_; }
diff --git a/remoting/signaling/signaling_address_unittest.cc b/remoting/signaling/signaling_address_unittest.cc
index 01dea593..bd0b1fec 100644
--- a/remoting/signaling/signaling_address_unittest.cc
+++ b/remoting/signaling/signaling_address_unittest.cc
@@ -25,6 +25,11 @@
     "user@domain.com/chromoting_lcs_KkMKIDB5NldsZndLalZZamZWVTZlYmhPT1RBa2p2TUl"
     "GX0lvEKT-HRhwIhB1V3QxYVkwdUptWlc3bnIxKVYHxmgZQ7i7";
 
+constexpr char kFtlRegistrationId[] = "f6b43f10-566e-11e9-8647-d663bd873d93";
+
+constexpr char kFtlAddress[] =
+    "user@domain.com/chromoting_ftl_f6b43f10-566e-11e9-8647-d663bd873d93";
+
 }  // namespace
 
 TEST(SignalingAddressTest, ParseAddress) {
@@ -168,6 +173,18 @@
   EXPECT_EQ(kLcsAddress, jingle->Attr(QName("", "to-endpoint-id")));
 }
 
+TEST(SignalingAddressTest, SetInMessageToFtl) {
+  std::unique_ptr<jingle_xmpp::XmlElement> message = GetEmptyJingleMessage();
+  SignalingAddress addr(kFtlAddress);
+
+  addr.SetInMessage(message.get(), SignalingAddress::TO);
+  EXPECT_EQ(kFtlAddress, message->Attr(QName("", "to")));
+  jingle_xmpp::XmlElement* jingle =
+      message->FirstNamed(jingle_xmpp::QName("urn:xmpp:jingle:1", "jingle"));
+  EXPECT_EQ("", jingle->Attr(QName("", "to-channel")));
+  EXPECT_EQ("", jingle->Attr(QName("", "to-endpoint-id")));
+}
+
 TEST(SignalingAddressTest, SetInMessageFromXmpp) {
   std::unique_ptr<jingle_xmpp::XmlElement> message = GetEmptyJingleMessage();
   SignalingAddress addr("user@domain.com/resource");
@@ -191,4 +208,37 @@
   EXPECT_EQ(kLcsAddress, jingle->Attr(QName("", "from-endpoint-id")));
 }
 
+TEST(SignalingAddressTest, SetInMessageFromFtl) {
+  std::unique_ptr<jingle_xmpp::XmlElement> message = GetEmptyJingleMessage();
+  SignalingAddress addr(kFtlAddress);
+  addr.SetInMessage(message.get(), SignalingAddress::FROM);
+  EXPECT_EQ(kFtlAddress, message->Attr(QName("", "from")));
+  jingle_xmpp::XmlElement* jingle =
+      message->FirstNamed(jingle_xmpp::QName("urn:xmpp:jingle:1", "jingle"));
+  EXPECT_EQ("", jingle->Attr(QName("", "from-channel")));
+  EXPECT_EQ("", jingle->Attr(QName("", "from-endpoint-id")));
+}
+
+TEST(SignalingAddressTest, CreateFtlSignalingAddress) {
+  SignalingAddress addr = SignalingAddress::CreateFtlSignalingAddress(
+      "user@domain.com", kFtlRegistrationId);
+  EXPECT_EQ(kFtlAddress, addr.jid());
+
+  std::string username;
+  std::string registration_id;
+  EXPECT_TRUE(addr.GetFtlInfo(&username, &registration_id));
+  EXPECT_EQ("user@domain.com", username);
+  EXPECT_EQ(kFtlRegistrationId, registration_id);
+}
+
+TEST(SignalingAddressTest, GetFtlInfo_NotFtlInfo) {
+  SignalingAddress addr(kLcsAddress);
+
+  std::string username;
+  std::string registration_id;
+  EXPECT_FALSE(addr.GetFtlInfo(&username, &registration_id));
+  EXPECT_TRUE(username.empty());
+  EXPECT_TRUE(registration_id.empty());
+}
+
 }  // namespace remoting
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc
index a402097..ad20c7df3 100644
--- a/services/identity/public/cpp/identity_manager.cc
+++ b/services/identity/public/cpp/identity_manager.cc
@@ -467,11 +467,6 @@
   }
 }
 
-void IdentityManager::GoogleSigninFailed(const GoogleServiceAuthError& error) {
-  for (auto& observer : observer_list_)
-    observer.OnPrimaryAccountSigninFailed(error);
-}
-
 void IdentityManager::OnRefreshTokenAvailable(const std::string& account_id) {
   CoreAccountInfo account_info =
       GetAccountInfoForAccountWithRefreshToken(account_id);
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h
index 530095e..5b54669 100644
--- a/services/identity/public/cpp/identity_manager.h
+++ b/services/identity/public/cpp/identity_manager.h
@@ -76,11 +76,6 @@
     virtual void OnPrimaryAccountCleared(
         const CoreAccountInfo& previous_primary_account_info) {}
 
-    // Called when the user attempts but fails to set their primary
-    // account. |error| gives the reason for the failure.
-    virtual void OnPrimaryAccountSigninFailed(
-        const GoogleServiceAuthError& error) {}
-
     // Called when a new refresh token is associated with |account_info|.
     // NOTE: On a signin event, the ordering of this callback wrt the
     // OnPrimaryAccountSet() callback is undefined. If you as a client are
@@ -607,7 +602,6 @@
   // SigninManagerBase::Observer:
   void GoogleSigninSucceeded(const AccountInfo& account_info) override;
   void GoogleSignedOut(const AccountInfo& account_info) override;
-  void GoogleSigninFailed(const GoogleServiceAuthError& error) override;
 
   // OAuth2TokenService::Observer:
   void OnRefreshTokenAvailable(const std::string& account_id) override;
diff --git a/services/identity/public/cpp/primary_account_mutator_unittest.cc b/services/identity/public/cpp/primary_account_mutator_unittest.cc
index b5082b1..6610d1d3 100644
--- a/services/identity/public/cpp/primary_account_mutator_unittest.cc
+++ b/services/identity/public/cpp/primary_account_mutator_unittest.cc
@@ -41,12 +41,6 @@
     base::RepeatingCallback<void(const CoreAccountInfo&)>;
 
 // This callback will be invoked every time the IdentityManager::Observer
-// method OnPrimaryAccountSigninFailed is invoked. The parameter will be
-// a reference to the authentication error.
-using PrimaryAccountSigninFailedCallback =
-    base::RepeatingCallback<void(const GoogleServiceAuthError&)>;
-
-// This callback will be invoked every time the IdentityManager::Observer
 // method OnRefreshTokenRemoved is invoked. The parameter will be a reference
 // to the account_id whose token was removed.
 using RefreshTokenRemovedCallback =
@@ -60,15 +54,11 @@
   ClearPrimaryAccountTestObserver(
       identity::IdentityManager* identity_manager,
       PrimaryAccountClearedCallback on_primary_account_cleared,
-      PrimaryAccountSigninFailedCallback on_primary_account_signin_failed,
       RefreshTokenRemovedCallback on_refresh_token_removed)
       : on_primary_account_cleared_(std::move(on_primary_account_cleared)),
-        on_primary_account_signin_failed_(
-            std::move(on_primary_account_signin_failed)),
         on_refresh_token_removed_(std::move(on_refresh_token_removed)),
         scoped_observer_(this) {
     DCHECK(on_primary_account_cleared_);
-    DCHECK(on_primary_account_signin_failed_);
     DCHECK(on_refresh_token_removed_);
     scoped_observer_.Add(identity_manager);
   }
@@ -78,18 +68,12 @@
     on_primary_account_cleared_.Run(account_info);
   }
 
-  void OnPrimaryAccountSigninFailed(
-      const GoogleServiceAuthError& error) override {
-    on_primary_account_signin_failed_.Run(error);
-  }
-
   void OnRefreshTokenRemovedForAccount(const std::string& account_id) override {
     on_refresh_token_removed_.Run(account_id);
   }
 
  private:
   PrimaryAccountClearedCallback on_primary_account_cleared_;
-  PrimaryAccountSigninFailedCallback on_primary_account_signin_failed_;
   RefreshTokenRemovedCallback on_refresh_token_removed_;
   ScopedObserver<identity::IdentityManager, identity::IdentityManager::Observer>
       scoped_observer_;
@@ -165,12 +149,6 @@
                              const CoreAccountInfo&) { quit_closure.Run(); },
                           run_loop.QuitClosure());
 
-  // Authentication error should not occur.
-  PrimaryAccountSigninFailedCallback primary_account_signin_failed_callback =
-      base::BindRepeating([](const GoogleServiceAuthError&) {
-        FAIL() << "auth should not fail";
-      });
-
   // Track Observer token removal notification.
   base::flat_set<std::string> observed_removals;
   RefreshTokenRemovedCallback refresh_token_removed_callback =
@@ -183,7 +161,7 @@
 
   ClearPrimaryAccountTestObserver scoped_observer(
       identity_manager, primary_account_cleared_callback,
-      primary_account_signin_failed_callback, refresh_token_removed_callback);
+      refresh_token_removed_callback);
 
   primary_account_mutator->ClearPrimaryAccount(
       account_action, signin_metrics::SIGNOUT_TEST,
diff --git a/services/identity/public/cpp/test_identity_manager_observer.cc b/services/identity/public/cpp/test_identity_manager_observer.cc
index 57d63a29..9c2f9a6 100644
--- a/services/identity/public/cpp/test_identity_manager_observer.cc
+++ b/services/identity/public/cpp/test_identity_manager_observer.cc
@@ -39,16 +39,6 @@
   return primary_account_from_cleared_callback_;
 }
 
-void TestIdentityManagerObserver::SetOnPrimaryAccountSigninFailedCallback(
-    base::OnceClosure callback) {
-  on_primary_account_signin_failed_callback_ = std::move(callback);
-}
-
-const GoogleServiceAuthError&
-TestIdentityManagerObserver::ErrorFromSigninFailedCallback() const {
-  return google_signin_failed_error_;
-}
-
 void TestIdentityManagerObserver::SetOnRefreshTokenUpdatedCallback(
     base::OnceClosure callback) {
   on_refresh_token_updated_callback_ = std::move(callback);
@@ -146,13 +136,6 @@
     std::move(on_primary_account_cleared_callback_).Run();
 }
 
-void TestIdentityManagerObserver::OnPrimaryAccountSigninFailed(
-    const GoogleServiceAuthError& error) {
-  google_signin_failed_error_ = error;
-  if (on_primary_account_signin_failed_callback_)
-    std::move(on_primary_account_signin_failed_callback_).Run();
-}
-
 void TestIdentityManagerObserver::OnRefreshTokenUpdatedForAccount(
     const CoreAccountInfo& account_info) {
   if (!is_inside_batch_)
diff --git a/services/identity/public/cpp/test_identity_manager_observer.h b/services/identity/public/cpp/test_identity_manager_observer.h
index 91cc2e6..91b6f8b 100644
--- a/services/identity/public/cpp/test_identity_manager_observer.h
+++ b/services/identity/public/cpp/test_identity_manager_observer.h
@@ -30,9 +30,6 @@
   void SetOnPrimaryAccountClearedCallback(base::OnceClosure callback);
   const CoreAccountInfo& PrimaryAccountFromClearedCallback();
 
-  void SetOnPrimaryAccountSigninFailedCallback(base::OnceClosure callback);
-  const GoogleServiceAuthError& ErrorFromSigninFailedCallback() const;
-
   void SetOnRefreshTokenUpdatedCallback(base::OnceClosure callback);
   const CoreAccountInfo& AccountFromRefreshTokenUpdatedCallback();
 
@@ -68,9 +65,6 @@
       const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const CoreAccountInfo& previous_primary_account_info) override;
-  void OnPrimaryAccountSigninFailed(
-      const GoogleServiceAuthError& error) override;
-
   void OnRefreshTokenUpdatedForAccount(
       const CoreAccountInfo& account_info) override;
   void OnRefreshTokenRemovedForAccount(const std::string& account_id) override;
@@ -98,9 +92,6 @@
   base::OnceClosure on_primary_account_cleared_callback_;
   CoreAccountInfo primary_account_from_cleared_callback_;
 
-  base::OnceClosure on_primary_account_signin_failed_callback_;
-  GoogleServiceAuthError google_signin_failed_error_;
-
   base::OnceClosure on_refresh_token_updated_callback_;
   CoreAccountInfo account_from_refresh_token_updated_callback_;
 
diff --git a/services/identity/public/objc/identity_manager_observer_bridge.h b/services/identity/public/objc/identity_manager_observer_bridge.h
index 2e7e7b4..4e5e3b0d 100644
--- a/services/identity/public/objc/identity_manager_observer_bridge.h
+++ b/services/identity/public/objc/identity_manager_observer_bridge.h
@@ -25,7 +25,6 @@
 - (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo;
 - (void)onPrimaryAccountCleared:
     (const CoreAccountInfo&)previousPrimaryAccountInfo;
-- (void)onPrimaryAccountSigninFailed:(const GoogleServiceAuthError&)error;
 - (void)onRefreshTokenUpdatedForAccount:(const CoreAccountInfo&)accountInfo;
 - (void)onRefreshTokenRemovedForAccount:(const std::string&)accountId;
 - (void)onRefreshTokensLoaded;
@@ -52,8 +51,6 @@
       const CoreAccountInfo& primary_account_info) override;
   void OnPrimaryAccountCleared(
       const CoreAccountInfo& previous_primary_account_info) override;
-  void OnPrimaryAccountSigninFailed(
-      const GoogleServiceAuthError& error) override;
   void OnRefreshTokenUpdatedForAccount(
       const CoreAccountInfo& account_info) override;
   void OnRefreshTokenRemovedForAccount(const std::string& account_id) override;
diff --git a/services/identity/public/objc/identity_manager_observer_bridge.mm b/services/identity/public/objc/identity_manager_observer_bridge.mm
index aa8aa3149..59aaea8d 100644
--- a/services/identity/public/objc/identity_manager_observer_bridge.mm
+++ b/services/identity/public/objc/identity_manager_observer_bridge.mm
@@ -35,13 +35,6 @@
   }
 }
 
-void IdentityManagerObserverBridge::OnPrimaryAccountSigninFailed(
-    const GoogleServiceAuthError& error) {
-  if ([delegate_ respondsToSelector:@selector(onPrimaryAccountSigninFailed:)]) {
-    [delegate_ onPrimaryAccountSigninFailed:error];
-  }
-}
-
 void IdentityManagerObserverBridge::OnRefreshTokenUpdatedForAccount(
     const CoreAccountInfo& account_info) {
   if ([delegate_
diff --git a/services/metrics/ukm_api.md b/services/metrics/ukm_api.md
index a50adbe..dbcb414 100644
--- a/services/metrics/ukm_api.md
+++ b/services/metrics/ukm_api.md
@@ -80,6 +80,7 @@
 
 *   `profile.country`
 *   `profile.form_factor`
+*   `profile.system_ram`
 
 ## Enumeration Proportions
 
diff --git a/services/network/p2p/socket_tcp.cc b/services/network/p2p/socket_tcp.cc
index 0895b68..89a5ac7 100644
--- a/services/network/p2p/socket_tcp.cc
+++ b/services/network/p2p/socket_tcp.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/sys_byteorder.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "jingle/glue/fake_ssl_client_socket.h"
 #include "net/base/io_buffer.h"
 #include "net/base/net_errors.h"
@@ -235,8 +236,9 @@
     }
   }
 
-  client_->DataReceived(remote_address_.ip_address, data,
-                        base::TimeTicks::Now());
+  client_->DataReceived(
+      remote_address_.ip_address, data,
+      base::TimeTicks() + base::TimeDelta::FromNanoseconds(rtc::TimeNanos()));
 
   delegate_->DumpPacket(
       base::make_span(reinterpret_cast<const uint8_t*>(&data[0]), data.size()),
@@ -296,9 +298,9 @@
 
   write_buffer_.buffer->DidConsume(result);
   if (write_buffer_.buffer->BytesRemaining() == 0) {
-    base::TimeTicks send_time = base::TimeTicks::Now();
+    int64_t send_time_ms = rtc::TimeMillis();
     client_->SendComplete(
-        P2PSendPacketMetrics(0, write_buffer_.rtc_packet_id, send_time));
+        P2PSendPacketMetrics(0, write_buffer_.rtc_packet_id, send_time_ms));
     if (write_queue_.empty()) {
       write_buffer_.buffer = nullptr;
       write_buffer_.rtc_packet_id = -1;
@@ -443,8 +445,7 @@
       reinterpret_cast<uint8_t*>(send_buffer.buffer->data()) +
           kPacketHeaderSize,
       send_buffer.buffer->BytesRemaining() - kPacketHeaderSize,
-      options.packet_time_params,
-      (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds());
+      options.packet_time_params, rtc::TimeMicros());
 
   WriteOrQueue(send_buffer);
 }
@@ -523,8 +524,7 @@
 
   cricket::ApplyPacketOptions(
       reinterpret_cast<uint8_t*>(send_buffer.buffer->data()), data.size(),
-      options.packet_time_params,
-      (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds());
+      options.packet_time_params, rtc::TimeMicros());
 
   if (pad_bytes) {
     char padding[4] = {0};
diff --git a/services/network/p2p/socket_tcp_unittest.cc b/services/network/p2p/socket_tcp_unittest.cc
index 6db0811..714be9c 100644
--- a/services/network/p2p/socket_tcp_unittest.cc
+++ b/services/network/p2p/socket_tcp_unittest.cc
@@ -276,7 +276,7 @@
 
   const int32_t kRtcPacketId = 1234;
 
-  base::TimeTicks now = base::TimeTicks::Now();
+  int64_t now = rtc::TimeMillis();
 
   EXPECT_CALL(*fake_client_.get(),
               SendComplete(MatchSendPacketMetrics(kRtcPacketId, now)))
diff --git a/services/network/p2p/socket_test_utils.h b/services/network/p2p/socket_test_utils.h
index 8683e10..9315c3c 100644
--- a/services/network/p2p/socket_test_utils.h
+++ b/services/network/p2p/socket_test_utils.h
@@ -159,8 +159,8 @@
 
 MATCHER_P2(MatchSendPacketMetrics, rtc_packet_id, test_start_time, "") {
   return arg.rtc_packet_id == rtc_packet_id &&
-         arg.send_time >= test_start_time &&
-         arg.send_time <= base::TimeTicks::Now();
+         arg.send_time_ms >= test_start_time &&
+         arg.send_time_ms <= rtc::TimeMillis();
 }
 
 }  // namespace network
diff --git a/services/network/p2p/socket_udp.cc b/services/network/p2p/socket_udp.cc
index fdaaf92..bb11a9a 100644
--- a/services/network/p2p/socket_udp.cc
+++ b/services/network/p2p/socket_udp.cc
@@ -9,6 +9,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "net/base/io_buffer.h"
@@ -222,7 +223,9 @@
       }
     }
 
-    client_->DataReceived(recv_address_, data, base::TimeTicks::Now());
+    client_->DataReceived(
+        recv_address_, data,
+        base::TimeTicks() + base::TimeDelta::FromNanoseconds(rtc::TimeNanos()));
 
     delegate_->DumpPacket(
         base::make_span(reinterpret_cast<uint8_t*>(&data[0]), data.size()),
@@ -237,7 +240,7 @@
 }
 
 bool P2PSocketUdp::DoSend(const PendingPacket& packet) {
-  base::TimeTicks send_time = base::TimeTicks::Now();
+  int64_t send_time_us = rtc::TimeMicros();
 
   // The peer is considered not connected until the first incoming STUN
   // request/response. In that state the renderer is allowed to send only STUN
@@ -262,7 +265,7 @@
       // and in the same order it generates them, so we need to respond even
       // when the packet is dropped.
       client_->SendComplete(P2PSendPacketMetrics(
-          packet.id, packet.packet_options.packet_id, send_time));
+          packet.id, packet.packet_options.packet_id, send_time_us / 1000));
       // Do not reset the socket.
       return true;
     }
@@ -289,13 +292,12 @@
     }
   }
 
-  cricket::ApplyPacketOptions(reinterpret_cast<uint8_t*>(packet.data->data()),
-                              packet.size,
-                              packet.packet_options.packet_time_params,
-                              (send_time - base::TimeTicks()).InMicroseconds());
+  cricket::ApplyPacketOptions(
+      reinterpret_cast<uint8_t*>(packet.data->data()), packet.size,
+      packet.packet_options.packet_time_params, send_time_us);
   auto callback_binding =
       base::Bind(&P2PSocketUdp::OnSend, base::Unretained(this), packet.id,
-                 packet.packet_options.packet_id, send_time);
+                 packet.packet_options.packet_id, send_time_us / 1000);
 
   // TODO(crbug.com/656607): Pass traffic annotation after DatagramSocketServer
   // is updated.
@@ -313,8 +315,8 @@
   if (result == net::ERR_IO_PENDING) {
     send_pending_ = true;
   } else {
-    if (!HandleSendResult(packet.id, packet.packet_options.packet_id, send_time,
-                          result)) {
+    if (!HandleSendResult(packet.id, packet.packet_options.packet_id,
+                          send_time_us / 1000, result)) {
       return false;
     }
   }
@@ -329,14 +331,14 @@
 
 void P2PSocketUdp::OnSend(uint64_t packet_id,
                           int32_t transport_sequence_number,
-                          base::TimeTicks send_time,
+                          int64_t send_time_ms,
                           int result) {
   DCHECK(send_pending_);
   DCHECK_NE(result, net::ERR_IO_PENDING);
 
   send_pending_ = false;
 
-  if (!HandleSendResult(packet_id, transport_sequence_number, send_time,
+  if (!HandleSendResult(packet_id, transport_sequence_number, send_time_ms,
                         result)) {
     return;
   }
@@ -353,7 +355,7 @@
 
 bool P2PSocketUdp::HandleSendResult(uint64_t packet_id,
                                     int32_t transport_sequence_number,
-                                    base::TimeTicks send_time,
+                                    int64_t send_time_ms,
                                     int result) {
   TRACE_EVENT_ASYNC_END1("p2p", "Send", packet_id, "result", result);
   if (result < 0) {
@@ -371,11 +373,12 @@
 
   // UMA to track the histograms from 1ms to 1 sec for how long a packet spends
   // in the browser process.
-  UMA_HISTOGRAM_TIMES("WebRTC.SystemSendPacketDuration_UDP" /* name */,
-                      base::TimeTicks::Now() - send_time /* sample */);
+  UMA_HISTOGRAM_TIMES(
+      "WebRTC.SystemSendPacketDuration_UDP" /* name */,
+      base::TimeDelta::FromMilliseconds(rtc::TimeMillis() - send_time_ms));
 
   client_->SendComplete(
-      P2PSendPacketMetrics(packet_id, transport_sequence_number, send_time));
+      P2PSendPacketMetrics(packet_id, transport_sequence_number, send_time_ms));
 
   return true;
 }
diff --git a/services/network/p2p/socket_udp.h b/services/network/p2p/socket_udp.h
index 8362a7f..e0d97db 100644
--- a/services/network/p2p/socket_udp.h
+++ b/services/network/p2p/socket_udp.h
@@ -94,13 +94,13 @@
   WARN_UNUSED_RESULT bool HandleReadResult(int result);
   WARN_UNUSED_RESULT bool HandleSendResult(uint64_t packet_id,
                                            int32_t transport_sequence_number,
-                                           base::TimeTicks send_time,
+                                           int64_t send_time_ms,
                                            int result);
   WARN_UNUSED_RESULT bool DoSend(const PendingPacket& packet);
 
   void OnSend(uint64_t packet_id,
               int32_t transport_sequence_number,
-              base::TimeTicks send_time,
+              int64_t send_time_ms,
               int result);
 
   int SetSocketDiffServCodePointInternal(net::DiffServCodePoint dscp);
diff --git a/services/network/public/cpp/p2p_param_traits.h b/services/network/public/cpp/p2p_param_traits.h
index e236d37..9572939 100644
--- a/services/network/public/cpp/p2p_param_traits.h
+++ b/services/network/public/cpp/p2p_param_traits.h
@@ -57,7 +57,7 @@
 IPC_STRUCT_TRAITS_BEGIN(network::P2PSendPacketMetrics)
   IPC_STRUCT_TRAITS_MEMBER(packet_id)
   IPC_STRUCT_TRAITS_MEMBER(rtc_packet_id)
-  IPC_STRUCT_TRAITS_MEMBER(send_time)
+  IPC_STRUCT_TRAITS_MEMBER(send_time_ms)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(network::P2PPortRange)
diff --git a/services/network/public/cpp/p2p_socket_type.h b/services/network/public/cpp/p2p_socket_type.h
index e88d18d0..3aa7fa7 100644
--- a/services/network/public/cpp/p2p_socket_type.h
+++ b/services/network/public/cpp/p2p_socket_type.h
@@ -56,10 +56,10 @@
   P2PSendPacketMetrics() {}
   P2PSendPacketMetrics(uint64_t packet_id,
                        int32_t rtc_packet_id,
-                       base::TimeTicks send_time)
+                       int64_t send_time_ms)
       : packet_id(packet_id),
         rtc_packet_id(rtc_packet_id),
-        send_time(send_time) {}
+        send_time_ms(send_time_ms) {}
 
   uint64_t packet_id = 0;
   // rtc_packet_id is a sequential packet counter written in the RTP header and
@@ -67,7 +67,10 @@
   // corresponding send time to WebRTC in the browser process so that it can be
   // combined with ACKs to compute inter-packet delay variations.
   int32_t rtc_packet_id = -1;
-  base::TimeTicks send_time;
+
+  // The time the packet was sent. Should be set using the webrtc clock
+  // rtc::TimeMillis()
+  int64_t send_time_ms = -1;
 };
 
 // Struct that carries a port range.
diff --git a/services/network/throttling/throttling_controller.cc b/services/network/throttling/throttling_controller.cc
index cc1d68ed..06176d94 100644
--- a/services/network/throttling/throttling_controller.cc
+++ b/services/network/throttling/throttling_controller.cc
@@ -29,14 +29,6 @@
 }
 
 // static
-base::Optional<base::UnguessableToken>
-ThrottlingController::GetProfileIDForNetLogSource(uint32_t net_log_source_id) {
-  if (!instance_)
-    return base::nullopt;
-  return instance_->GetProfileID(net_log_source_id);
-}
-
-// static
 ThrottlingNetworkInterceptor* ThrottlingController::GetInterceptor(
     uint32_t net_log_source_id) {
   if (!instance_)
diff --git a/services/network/throttling/throttling_controller.h b/services/network/throttling/throttling_controller.h
index b058a23c..f304242 100644
--- a/services/network/throttling/throttling_controller.h
+++ b/services/network/throttling/throttling_controller.h
@@ -29,16 +29,6 @@
   static void SetConditions(const base::UnguessableToken& throttling_profile_id,
                             std::unique_ptr<NetworkConditions>);
 
-  // Returns the profile ID for the NetLog source ID. Returns an empty string if
-  // not registered.
-  // Note: This method is used only from ServiceWorkerFetchDispatcher to copy
-  // the profile ID from the net::URLRequest of original navigation request to
-  // the network::ResourceRequest of navigation preload request when
-  // S13nServiceWorker is not enabled.
-  // TODO(crbug/846235): Remove this method once S13nServiceWorker is shipped.
-  static base::Optional<base::UnguessableToken> GetProfileIDForNetLogSource(
-      uint32_t net_log_source_id);
-
   // Returns the interceptor for the NetLog source ID.
   static ThrottlingNetworkInterceptor* GetInterceptor(
       uint32_t net_log_source_id);
diff --git a/services/ws/window_service_unittest.cc b/services/ws/window_service_unittest.cc
index 1928508b..b20c9f7 100644
--- a/services/ws/window_service_unittest.cc
+++ b/services/ws/window_service_unittest.cc
@@ -120,6 +120,15 @@
   }
 
  private:
+  // ime::mojom::ImeEngineClient:
+  void CommitText(const std::string& text) override {}
+  void UpdateCompositionText(const ui::CompositionText& composition,
+                             uint32_t cursor_pos,
+                             bool visible) override {}
+  void DeleteSurroundingText(int32_t offset, uint32_t length) override {}
+  void SendKeyEvent(std::unique_ptr<ui::Event> key_event) override {}
+  void Reconnect() override {}
+
   mojo::Binding<ime::mojom::ImeEngineClient> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(TestImeEngineClient);
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index c001e32..213b240 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -10576,6 +10576,196 @@
       }
     ]
   },
+  "Linux FYI SkiaRenderer Vulkan (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "angle_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0",
+          "--no-xvfb"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "angle_unittests"
+      },
+      {
+        "args": [
+          "--test-launcher-retry-limit=0"
+        ],
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "angle_white_box_tests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=CastStreamingApiTestWithPixelOutput.EndToEnd*:TabCaptureApiPixelTest.EndToEnd*",
+          "--no-xvfb"
+        ],
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "browser_tests"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/vulkan.content_browsertests.filter",
+          "--enable-features=VizDisplayCompositor,UseSkiaRenderer,UiGpuRasterization",
+          "--use-gl=any",
+          "--enable-oop-rasterization",
+          "--enable-vulkan",
+          "--enable-gpu-rasterization",
+          "--enable-raster-to-sk-image",
+          "--force-gpu-rasterization",
+          "--disable-software-compositing-fallback",
+          "--no-xvfb"
+        ],
+        "name": "vulkan_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--test-launcher-retry-limit=0"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ],
+          "shards": 4
+        },
+        "test": "dawn_end2end_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--use-cmd-decoder=validating"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_tests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--no-xvfb"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gl_unittests"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests"
+        ],
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "gles2_conform_test"
+      },
+      {
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "gpu": "nvidia-quadro-p400-ubuntu-stable",
+              "os": "Ubuntu",
+              "pool": "Chrome-GPU"
+            }
+          ]
+        },
+        "test": "swiftshader_unittests"
+      }
+    ]
+  },
   "Linux FYI dEQP Release (Intel HD 630)": {
     "gtest_tests": [
       {
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index bbec5e6..e6e2ec3 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2537,6 +2537,16 @@
           'gpu_telemetry_tests': 'gpu_fyi_linux_intel_and_nvidia_release_telemetry_tests',
         },
       },
+     'Linux FYI SkiaRenderer Vulkan (NVIDIA)': {
+        'os_type': 'linux',
+        'browser_config': 'release',
+        'mixins': [
+          'linux_nvidia_quadro_p400',
+        ],
+        'test_suites': {
+          'gtest_tests': 'gpu_fyi_linux_release_gtests',
+        },
+      },
       'Linux FYI dEQP Release (Intel HD 630)': {
         'os_type': 'linux',
         'browser_config': 'release',
diff --git a/testing/libfuzzer/fuzzers/BUILD.gn b/testing/libfuzzer/fuzzers/BUILD.gn
index 71447aef..88e5804e4 100644
--- a/testing/libfuzzer/fuzzers/BUILD.gn
+++ b/testing/libfuzzer/fuzzers/BUILD.gn
@@ -401,7 +401,7 @@
     "$target_gen_dir/javascript_parser.proto",
   ]
   proto_deps = [ ":gen_javascript_parser_proto" ]
-  proto_out_dir = ""
+  proto_out_dir = target_gen_dir
 }
 
 fuzzer_test("javascript_parser_proto_fuzzer") {
diff --git a/testing/libfuzzer/fuzzers/javascript_parser_proto_fuzzer.cc b/testing/libfuzzer/fuzzers/javascript_parser_proto_fuzzer.cc
index e38f88e..56f39a0 100644
--- a/testing/libfuzzer/fuzzers/javascript_parser_proto_fuzzer.cc
+++ b/testing/libfuzzer/fuzzers/javascript_parser_proto_fuzzer.cc
@@ -6,7 +6,7 @@
 
 #include <iostream>
 
-#include "javascript_parser.pb.h"  // from out/gen
+#include "testing/libfuzzer/fuzzers/javascript_parser.pb.h"  // from out/gen
 #include "testing/libfuzzer/fuzzers/javascript_parser_proto_to_string.h"
 #include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
 
diff --git a/testing/libfuzzer/fuzzers/javascript_parser_proto_to_string.h b/testing/libfuzzer/fuzzers/javascript_parser_proto_to_string.h
index 285ed1a..d9e5a4c 100644
--- a/testing/libfuzzer/fuzzers/javascript_parser_proto_to_string.h
+++ b/testing/libfuzzer/fuzzers/javascript_parser_proto_to_string.h
@@ -5,7 +5,7 @@
 #ifndef TESTING_LIBFUZZER_FUZZERS_JAVASCRIPT_PARSER_PROTO_TO_STRING_H
 #define TESTING_LIBFUZZER_FUZZERS_JAVASCRIPT_PARSER_PROTO_TO_STRING_H
 
-#include "javascript_parser.pb.h"  // from out/gen
+#include "testing/libfuzzer/fuzzers/javascript_parser.pb.h"  // from out/gen
 
 #include <string>
 
diff --git a/testing/libfuzzer/proto/BUILD.gn b/testing/libfuzzer/proto/BUILD.gn
index 47747e3..40d3a9a 100644
--- a/testing/libfuzzer/proto/BUILD.gn
+++ b/testing/libfuzzer/proto/BUILD.gn
@@ -8,10 +8,6 @@
   sources = [
     "json.proto",
   ]
-
-  # This way json.pb.h header goes into "$root_gen_dir" directory precisely,
-  # otherwise it goes into "$root_gen_dir" + "/testing/libfuzzer/proto/".
-  proto_out_dir = ""
 }
 
 source_set("json_proto_converter") {
@@ -19,6 +15,7 @@
     "json_proto_converter.cc",
     "json_proto_converter.h",
   ]
+  include_dirs = [ target_gen_dir ]
   deps = [
     ":json_proto",
   ]
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 0b52366d..d28ea53 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -5832,10 +5832,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Default",
-                    "enable_features": [
-                        "WebRTC-Aec3FilterAnalyzerIncrementalAnalysisKillSwitch"
-                    ]
+                    "name": "SwitchEnabled"
                 }
             ]
         }
diff --git a/third_party/blink/public/mojom/service_worker/controller_service_worker.mojom b/third_party/blink/public/mojom/service_worker/controller_service_worker.mojom
index faeab18..90c777c 100644
--- a/third_party/blink/public/mojom/service_worker/controller_service_worker.mojom
+++ b/third_party/blink/public/mojom/service_worker/controller_service_worker.mojom
@@ -14,7 +14,6 @@
 import "third_party/blink/public/mojom/service_worker/service_worker_object.mojom";
 import "third_party/blink/public/mojom/web_feature/web_feature.mojom";
 
-// S13nServiceWorker:
 // Represents a service worker that is a 'controller'.
 // (https://w3c.github.io/ServiceWorker/#navigator-service-worker-controller)
 // One of its Mojo end points (i.e. the caller end) is passed to the
@@ -58,11 +57,9 @@
   blink.mojom.ControllerServiceWorkerMode mode =
       blink.mojom.ControllerServiceWorkerMode.kNoController;
 
-  // S13nServiceWorker only:
   // Non-null iff there is a controller and it has a fetch event handler.
   ControllerServiceWorker? endpoint;
 
-  // S13nServiceWorker:
   // The client being controlled, used for FetchEvent#clientId. The ID is
   // issued by the browser process for this receiving client, and would
   // never change thoughout the lifetime of the client.
diff --git a/third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom b/third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom
index 402efab..756800b 100644
--- a/third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom
+++ b/third_party/blink/public/mojom/service_worker/controller_service_worker_mode.mojom
@@ -6,12 +6,6 @@
 
 // Describes whether a controller service worker exists and if it has a fetch
 // handler.
-//
-// BEWARE: In non-NetS13nServiceWorker, the renderer often does not know which
-// controller service worker a request will go to, as skipWaiting() may occur
-// in the browser process at the same time the renderer makes a request.
-// Therefore, non-NetS13nServiceWorker should usually treat
-// kNoFetchEventHandler and kControlled as the same.
 enum ControllerServiceWorkerMode {
   // No controller exists.
   kNoController,
diff --git a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
index 43df8684..ac10ca22 100644
--- a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
+++ b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
@@ -31,53 +31,71 @@
   // The id of the service worker being started. This remains fixed even if the
   // worker is stopped and restarted, or even if the browser restarts.
   int64 service_worker_version_id;
+
   // This service worker's registration's scope:
   // https://w3c.github.io/ServiceWorker/#service-worker-registration-scope
   url.mojom.Url scope;
+
   // This service worker's script url:
   // https://w3c.github.io/ServiceWorker/#dom-serviceworker-scripturl
   url.mojom.Url script_url;
+
   // This service worker's script type:
   // https://w3c.github.io/ServiceWorker/#dfn-type
   ScriptType script_type;
+
   // The string used for "user-agent" HTTP header.
   string user_agent;
+
   // The id to talk with the DevTools agent for the worker.
   int32 worker_devtools_agent_route_id;
+
   // Unique token identifying this worker for DevTools.
   mojo_base.mojom.UnguessableToken devtools_worker_token;
+
   // When true, worker script evaluation is blocked until
   // EmbeddedWorkerInstanceClient::ResumeAfterDownload() is called.
   // This isn't used when off-the-main-thread script fetch is enabled. The
   // browser process is responsible for delaying execution.
   bool pause_after_download;
+
   // True if starting the worker should wait until DevTools gets ready.
   bool wait_for_debugger;
+
   // True if this service worker has been installed.
   bool is_installed;
+
   // Determines how eagerly V8 creates the code cache.
   V8CacheOptions v8_cache_options;
+
   // Used to set up fetch requests.
   RendererPreferences renderer_preferences;
 
   // Used to talk to the service worker from the browser process.
   ServiceWorker& service_worker_request;
-  // S13nServiceWorker: cloned and passed to each controllee to directly
-  // dispatch events from the controllees.
+
+  // Cloned and passed to each controllee to directly dispatch events from the
+  // controllees.
   ControllerServiceWorker& controller_request;
+
   // Information to transfer installed scripts from the browser to the renderer.
   ServiceWorkerInstalledScriptsInfo? installed_scripts_info;
+
   // Interface for the renderer to send the status updates to the browser.
   associated EmbeddedWorkerInstanceHost instance_host;
+
   // Information for creating ServiceWorkerProviderContext on the renderer.
   ServiceWorkerProviderInfoForStartWorker provider_info;
+
   // Interface for the renderer to query the content settings in the browser.
   WorkerContentSettingsProxy content_settings_proxy;
+
   // Interface for keeping track of the renderer preferences.
   RendererPreferenceWatcher& preference_watcher_request;
-  // S13nServiceWorker: Used to load subresources in the service worker.
-  // This allows the service worker to load chrome-extension:// URLs which
-  // the renderer's default loader factory can't load.
+
+  // Used to load subresources in the service worker.  This allows the service
+  // worker to load chrome-extension:// URLs which the renderer's default
+  // loader factory can't load.
   URLLoaderFactoryBundle? subresource_loader_factories;
 };
 
@@ -130,7 +148,6 @@
 // EmbeddedWorkerInstanceClient, so it lives on the same message pipe as
 // EmbeddedWorkerInstanceClient.
 interface EmbeddedWorkerInstanceHost {
-  // S13nServiceWorker:
   // Called when the worker requests to be terminated. The worker will request
   // to be terminated when it realizes it has been idle for some time. The
   // browser doesn't terminate the worker when there are inflight events or
diff --git a/third_party/blink/public/mojom/service_worker/service_worker.mojom b/third_party/blink/public/mojom/service_worker/service_worker.mojom
index ea0fa99f..6bf1896 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker.mojom
@@ -214,9 +214,7 @@
       => (ServiceWorkerEventStatus status);
   // Arguments are passed to the event handler as parameters of SyncEvent.
   // Ref: https://wicg.github.io/BackgroundSync/spec/#sync-event
-  // S13nServiceWorker: |timeout| is the amount of time to allow this event to
-  // finish.
-  // Non-S13nServiceWorker: |timeout| is just ignored.
+  // |timeout| is the amount of time to allow this event to finish.
   DispatchSyncEvent(string id,
                     bool last_chance,
                     mojo_base.mojom.TimeDelta timeout)
@@ -246,7 +244,6 @@
   // worker.
   Ping() => ();
 
-  // S13nServiceWorker:
   // Lets the idle timer request termination immediately after all inflight
   // events are handled without delay.
   SetIdleTimerDelayToZero();
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_container.mojom b/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
index 719910d..3e73835a 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
@@ -73,7 +73,6 @@
   GetRegistrationForReady()
     => (ServiceWorkerRegistrationObjectInfo? registration);
 
-  // S13nServiceWorker:
   // Returns a Mojo end point to the controller ServiceWorker. This may start a
   // service worker instance in a renderer process if the corresponding
   // instance is not alive.
@@ -86,7 +85,6 @@
   EnsureControllerServiceWorker(ControllerServiceWorker& controller,
                                 ControllerServiceWorkerPurpose purpose);
 
-  // S13nServiceWorker:
   // Makes a new endpoint to this ServiceWorkerContainerHost.
   CloneContainerHost(ServiceWorkerContainerHost& container_host);
 
@@ -95,7 +93,6 @@
   // you know all incoming messages up to the Ping() call have been received.
   Ping() => ();
 
-  // S13nServiceWorker:
   // Gives a hint to the browser process to update the service worker after a
   // controlled page load. This message is meant to be sent at a time when page
   // load is no longer busy, so update doesn't adversely affect performance.
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
index f86e2681..d748a51 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
@@ -18,7 +18,6 @@
 // and browser.
 const string kNavigation_ServiceWorkerSpec = "navigation:service_worker";
 
-// S13nServiceWorker:
 // Sent from the browser process to the renderer. Contains parameters for the
 // WebServiceWorkerNetworkProvider used for starting a web worker (dedicated
 // worker or shared worker).
@@ -36,7 +35,6 @@
   associated ServiceWorkerContainerHost host_ptr_info;
   associated ServiceWorkerContainer& client_request;
 
-  // S13nServiceWorker:
   // The loader to use for loading the worker's main script and
   // importScripts().
   associated network.mojom.URLLoaderFactory? script_loader_factory_ptr_info;
diff --git a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
index ce14a565..d254dfb 100644
--- a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
+++ b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
@@ -39,11 +39,9 @@
       RendererPreferenceWatcher& preference_watcher_request,
       WorkerContentSettingsProxy content_settings,
 
-      // S13nServiceWorker:
       // The info about the service worker host in the browser process that
       // provides support for this shared worker to be a service worker client.
-      // Null when S13nServiceWorker is disabled.
-      ServiceWorkerProviderInfoForWorker? service_worker_provider_info,
+      ServiceWorkerProviderInfoForWorker service_worker_provider_info,
 
       // NetworkService:
       // The ID of the AppCacheHost in the browser process that serves resources
@@ -51,13 +49,12 @@
       // is disabled or AppCache doesn't serve resources for this shared worker.
       int32 appcache_host_id,
 
-      // S13nServiceWorker (non-NetworkService):
+      // Non-NetworkService:
       // The URLLoaderFactory to use to request the shared worker's script
       // (just the main script resource; importScripts() should go through the
       // usual loader or the controller service worker if appropriate).
       //
-      // This is only non-null when S13nServiceWorker is enabled but
-      // NetworkService is disabled.
+      // This is only non-null when NetworkService is disabled.
       //
       // TODO(leonhsl): It doesn't really need to be associated. Make it
       // non-associated and update
@@ -80,9 +77,9 @@
 
       // NetworkService (PlzWorker):
       // Used for setting ServiceWorkerContainer#controller. This is null when
-      // NetworkService is disabled or there're no controller service worker.
+      // NetworkService is disabled or there is no controller service worker.
       //
-      // In S13nServiceWorker, the controller is sent via
+      // In non-NetworkService, the controller is sent via
       // ServiceWorkerContainer.SetController().
       ControllerServiceWorkerInfo? controller_info,
 
diff --git a/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h b/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h
index e2b1d91..4a78b88 100644
--- a/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h
+++ b/third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h
@@ -62,7 +62,6 @@
   // request made.
   virtual void WillSendRequest(WebURLRequest&) = 0;
 
-  // S13nServiceWorker:
   // Returns a URLLoader for loading |request|. May return nullptr to fall back
   // to the default loading behavior.
   virtual std::unique_ptr<WebURLLoader> CreateURLLoader(
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver_test.cc b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver_test.cc
index fc5dee6..0243503 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_promise_resolver_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_promise_resolver_test.cc
@@ -223,15 +223,13 @@
         MakeGarbageCollected<ScriptPromiseResolverKeepAlive>(GetScriptState());
   }
   resolver->KeepAliveWhilePending();
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   ASSERT_TRUE(ScriptPromiseResolverKeepAlive::IsAlive());
 
   resolver->Resolve("hello");
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_FALSE(ScriptPromiseResolverKeepAlive::IsAlive());
 }
 
@@ -244,15 +242,13 @@
         MakeGarbageCollected<ScriptPromiseResolverKeepAlive>(GetScriptState());
   }
   resolver->KeepAliveWhilePending();
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   ASSERT_TRUE(ScriptPromiseResolverKeepAlive::IsAlive());
 
   resolver->Reject("hello");
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_FALSE(ScriptPromiseResolverKeepAlive::IsAlive());
 }
 
@@ -269,17 +265,15 @@
     ScriptForbiddenScope forbidden;
     resolver->Resolve("hello");
 
-    ThreadState::Current()->CollectGarbage(
-        BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-        BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+    ThreadState::Current()->CollectAllGarbageForTesting(
+        BlinkGC::kNoHeapPointersOnStack);
     EXPECT_TRUE(ScriptPromiseResolverKeepAlive::IsAlive());
   }
 
   base::RunLoop().RunUntilIdle();
 
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_FALSE(ScriptPromiseResolverKeepAlive::IsAlive());
 }
 
@@ -292,15 +286,13 @@
         MakeGarbageCollected<ScriptPromiseResolverKeepAlive>(GetScriptState());
   }
   resolver->KeepAliveWhilePending();
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_TRUE(ScriptPromiseResolverKeepAlive::IsAlive());
 
   GetExecutionContext()->NotifyContextDestroyed();
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_FALSE(ScriptPromiseResolverKeepAlive::IsAlive());
 }
 
@@ -313,22 +305,19 @@
         MakeGarbageCollected<ScriptPromiseResolverKeepAlive>(GetScriptState());
   }
   resolver->KeepAliveWhilePending();
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   ASSERT_TRUE(ScriptPromiseResolverKeepAlive::IsAlive());
 
   GetExecutionContext()->SetLifecycleState(mojom::FrameLifecycleState::kFrozen);
   resolver->Resolve("hello");
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_TRUE(ScriptPromiseResolverKeepAlive::IsAlive());
 
   GetExecutionContext()->NotifyContextDestroyed();
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_FALSE(ScriptPromiseResolverKeepAlive::IsAlive());
 }
 
diff --git a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
index ce55ccc..762889f7 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_streamer_test.cc
@@ -527,9 +527,8 @@
   EXPECT_FALSE(resource_client_->Finished());
 
   resource_ = nullptr;
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
 }
 
 // TODO(crbug.com/939054): Tests are disabled due to flakiness caused by being
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
index a9e32990..75fd7fa 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.cc
@@ -350,7 +350,7 @@
           key))
     return nullptr;
 
-  return CryptoKey::Create(key);
+  return MakeGarbageCollected<CryptoKey>(key);
 }
 
 bool V8ScriptValueDeserializerForModules::ReadLandmark(Landmark* landmark) {
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
index 3155d64..5548998 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules_test.cc
@@ -290,7 +290,7 @@
 template <typename T>
 WebCryptoResult ToWebCryptoResult(ScriptState* script_state,
                                   base::RepeatingCallback<void(T)> function) {
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   result->Promise().Then(
       (MakeGarbageCollected<WebCryptoResultAdapter<T>>(script_state,
                                                        std::move(function)))
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_input.h b/third_party/blink/renderer/core/css/cssom/paint_worklet_input.h
index a3d3f04..645f4d52 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_input.h
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_input.h
@@ -48,11 +48,11 @@
   gfx::SizeF GetSize() const override {
     return gfx::SizeF(container_size_.Width(), container_size_.Height());
   }
+  int WorkletId() const override { return worklet_id_; }
 
   // These accessors are safe on any thread.
   const FloatSize& ContainerSize() const { return container_size_; }
   float EffectiveZoom() const { return effective_zoom_; }
-  int WorkletId() const { return worklet_id_; }
 
   // These should only be accessed on the PaintWorklet thread.
   String NameCopy() const { return name_.IsolatedCopy(); }
diff --git a/third_party/blink/renderer/core/dom/dom_node_ids_test.cc b/third_party/blink/renderer/core/dom/dom_node_ids_test.cc
index d8038d99..7449a51 100644
--- a/third_party/blink/renderer/core/dom/dom_node_ids_test.cc
+++ b/third_party/blink/renderer/core/dom/dom_node_ids_test.cc
@@ -39,9 +39,8 @@
   DOMNodeId id_a = DOMNodeIds::IdForNode(a);
 
   a->remove();
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_EQ(nullptr, DOMNodeIds::NodeForId(id_a));
 }
 
diff --git a/third_party/blink/renderer/core/dom/mutation_observer_test.cc b/third_party/blink/renderer/core/dom/mutation_observer_test.cc
index 63598f40..916625bf 100644
--- a/third_party/blink/renderer/core/dom/mutation_observer_test.cc
+++ b/third_party/blink/renderer/core/dom/mutation_observer_test.cc
@@ -51,9 +51,8 @@
       observer->registrations_.begin()->Get();
   // The following GC will collect |head|, but won't collect a
   // MutationObserverRegistration for |head|.
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   observer->disconnect();
   // The test passes if disconnect() didn't crash.  crbug.com/657613.
 }
diff --git a/third_party/blink/renderer/core/dom/weak_identifier_map_test.cc b/third_party/blink/renderer/core/dom/weak_identifier_map_test.cc
index 1773c2a9..6d712d0 100644
--- a/third_party/blink/renderer/core/dom/weak_identifier_map_test.cc
+++ b/third_party/blink/renderer/core/dom/weak_identifier_map_test.cc
@@ -22,9 +22,8 @@
   void TearDown() override;
 
   void CollectGarbage() {
-    ThreadState::Current()->CollectGarbage(
-        BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-        BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+    ThreadState::Current()->CollectAllGarbageForTesting(
+        BlinkGC::kNoHeapPointersOnStack);
   }
 };
 
diff --git a/third_party/blink/renderer/core/exported/web_heap.cc b/third_party/blink/renderer/core/exported/web_heap.cc
index 642006f..8d9af33 100644
--- a/third_party/blink/renderer/core/exported/web_heap.cc
+++ b/third_party/blink/renderer/core/exported/web_heap.cc
@@ -36,9 +36,8 @@
 namespace blink {
 
 void WebHeap::CollectGarbageForTesting() {
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kHeapPointersOnStack);
 }
 
 void WebHeap::CollectAllGarbageForTesting() {
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index 23f82d77..2418d77 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -206,6 +206,8 @@
 
 void WebSharedWorkerImpl::Connect(MessagePortChannel web_channel) {
   DCHECK(IsMainThread());
+  if (asked_to_terminate_)
+    return;
   // The HTML spec requires to queue a connect event using the DOM manipulation
   // task source.
   // https://html.spec.whatwg.org/C/#shared-workers-and-the-sharedworker-interface
@@ -292,16 +294,11 @@
                         main_script_loader_->Identifier(),
                         main_script_loader_->SourceText());
 
-  // S13nServiceWorker: The browser process is expected to send a
-  // SetController IPC before sending the script response, but there is no
-  // guarantee of the ordering as the messages arrive on different message
-  // pipes. Wait for the SetController IPC to be received before starting the
-  // worker; otherwise fetches from the worker might not go through the
-  // appropriate controller.
-  //
-  // (For non-S13nServiceWorker, we don't need to do this step as the controller
-  // service worker isn't used directly by the renderer, but to minimize code
-  // differences between the flags just do it anyway.)
+  // The browser process is expected to send a SetController IPC before sending
+  // the script response, but there is no guarantee of the ordering as the
+  // messages arrive on different message pipes. Wait for the SetController IPC
+  // to be received before starting the worker; otherwise fetches from the
+  // worker might not go through the appropriate controller.
   client_->WaitForServiceWorkerControllerInfo(
       shadow_page_->DocumentLoader()->GetServiceWorkerNetworkProvider(),
       WTF::Bind(&WebSharedWorkerImpl::ContinueStartWorkerContext,
diff --git a/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc b/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
index 80899ea5..620d420d 100644
--- a/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_data_loader_test.cc
@@ -7,11 +7,16 @@
 #include <memory>
 
 #include "base/stl_util.h"
+#include "mojo/public/cpp/system/data_pipe_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h"
 #include "third_party/blink/renderer/core/fileapi/blob.h"
 #include "third_party/blink/renderer/core/html/forms/form_data.h"
+#include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h"
+#include "third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h"
+#include "third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
 
 namespace blink {
 
@@ -58,7 +63,50 @@
 constexpr size_t kQuickBrownFoxFormDataLength =
     base::size(kQuickBrownFoxFormData) - 1u;
 
-TEST(FetchDataLoaderTest, LoadAsBlob) {
+class FetchDataLoaderTest : public testing::Test {
+ protected:
+  struct PipingClient : public GarbageCollectedFinalized<PipingClient>,
+                        public FetchDataLoader::Client {
+    USING_GARBAGE_COLLECTED_MIXIN(PipingClient);
+
+   public:
+    explicit PipingClient(
+        scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+        : task_runner_(std::move(task_runner)) {}
+
+    void DidFetchDataStartedDataPipe(
+        mojo::ScopedDataPipeConsumerHandle handle) override {
+      DataPipeBytesConsumer::CompletionNotifier* notifier;
+      destination_ = MakeGarbageCollected<DataPipeBytesConsumer>(
+          task_runner_, std::move(handle), &notifier);
+      completion_notifier_ = notifier;
+    }
+    void DidFetchDataLoadedDataPipe() override {
+      completion_notifier_->SignalComplete();
+    }
+    void DidFetchDataLoadFailed() override {
+      completion_notifier_->SignalError(BytesConsumer::Error());
+    }
+    void Abort() override {
+      completion_notifier_->SignalError(BytesConsumer::Error());
+    }
+
+    BytesConsumer* GetDestination() { return destination_; }
+
+    void Trace(Visitor* visitor) override {
+      visitor->Trace(destination_);
+      visitor->Trace(completion_notifier_);
+      FetchDataLoader::Client::Trace(visitor);
+    }
+
+   private:
+    const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+    Member<BytesConsumer> destination_;
+    Member<DataPipeBytesConsumer::CompletionNotifier> completion_notifier_;
+  };
+};
+
+TEST_F(FetchDataLoaderTest, LoadAsBlob) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -107,7 +155,7 @@
   EXPECT_EQ(String("text/test"), blob_data_handle->GetType());
 }
 
-TEST(FetchDataLoaderTest, LoadAsBlobFailed) {
+TEST_F(FetchDataLoaderTest, LoadAsBlobFailed) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -150,7 +198,7 @@
   checkpoint.Call(4);
 }
 
-TEST(FetchDataLoaderTest, LoadAsBlobCancel) {
+TEST_F(FetchDataLoaderTest, LoadAsBlobCancel) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -181,8 +229,8 @@
   checkpoint.Call(3);
 }
 
-TEST(FetchDataLoaderTest,
-     LoadAsBlobViaDrainAsBlobDataHandleWithSameContentType) {
+TEST_F(FetchDataLoaderTest,
+       LoadAsBlobViaDrainAsBlobDataHandleWithSameContentType) {
   auto blob_data = std::make_unique<BlobData>();
   blob_data->AppendBytes(kQuickBrownFox,
                          kQuickBrownFoxLengthWithTerminatingNull);
@@ -223,8 +271,8 @@
   EXPECT_EQ(String("text/test"), blob_data_handle->GetType());
 }
 
-TEST(FetchDataLoaderTest,
-     LoadAsBlobViaDrainAsBlobDataHandleWithDifferentContentType) {
+TEST_F(FetchDataLoaderTest,
+       LoadAsBlobViaDrainAsBlobDataHandleWithDifferentContentType) {
   auto blob_data = std::make_unique<BlobData>();
   blob_data->AppendBytes(kQuickBrownFox,
                          kQuickBrownFoxLengthWithTerminatingNull);
@@ -265,7 +313,7 @@
   EXPECT_EQ(String("text/test"), blob_data_handle->GetType());
 }
 
-TEST(FetchDataLoaderTest, LoadAsArrayBuffer) {
+TEST_F(FetchDataLoaderTest, LoadAsArrayBuffer) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -311,7 +359,7 @@
   EXPECT_STREQ(kQuickBrownFox, static_cast<const char*>(array_buffer->Data()));
 }
 
-TEST(FetchDataLoaderTest, LoadAsArrayBufferFailed) {
+TEST_F(FetchDataLoaderTest, LoadAsArrayBufferFailed) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -350,7 +398,7 @@
   checkpoint.Call(4);
 }
 
-TEST(FetchDataLoaderTest, LoadAsArrayBufferCancel) {
+TEST_F(FetchDataLoaderTest, LoadAsArrayBufferCancel) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -377,7 +425,7 @@
   checkpoint.Call(3);
 }
 
-TEST(FetchDataLoaderTest, LoadAsFormData) {
+TEST_F(FetchDataLoaderTest, LoadAsFormData) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -446,7 +494,7 @@
   EXPECT_EQ(kQuickBrownFox, form_data->Entries()[3]->Value());
 }
 
-TEST(FetchDataLoaderTest, LoadAsFormDataPartialInput) {
+TEST_F(FetchDataLoaderTest, LoadAsFormDataPartialInput) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -485,7 +533,7 @@
   checkpoint.Call(4);
 }
 
-TEST(FetchDataLoaderTest, LoadAsFormDataFailed) {
+TEST_F(FetchDataLoaderTest, LoadAsFormDataFailed) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -524,7 +572,7 @@
   checkpoint.Call(4);
 }
 
-TEST(FetchDataLoaderTest, LoadAsFormDataCancel) {
+TEST_F(FetchDataLoaderTest, LoadAsFormDataCancel) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -551,7 +599,7 @@
   checkpoint.Call(3);
 }
 
-TEST(FetchDataLoaderTest, LoadAsString) {
+TEST_F(FetchDataLoaderTest, LoadAsString) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -590,7 +638,7 @@
   checkpoint.Call(4);
 }
 
-TEST(FetchDataLoaderTest, LoadAsStringWithNullBytes) {
+TEST_F(FetchDataLoaderTest, LoadAsStringWithNullBytes) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -630,7 +678,7 @@
   checkpoint.Call(4);
 }
 
-TEST(FetchDataLoaderTest, LoadAsStringError) {
+TEST_F(FetchDataLoaderTest, LoadAsStringError) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -668,7 +716,7 @@
   checkpoint.Call(4);
 }
 
-TEST(FetchDataLoaderTest, LoadAsStringCancel) {
+TEST_F(FetchDataLoaderTest, LoadAsStringCancel) {
   Checkpoint checkpoint;
   BytesConsumer::Client* client = nullptr;
   auto* consumer = MakeGarbageCollected<MockBytesConsumer>();
@@ -694,6 +742,115 @@
   checkpoint.Call(3);
 }
 
+TEST_F(FetchDataLoaderTest, LoadAsDataPipeWithCopy) {
+  using Command = ReplayingBytesConsumer::Command;
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* src = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  src->Add(Command(Command::Name::kData, "hello, "));
+  src->Add(Command(Command::Name::kDataAndDone, "world"));
+
+  auto* loader = FetchDataLoader::CreateLoaderAsDataPipe(task_runner);
+  auto* client = MakeGarbageCollected<PipingClient>(task_runner);
+  loader->Start(src, client);
+
+  BytesConsumer* dest = client->GetDestination();
+  ASSERT_TRUE(dest);
+
+  auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(dest);
+  auto result = reader->Run(task_runner.get());
+
+  EXPECT_EQ(result.first, BytesConsumer::Result::kDone);
+  EXPECT_EQ(String(result.second.data(), result.second.size()), "hello, world");
+}
+
+TEST_F(FetchDataLoaderTest, LoadAsDataPipeWithCopyFailure) {
+  using Command = ReplayingBytesConsumer::Command;
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* src = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  src->Add(Command(Command::Name::kData, "hello, "));
+  src->Add(Command(Command::Name::kError));
+
+  auto* loader = FetchDataLoader::CreateLoaderAsDataPipe(task_runner);
+  auto* client = MakeGarbageCollected<PipingClient>(task_runner);
+  loader->Start(src, client);
+
+  BytesConsumer* dest = client->GetDestination();
+  ASSERT_TRUE(dest);
+
+  auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(dest);
+  auto result = reader->Run(task_runner.get());
+
+  EXPECT_EQ(result.first, BytesConsumer::Result::kError);
+}
+
+TEST_F(FetchDataLoaderTest, LoadAsDataPipeFromDataPipe) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  mojo::ScopedDataPipeConsumerHandle readable;
+  mojo::ScopedDataPipeProducerHandle writable;
+  MojoResult rv = mojo::CreateDataPipe(nullptr, &writable, &readable);
+  ASSERT_EQ(rv, MOJO_RESULT_OK);
+
+  ASSERT_TRUE(mojo::BlockingCopyFromString("hello", writable));
+
+  DataPipeBytesConsumer::CompletionNotifier* completion_notifier = nullptr;
+  auto* src = MakeGarbageCollected<DataPipeBytesConsumer>(
+      task_runner, std::move(readable), &completion_notifier);
+
+  auto* loader = FetchDataLoader::CreateLoaderAsDataPipe(task_runner);
+  auto* client = MakeGarbageCollected<PipingClient>(task_runner);
+  loader->Start(src, client);
+
+  BytesConsumer* dest = client->GetDestination();
+  ASSERT_TRUE(dest);
+
+  const char* buffer = nullptr;
+  size_t available = 0;
+  auto result = dest->BeginRead(&buffer, &available);
+  ASSERT_EQ(result, BytesConsumer::Result::kOk);
+  EXPECT_EQ(available, 5u);
+  EXPECT_EQ(std::string(buffer, available), "hello");
+  result = dest->EndRead(available);
+  ASSERT_EQ(result, BytesConsumer::Result::kOk);
+
+  result = dest->BeginRead(&buffer, &available);
+  ASSERT_EQ(result, BytesConsumer::Result::kShouldWait);
+
+  writable.reset();
+  result = dest->BeginRead(&buffer, &available);
+  ASSERT_EQ(result, BytesConsumer::Result::kShouldWait);
+
+  completion_notifier->SignalComplete();
+  result = dest->BeginRead(&buffer, &available);
+  ASSERT_EQ(result, BytesConsumer::Result::kDone);
+}
+
+TEST_F(FetchDataLoaderTest, LoadAsDataPipeFromDataPipeFailure) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  mojo::ScopedDataPipeConsumerHandle readable;
+  mojo::ScopedDataPipeProducerHandle writable;
+  MojoResult rv = mojo::CreateDataPipe(nullptr, &writable, &readable);
+  ASSERT_EQ(rv, MOJO_RESULT_OK);
+
+  ASSERT_TRUE(mojo::BlockingCopyFromString("hello", writable));
+
+  DataPipeBytesConsumer::CompletionNotifier* completion_notifier = nullptr;
+  auto* src = MakeGarbageCollected<DataPipeBytesConsumer>(
+      task_runner, std::move(readable), &completion_notifier);
+
+  auto* loader = FetchDataLoader::CreateLoaderAsDataPipe(task_runner);
+  auto* client = MakeGarbageCollected<PipingClient>(task_runner);
+  loader->Start(src, client);
+
+  BytesConsumer* dest = client->GetDestination();
+  ASSERT_TRUE(dest);
+
+  completion_notifier->SignalError(BytesConsumer::Error());
+  auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(dest);
+  auto result = reader->Run(task_runner.get());
+
+  EXPECT_EQ(result.first, BytesConsumer::Result::kError);
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy_fuzzer.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy_fuzzer.cc
index 727cc79..cbb2f65 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy_fuzzer.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy_fuzzer.cc
@@ -52,9 +52,8 @@
   // Force a garbage collection.
   // Specify namespace explicitly. Otherwise it conflicts on Mac OS X with:
   // CoreServices.framework/Frameworks/CarbonCore.framework/Headers/Threads.h.
-  blink::ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
 
   return 0;
 }
diff --git a/third_party/blink/renderer/core/html/canvas/text_metrics.idl b/third_party/blink/renderer/core/html/canvas/text_metrics.idl
index b837104..367d5d53 100644
--- a/third_party/blink/renderer/core/html/canvas/text_metrics.idl
+++ b/third_party/blink/renderer/core/html/canvas/text_metrics.idl
@@ -28,7 +28,7 @@
 // TODO(foolip): Exposed=(Window,Worker)
 interface TextMetrics {
     // x-direction
-    readonly attribute float width; // advance width
+    readonly attribute double width; // advance width
     [RuntimeEnabled=ExtendedTextMetrics] readonly attribute FrozenArray<double> advances;
     [RuntimeEnabled=ExtendedTextMetrics] readonly attribute double actualBoundingBoxLeft;
     [RuntimeEnabled=ExtendedTextMetrics] readonly attribute double actualBoundingBoxRight;
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
index ebba576..d5ad417 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
@@ -90,9 +90,8 @@
     // Garbage collection is required prior to switching out the
     // test's memory cache; image resources are released, evicting
     // them from the cache.
-    ThreadState::Current()->CollectGarbage(
-        BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-        BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+    ThreadState::Current()->CollectAllGarbageForTesting(
+        BlinkGC::kNoHeapPointersOnStack);
 
     ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
     SharedGpuContext::ResetForTesting();
diff --git a/third_party/blink/renderer/core/paint/theme_painter_default.cc b/third_party/blink/renderer/core/paint/theme_painter_default.cc
index d4f4e51b..02c262ed 100644
--- a/third_party/blink/renderer/core/paint/theme_painter_default.cc
+++ b/third_party/blink/renderer/core/paint/theme_painter_default.cc
@@ -214,6 +214,15 @@
   if (style.HasBorderRadius() || style.HasBackgroundImage())
     return true;
 
+  // Don't use the theme painter if dark mode is enabled. It has a separate
+  // graphics pipeline that doesn't go through GraphicsContext and so does not
+  // currently know how to handle Dark Mode, causing elements to be rendered
+  // incorrectly (e.g. https://crbug.com/937872).
+  // TODO(gilmanmh): Implement a more permanent solution that allows use of
+  // native dark themes.
+  if (paint_info.context.dark_mode_settings().mode != DarkMode::kOff)
+    return true;
+
   ControlPart part = style.Appearance();
 
   WebThemeEngine::ExtraParams extra_params;
@@ -466,8 +475,7 @@
   // pixel off-center, it will be one pixel closer to the bottom of the field.
   // This tends to look better with the text.
   LayoutRect cancel_button_rect(
-      cancel_button_object.OffsetFromAncestor(&input_layout_box)
-          .Width(),
+      cancel_button_object.OffsetFromAncestor(&input_layout_box).Width(),
       input_content_box.Y() +
           (input_content_box.Height() - cancel_button_size + 1) / 2,
       cancel_button_size, cancel_button_size);
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.h b/third_party/blink/renderer/core/svg/graphics/svg_image.h
index e2391c5..b02db39 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.h
@@ -212,6 +212,14 @@
   void LoadCompleted();
   void NotifyAsyncLoadCompleted();
 
+  // TODO(v.paturi): Implement an SVG classifier which can decide if a
+  // filter should be applied based on the image's content and it's
+  // visibility on a dark background.
+  DarkModeClassification ClassifyImageForDarkMode(
+      const FloatRect& src_rect) override {
+    return DarkModeClassification::kApplyDarkModeFilter;
+  }
+
   class SVGImageLocalFrameClient;
 
   Persistent<SVGImageChromeClient> chrome_client_;
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h b/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h
index 63e8dcf..6348dbd 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image_for_container.h
@@ -88,8 +88,6 @@
 
   PaintImage PaintImageForCurrentFrame() override;
 
-  bool IsSVGImageForContainer() const override { return true; }
-
  protected:
   void DrawPattern(GraphicsContext&,
                    const FloatRect&,
@@ -111,6 +109,14 @@
 
   void DestroyDecodedData() override {}
 
+  // TODO(v.paturi): Implement an SVG classifier which can decide if a
+  // filter should be applied based on the image's content and it's
+  // visibility on a dark background.
+  DarkModeClassification ClassifyImageForDarkMode(
+      const FloatRect& src_rect) override {
+    return DarkModeClassification::kApplyDarkModeFilter;
+  }
+
   SVGImage* image_;
   const FloatSize container_size_;
   const float zoom_;
diff --git a/third_party/blink/renderer/core/workers/worker_thread.h b/third_party/blink/renderer/core/workers/worker_thread.h
index c82c19d..53bab65 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.h
+++ b/third_party/blink/renderer/core/workers/worker_thread.h
@@ -222,6 +222,7 @@
   // and underlying thread. After the global scope is destroyed, queued tasks
   // are discarded and PostTask on the returned task runner just fails. This
   // function can be called on both the main thread and the worker thread.
+  // You must not call this after Terminate() is called.
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner(TaskType type) {
     return worker_scheduler_->GetTaskRunner(type);
   }
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
index 648d3c46..ecfe1761 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
@@ -236,9 +236,9 @@
 }
 
 void CanvasRenderingContext2DTest::TearDown() {
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
+
   ReplaceMemoryCacheForTesting(global_memory_cache_.Release());
   SharedGpuContext::ResetForTesting();
 }
diff --git a/third_party/blink/renderer/modules/crypto/crypto.cc b/third_party/blink/renderer/modules/crypto/crypto.cc
index 7c5bf68..67328c7 100644
--- a/third_party/blink/renderer/modules/crypto/crypto.cc
+++ b/third_party/blink/renderer/modules/crypto/crypto.cc
@@ -76,7 +76,7 @@
 
 SubtleCrypto* Crypto::subtle() {
   if (!subtle_crypto_)
-    subtle_crypto_ = SubtleCrypto::Create();
+    subtle_crypto_ = MakeGarbageCollected<SubtleCrypto>();
   return subtle_crypto_.Get();
 }
 
diff --git a/third_party/blink/renderer/modules/crypto/crypto_key.h b/third_party/blink/renderer/modules/crypto/crypto_key.h
index 5ad7246..8b6d315 100644
--- a/third_party/blink/renderer/modules/crypto/crypto_key.h
+++ b/third_party/blink/renderer/modules/crypto/crypto_key.h
@@ -47,10 +47,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static CryptoKey* Create(const WebCryptoKey& key) {
-    return MakeGarbageCollected<CryptoKey>(key);
-  }
-
   explicit CryptoKey(const WebCryptoKey&);
   ~CryptoKey() override;
 
diff --git a/third_party/blink/renderer/modules/crypto/crypto_result_impl.cc b/third_party/blink/renderer/modules/crypto/crypto_result_impl.cc
index a4de48e4..f10efd9 100644
--- a/third_party/blink/renderer/modules/crypto/crypto_result_impl.cc
+++ b/third_party/blink/renderer/modules/crypto/crypto_result_impl.cc
@@ -129,10 +129,6 @@
   resolver_ = nullptr;
 }
 
-CryptoResultImpl* CryptoResultImpl::Create(ScriptState* script_state) {
-  return MakeGarbageCollected<CryptoResultImpl>(script_state);
-}
-
 void CryptoResultImpl::CompleteWithError(WebCryptoErrorType error_type,
                                          const WebString& error_details) {
   if (!resolver_)
@@ -201,7 +197,7 @@
   if (!resolver_)
     return;
 
-  resolver_->Resolve(CryptoKey::Create(key));
+  resolver_->Resolve(MakeGarbageCollected<CryptoKey>(key));
   ClearResolver();
 }
 
@@ -216,9 +212,11 @@
   V8ObjectBuilder key_pair(script_state);
 
   key_pair.Add("publicKey",
-               ScriptValue::From(script_state, CryptoKey::Create(public_key)));
+               ScriptValue::From(script_state,
+                                 MakeGarbageCollected<CryptoKey>(public_key)));
   key_pair.Add("privateKey",
-               ScriptValue::From(script_state, CryptoKey::Create(private_key)));
+               ScriptValue::From(script_state,
+                                 MakeGarbageCollected<CryptoKey>(private_key)));
 
   resolver_->Resolve(key_pair.V8Value());
   ClearResolver();
diff --git a/third_party/blink/renderer/modules/crypto/crypto_result_impl.h b/third_party/blink/renderer/modules/crypto/crypto_result_impl.h
index f1e97954..0a059cd 100644
--- a/third_party/blink/renderer/modules/crypto/crypto_result_impl.h
+++ b/third_party/blink/renderer/modules/crypto/crypto_result_impl.h
@@ -55,8 +55,6 @@
 //    m_resolver will be leaked until the ExecutionContext is destroyed.
 class MODULES_EXPORT CryptoResultImpl final : public CryptoResult {
  public:
-  static CryptoResultImpl* Create(ScriptState*);
-
   explicit CryptoResultImpl(ScriptState*);
   ~CryptoResultImpl() override;
 
diff --git a/third_party/blink/renderer/modules/crypto/dom_window_crypto.cc b/third_party/blink/renderer/modules/crypto/dom_window_crypto.cc
index 36dac7d0..97b16247 100644
--- a/third_party/blink/renderer/modules/crypto/dom_window_crypto.cc
+++ b/third_party/blink/renderer/modules/crypto/dom_window_crypto.cc
@@ -56,7 +56,7 @@
 
 Crypto* DOMWindowCrypto::crypto() const {
   if (!crypto_)
-    crypto_ = Crypto::Create();
+    crypto_ = MakeGarbageCollected<Crypto>();
   return crypto_.Get();
 }
 
diff --git a/third_party/blink/renderer/modules/crypto/subtle_crypto.cc b/third_party/blink/renderer/modules/crypto/subtle_crypto.cc
index 5f83946b..6dbc3f4 100644
--- a/third_party/blink/renderer/modules/crypto/subtle_crypto.cc
+++ b/third_party/blink/renderer/modules/crypto/subtle_crypto.cc
@@ -167,7 +167,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-encrypt
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   // 14.3.1.2: Let data be the result of getting a copy of the bytes held by
@@ -209,7 +209,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-decrypt
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   // 14.3.2.2: Let data be the result of getting a copy of the bytes held by
@@ -251,7 +251,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-sign
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   // 14.3.3.2: Let data be the result of getting a copy of the bytes held by
@@ -295,7 +295,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-verify
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   // 14.3.4.2: Let signature be the result of getting a copy of the bytes
@@ -340,7 +340,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-digest
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   // 14.3.5.2: Let data be the result of getting a copy of the bytes held
@@ -373,7 +373,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-generateKey
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   WebCryptoKeyUsageMask key_usages;
@@ -413,7 +413,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-importKey
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   WebCryptoKeyFormat format;
@@ -497,7 +497,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-exportKey
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   WebCryptoKeyFormat format;
@@ -530,7 +530,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-wrapKey
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   WebCryptoKeyFormat format;
@@ -595,7 +595,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-unwrapKey
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   WebCryptoKeyFormat format;
@@ -667,7 +667,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#dfn-SubtleCrypto-method-deriveBits
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   // 14.3.8.2: Let normalizedAlgorithm be the result of normalizing an
@@ -709,7 +709,7 @@
   // Method described by:
   // https://w3c.github.io/webcrypto/Overview.html#SubtleCrypto-method-deriveKey
 
-  CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+  auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
   ScriptPromise promise = result->Promise();
 
   WebCryptoKeyUsageMask key_usages;
diff --git a/third_party/blink/renderer/modules/crypto/subtle_crypto.h b/third_party/blink/renderer/modules/crypto/subtle_crypto.h
index 87185e6..e112079 100644
--- a/third_party/blink/renderer/modules/crypto/subtle_crypto.h
+++ b/third_party/blink/renderer/modules/crypto/subtle_crypto.h
@@ -50,8 +50,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static SubtleCrypto* Create() { return MakeGarbageCollected<SubtleCrypto>(); }
-
   SubtleCrypto();
 
   ScriptPromise encrypt(ScriptState*,
diff --git a/third_party/blink/renderer/modules/crypto/worker_global_scope_crypto.cc b/third_party/blink/renderer/modules/crypto/worker_global_scope_crypto.cc
index bec0df05..5f0b33f 100644
--- a/third_party/blink/renderer/modules/crypto/worker_global_scope_crypto.cc
+++ b/third_party/blink/renderer/modules/crypto/worker_global_scope_crypto.cc
@@ -58,7 +58,7 @@
 
 Crypto* WorkerGlobalScopeCrypto::crypto() const {
   if (!crypto_)
-    crypto_ = Crypto::Create();
+    crypto_ = MakeGarbageCollected<Crypto>();
   return crypto_.Get();
 }
 
diff --git a/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc b/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
index c961997..a267772 100644
--- a/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
+++ b/third_party/blink/renderer/modules/csspaint/css_paint_definition.cc
@@ -33,19 +33,6 @@
 
 }  // namespace
 
-CSSPaintDefinition* CSSPaintDefinition::Create(
-    ScriptState* script_state,
-    V8NoArgumentConstructor* constructor,
-    V8PaintCallback* paint,
-    const Vector<CSSPropertyID>& native_invalidation_properties,
-    const Vector<AtomicString>& custom_invalidation_properties,
-    const Vector<CSSSyntaxDescriptor>& input_argument_types,
-    const PaintRenderingContext2DSettings* context_settings) {
-  return MakeGarbageCollected<CSSPaintDefinition>(
-      script_state, constructor, paint, native_invalidation_properties,
-      custom_invalidation_properties, input_argument_types, context_settings);
-}
-
 CSSPaintDefinition::CSSPaintDefinition(
     ScriptState* script_state,
     V8NoArgumentConstructor* constructor,
@@ -66,16 +53,12 @@
 
 CSSPaintDefinition::~CSSPaintDefinition() = default;
 
-scoped_refptr<Image> CSSPaintDefinition::Paint(
-    const ImageResourceObserver& client,
+sk_sp<PaintRecord> CSSPaintDefinition::Paint(
     const FloatSize& container_size,
+    float zoom,
+    StylePropertyMapReadOnly* style_map,
     const CSSStyleValueVector* paint_arguments) {
-  // TODO: Break dependency on LayoutObject. Passing the Node should work.
-  const LayoutObject& layout_object = static_cast<const LayoutObject&>(client);
-
-  float zoom = layout_object.StyleRef().EffectiveZoom();
   const FloatSize specified_size = GetSpecifiedSize(container_size, zoom);
-
   ScriptState::Scope scope(script_state_);
 
   MaybeCreatePaintInstance();
@@ -86,21 +69,15 @@
 
   v8::Isolate* isolate = script_state_->GetIsolate();
 
-  DCHECK(layout_object.GetNode());
   CanvasColorParams color_params;
   if (!context_settings_->alpha()) {
     color_params.SetOpacityMode(kOpaque);
   }
 
   // Do subpixel snapping for the |container_size|.
-  PaintRenderingContext2D* rendering_context = PaintRenderingContext2D::Create(
+  auto* rendering_context = MakeGarbageCollected<PaintRenderingContext2D>(
       RoundedIntSize(container_size), color_params, context_settings_, zoom);
-  PaintSize* paint_size = PaintSize::Create(specified_size);
-  StylePropertyMapReadOnly* style_map =
-      MakeGarbageCollected<PrepopulatedComputedStylePropertyMap>(
-          layout_object.GetDocument(), layout_object.StyleRef(),
-          layout_object.GetNode(), native_invalidation_properties_,
-          custom_invalidation_properties_);
+  PaintSize* paint_size = MakeGarbageCollected<PaintSize>(specified_size);
 
   CSSStyleValueVector empty_paint_arguments;
   if (!paint_arguments)
@@ -118,8 +95,7 @@
     return nullptr;
   }
 
-  return PaintGeneratedImage::Create(rendering_context->GetRecord(),
-                                     container_size);
+  return rendering_context->GetRecord();
 }
 
 void CSSPaintDefinition::MaybeCreatePaintInstance() {
diff --git a/third_party/blink/renderer/modules/csspaint/css_paint_definition.h b/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
index abde5b64..c5becea 100644
--- a/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
+++ b/third_party/blink/renderer/modules/csspaint/css_paint_definition.h
@@ -14,14 +14,15 @@
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
 #include "third_party/blink/renderer/platform/geometry/float_size.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
 #include "v8/include/v8.h"
 
 namespace blink {
 
-class Image;
-class ImageResourceObserver;
 class ScriptState;
+class StylePropertyMapReadOnly;
 class V8NoArgumentConstructor;
 class V8PaintCallback;
 
@@ -32,15 +33,6 @@
     : public GarbageCollectedFinalized<CSSPaintDefinition>,
       public NameClient {
  public:
-  static CSSPaintDefinition* Create(
-      ScriptState*,
-      V8NoArgumentConstructor* constructor,
-      V8PaintCallback* paint,
-      const Vector<CSSPropertyID>&,
-      const Vector<AtomicString>& custom_invalidation_properties,
-      const Vector<CSSSyntaxDescriptor>& input_argument_types,
-      const PaintRenderingContext2DSettings*);
-
   CSSPaintDefinition(
       ScriptState*,
       V8NoArgumentConstructor* constructor,
@@ -59,9 +51,10 @@
   // throws an error.
   //
   // The |container_size| is without subpixel snapping.
-  scoped_refptr<Image> Paint(const ImageResourceObserver&,
-                             const FloatSize& container_size,
-                             const CSSStyleValueVector*);
+  sk_sp<PaintRecord> Paint(const FloatSize& container_size,
+                           float zoom,
+                           StylePropertyMapReadOnly*,
+                           const CSSStyleValueVector*);
   const Vector<CSSPropertyID>& NativeInvalidationProperties() const {
     return native_invalidation_properties_;
   }
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h
index 762afc7..22d7629 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d.h
@@ -26,15 +26,6 @@
   USING_GARBAGE_COLLECTED_MIXIN(PaintRenderingContext2D);
 
  public:
-  static PaintRenderingContext2D* Create(
-      const IntSize& container_size,
-      const CanvasColorParams& color_params,
-      const PaintRenderingContext2DSettings* context_settings,
-      float zoom) {
-    return MakeGarbageCollected<PaintRenderingContext2D>(
-        container_size, color_params, context_settings, zoom);
-  }
-
   PaintRenderingContext2D(const IntSize& container_size,
                           const CanvasColorParams&,
                           const PaintRenderingContext2DSettings*,
diff --git a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d_test.cc
index 116fd82..a4ba95a 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_rendering_context_2d_test.cc
@@ -24,7 +24,7 @@
   PaintRenderingContext2DSettings* context_settings =
       PaintRenderingContext2DSettings::Create();
   context_settings->setAlpha(false);
-  ctx_ = PaintRenderingContext2D::Create(
+  ctx_ = MakeGarbageCollected<PaintRenderingContext2D>(
       IntSize(kWidth, kHeight), CanvasColorParams(), context_settings, kZoom);
 }
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_size.h b/third_party/blink/renderer/modules/csspaint/paint_size.h
index 474dd2ce..7719929 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_size.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_size.h
@@ -16,10 +16,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static PaintSize* Create(FloatSize size) {
-    return MakeGarbageCollected<PaintSize>(size);
-  }
-
   explicit PaintSize(FloatSize size) : size_(size) {}
   ~PaintSize() override = default;
 
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
index bc2d1f5..aba0caa 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.cc
@@ -7,14 +7,17 @@
 #include "base/atomic_sequence_num.h"
 #include "base/rand_util.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/core/css/cssom/prepopulated_computed_style_property_map.h"
 #include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/node_rare_data.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/modules/csspaint/css_paint_definition.h"
 #include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h"
 #include "third_party/blink/renderer/modules/csspaint/paint_worklet_messaging_proxy.h"
 #include "third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h"
-#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/paint_generated_image.h"
 
 namespace blink {
 
@@ -36,17 +39,12 @@
   PaintWorklet* supplement =
       Supplement<LocalDOMWindow>::From<PaintWorklet>(window);
   if (!supplement && window.GetFrame()) {
-    supplement = Create(window.GetFrame());
+    supplement = MakeGarbageCollected<PaintWorklet>(window.GetFrame());
     ProvideTo(window, supplement);
   }
   return supplement;
 }
 
-// static
-PaintWorklet* PaintWorklet::Create(LocalFrame* frame) {
-  return MakeGarbageCollected<PaintWorklet>(frame);
-}
-
 PaintWorklet::PaintWorklet(LocalFrame* frame)
     : Worklet(frame->GetDocument()),
       Supplement<LocalDOMWindow>(*frame->DomWindow()),
@@ -121,7 +119,21 @@
   CSSPaintDefinition* paint_definition = proxy->FindDefinition(name);
   if (!paint_definition)
     return nullptr;
-  return paint_definition->Paint(observer, container_size, data);
+  // TODO(crbug.com/946515): Break dependency on LayoutObject.
+  const LayoutObject& layout_object =
+      static_cast<const LayoutObject&>(observer);
+  float zoom = layout_object.StyleRef().EffectiveZoom();
+  StylePropertyMapReadOnly* style_map =
+      MakeGarbageCollected<PrepopulatedComputedStylePropertyMap>(
+          layout_object.GetDocument(), layout_object.StyleRef(),
+          layout_object.GetNode(),
+          paint_definition->NativeInvalidationProperties(),
+          paint_definition->CustomInvalidationProperties());
+  sk_sp<PaintRecord> paint_record =
+      paint_definition->Paint(container_size, zoom, style_map, data);
+  if (!paint_record)
+    return nullptr;
+  return PaintGeneratedImage::Create(paint_record, container_size);
 }
 
 // static
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet.h b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
index c0d771f2..6fd1431 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet.h
@@ -31,7 +31,6 @@
   // At this moment, paint worklet allows at most two global scopes at any time.
   static const wtf_size_t kNumGlobalScopes;
   static PaintWorklet* From(LocalDOMWindow&);
-  static PaintWorklet* Create(LocalFrame*);
 
   explicit PaintWorklet(LocalFrame*);
   ~PaintWorklet() override;
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
index 1b908ce..ec592cc 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.cc
@@ -214,7 +214,7 @@
     return;
   V8PaintCallback* paint = V8PaintCallback::Create(v8_paint);
 
-  CSSPaintDefinition* definition = CSSPaintDefinition::Create(
+  auto* definition = MakeGarbageCollected<CSSPaintDefinition>(
       ScriptController()->GetScriptState(), paint_ctor, paint,
       native_invalidation_properties, custom_invalidation_properties,
       input_argument_types, context_settings);
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
index f70f53e..17e16d9 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h"
 
 #include "base/single_thread_task_runner.h"
+#include "third_party/blink/renderer/core/css/cssom/paint_worklet_input.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/web_frame_widget_base.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
@@ -85,6 +86,18 @@
   state_ = RunState::kDisposed;
 }
 
+sk_sp<PaintRecord> PaintWorkletProxyClient::Paint(
+    CompositorPaintWorkletInput* compositor_input) {
+  if (!global_scope_)
+    return sk_make_sp<PaintRecord>();
+  PaintWorkletInput* input = static_cast<PaintWorkletInput*>(compositor_input);
+  CSSPaintDefinition* definition =
+      global_scope_->FindDefinition(input->NameCopy());
+
+  return definition->Paint(FloatSize(input->GetSize()), input->EffectiveZoom(),
+                           nullptr, nullptr);
+}
+
 // static
 PaintWorkletProxyClient* PaintWorkletProxyClient::From(WorkerClients* clients) {
   return Supplement<WorkerClients>::From<PaintWorkletProxyClient>(clients);
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
index 639b999..94e604da 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h"
 #include "third_party/blink/renderer/platform/graphics/paint_worklet_painter.h"
+#include "third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h"
 
 namespace blink {
 
@@ -44,6 +45,10 @@
 
   void Trace(blink::Visitor*) override;
 
+  // PaintWorkletPainter implementation
+  int GetWorkletId() const override { return worklet_id_; }
+  sk_sp<PaintRecord> Paint(CompositorPaintWorkletInput*) override;
+
   virtual void SetGlobalScope(WorkletGlobalScope*);
   void SetGlobalScopeForTesting(PaintWorkletGlobalScope*);
   void Dispose();
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
index 4c099d85d..be13087c 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_proxy_client_test.cc
@@ -10,8 +10,12 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/test/test_simple_task_runner.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_source_code.h"
+#include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
+#include "third_party/blink/renderer/core/css/cssom/paint_worklet_input.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/core/workers/worker_reporting_proxy.h"
+#include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h"
 #include "third_party/blink/renderer/modules/worklet/worklet_thread_test_common.h"
 #include "third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h"
 
@@ -40,6 +44,64 @@
     waitable_event->Signal();
   }
 
+  void SetGlobalScopeForTesting(WorkerThread* thread,
+                                PaintWorkletProxyClient* proxy_client,
+                                base::WaitableEvent* waitable_event) {
+    proxy_client->SetGlobalScopeForTesting(
+        static_cast<PaintWorkletGlobalScope*>(
+            To<WorkletGlobalScope>(thread->GlobalScope())));
+    waitable_event->Signal();
+  }
+
+  using TestCallback =
+      void (PaintWorkletProxyClientTest::*)(WorkerThread*,
+                                            PaintWorkletProxyClient*,
+                                            base::WaitableEvent*);
+  void RunTestOnWorkletThread(TestCallback callback) {
+    std::unique_ptr<WorkerThread> worklet =
+        CreateThreadAndProvidePaintWorkletProxyClient(
+            &GetDocument(), reporting_proxy_.get(), proxy_client_);
+
+    base::WaitableEvent waitable_event;
+    PostCrossThreadTask(
+        *worklet->GetTaskRunner(TaskType::kInternalTest), FROM_HERE,
+        CrossThreadBind(
+            callback, CrossThreadUnretained(this),
+            CrossThreadUnretained(worklet.get()),
+            CrossThreadPersistent<PaintWorkletProxyClient>(proxy_client_),
+            CrossThreadUnretained(&waitable_event)));
+    waitable_event.Wait();
+    waitable_event.Reset();
+
+    worklet->Terminate();
+    worklet->WaitForShutdownForTesting();
+  }
+
+  void RunPaintOnWorklet(WorkerThread* thread,
+                         PaintWorkletProxyClient* proxy_client,
+                         base::WaitableEvent* waitable_event) {
+    // The "registerPaint" script calls the real SetGlobalScope, so at this
+    // moment all we need is setting the |global_scope_| without any other
+    // things.
+    proxy_client->SetGlobalScopeForTesting(
+        static_cast<PaintWorkletGlobalScope*>(
+            To<WorkletGlobalScope>(thread->GlobalScope())));
+    CrossThreadPersistent<PaintWorkletGlobalScope> global_scope =
+        proxy_client->global_scope_;
+    global_scope->ScriptController()->Evaluate(
+        ScriptSourceCode("registerPaint('foo', class { paint() { } });"),
+        SanitizeScriptErrors::kDoNotSanitize);
+
+    PaintWorkletStylePropertyMap::CrossThreadData data;
+    scoped_refptr<PaintWorkletInput> input =
+        base::MakeRefCounted<PaintWorkletInput>("foo", FloatSize(100, 100),
+                                                1.0f, 1, std::move(data));
+    sk_sp<PaintRecord> record = proxy_client->Paint(input.get());
+    EXPECT_NE(record, nullptr);
+
+    waitable_event->Signal();
+  }
+
   scoped_refptr<PaintWorkletPaintDispatcher> dispatcher_;
   Persistent<PaintWorkletProxyClient> proxy_client_;
   std::unique_ptr<WorkerReportingProxy> reporting_proxy_;
@@ -86,4 +148,9 @@
   worklet_thread->WaitForShutdownForTesting();
 }
 
+TEST_F(PaintWorkletProxyClientTest, Paint) {
+  ScopedOffMainThreadCSSPaintForTest off_main_thread_css_paint(true);
+  RunTestOnWorkletThread(&PaintWorkletProxyClientTest::RunPaintOnWorklet);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
index 3d6790b..f96b0ac1 100644
--- a/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
+++ b/third_party/blink/renderer/modules/csspaint/paint_worklet_test.cc
@@ -11,12 +11,13 @@
 #include "third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 #include "third_party/blink/renderer/modules/csspaint/css_paint_definition.h"
 #include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope.h"
 #include "third_party/blink/renderer/modules/csspaint/paint_worklet_global_scope_proxy.h"
-#include "third_party/blink/renderer/platform/graphics/image.h"
+#include "third_party/blink/renderer/platform/graphics/paint_generated_image.h"
 
 namespace blink {
 class TestPaintWorklet : public PaintWorklet {
@@ -122,8 +123,17 @@
   ASSERT_TRUE(observer);
 
   const FloatSize container_size(100, 100);
-  scoped_refptr<Image> image =
-      definition->Paint(*observer, container_size, nullptr);
+  const LayoutObject& layout_object =
+      static_cast<const LayoutObject&>(*observer);
+  float zoom = layout_object.StyleRef().EffectiveZoom();
+  StylePropertyMapReadOnly* style_map =
+      MakeGarbageCollected<PrepopulatedComputedStylePropertyMap>(
+          layout_object.GetDocument(), layout_object.StyleRef(),
+          layout_object.GetNode(), definition->NativeInvalidationProperties(),
+          definition->CustomInvalidationProperties());
+  scoped_refptr<Image> image = PaintGeneratedImage::Create(
+      definition->Paint(container_size, zoom, style_map, nullptr),
+      container_size);
   EXPECT_NE(image, nullptr);
 }
 
diff --git a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
index 129e1f1..387f29e 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/html_media_element_encrypted_media.cc
@@ -400,7 +400,8 @@
   initializer->setBubbles(false);
   initializer->setCancelable(false);
 
-  return MediaEncryptedEvent::Create(event_type_names::kEncrypted, initializer);
+  return MakeGarbageCollected<MediaEncryptedEvent>(event_type_names::kEncrypted,
+                                                   initializer);
 }
 
 void HTMLMediaElementEncryptedMedia::Encrypted(
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
index 2efd732..840e1b4 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.cc
@@ -337,14 +337,6 @@
   Member<MediaKeySession> session_;
 };
 
-MediaKeySession* MediaKeySession::Create(
-    ScriptState* script_state,
-    MediaKeys* media_keys,
-    WebEncryptedMediaSessionType session_type) {
-  return MakeGarbageCollected<MediaKeySession>(script_state, media_keys,
-                                               session_type);
-}
-
 MediaKeySession::MediaKeySession(ScriptState* script_state,
                                  MediaKeys* media_keys,
                                  WebEncryptedMediaSessionType session_type)
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.h b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.h
index 20ea68a..5991da690 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_session.h
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_session.h
@@ -71,10 +71,6 @@
   USING_PRE_FINALIZER(MediaKeySession, Dispose);
 
  public:
-  static MediaKeySession* Create(ScriptState*,
-                                 MediaKeys*,
-                                 WebEncryptedMediaSessionType);
-
   MediaKeySession(ScriptState*, MediaKeys*, WebEncryptedMediaSessionType);
   ~MediaKeySession() override;
 
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc b/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
index 8c930dcd..bd6825d 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
@@ -20,10 +20,6 @@
 class MediaKeyStatusMap::MapEntry final
     : public GarbageCollectedFinalized<MediaKeyStatusMap::MapEntry> {
  public:
-  static MapEntry* Create(WebData key_id, const String& status) {
-    return MakeGarbageCollected<MapEntry>(key_id, status);
-  }
-
   MapEntry(WebData key_id, const String& status)
       : key_id_(DOMArrayBuffer::Create(scoped_refptr<SharedBuffer>(key_id))),
         status_(status) {}
@@ -108,7 +104,7 @@
 
 void MediaKeyStatusMap::AddEntry(WebData key_id, const String& status) {
   // Insert new entry into sorted list.
-  MapEntry* entry = MapEntry::Create(key_id, status);
+  auto* entry = MakeGarbageCollected<MapEntry>(key_id, status);
   uint32_t index = 0;
   while (index < entries_.size() &&
          MapEntry::CompareLessThan(entries_[index], entry))
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.cc b/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.cc
index f412eb8..568651b5 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.cc
@@ -55,7 +55,7 @@
       return;
 
     // 2.9. Let media keys be a new MediaKeys object.
-    MediaKeys* media_keys = MediaKeys::Create(
+    auto* media_keys = MakeGarbageCollected<MediaKeys>(
         GetExecutionContext(), supported_session_types_, base::WrapUnique(cdm));
 
     // 2.10. Resolve promise with media keys.
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc b/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
index 7550712a..3c7b9af 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_keys.cc
@@ -200,14 +200,6 @@
   Member<MediaKeys> media_keys_;
 };
 
-MediaKeys* MediaKeys::Create(
-    ExecutionContext* context,
-    const WebVector<WebEncryptedMediaSessionType>& supported_session_types,
-    std::unique_ptr<WebContentDecryptionModule> cdm) {
-  return MakeGarbageCollected<MediaKeys>(context, supported_session_types,
-                                         std::move(cdm));
-}
-
 MediaKeys::MediaKeys(
     ExecutionContext* context,
     const WebVector<WebEncryptedMediaSessionType>& supported_session_types,
@@ -273,7 +265,8 @@
   //    follows:
   //    (Initialization is performed in the constructor.)
   // 4. Return session.
-  return MediaKeySession::Create(script_state, this, session_type);
+  return MakeGarbageCollected<MediaKeySession>(script_state, this,
+                                               session_type);
 }
 
 ScriptPromise MediaKeys::setServerCertificate(
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_keys.h b/third_party/blink/renderer/modules/encryptedmedia/media_keys.h
index 7549e59d..542e066 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_keys.h
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_keys.h
@@ -59,11 +59,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static MediaKeys* Create(
-      ExecutionContext*,
-      const WebVector<WebEncryptedMediaSessionType>& supported_session_types,
-      std::unique_ptr<WebContentDecryptionModule>);
-
   MediaKeys(
       ExecutionContext*,
       const WebVector<WebEncryptedMediaSessionType>& supported_session_types,
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index 5b4ce16..2fcbd13c 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -181,16 +181,9 @@
     pause_after_download_state_ = kDoPauseAfterDownload;
 
   devtools_worker_token_ = data.devtools_worker_token;
-  // S13nServiceWorker: |loader_factory| is null since all loads for new scripts
-  // go through (internal WebServiceWorkerNetworkProvider class in
-  // service_worker_context_client.cc)::script_loader_factory() rather than the
-  // shadow page's loader. This is different to shared workers, which use
-  // script_loader_factory() for the main script only, and the shadow page
-  // loader for importScripts().
-  //
-  // Non-S13nServiceWorker: |loader_factory| is null since the main script load
-  // goes through the shadow page loader which uses the default loader that goes
-  // to ResourceDispatcherHost.
+  // |loader_factory| is null since all loads for new scripts go through
+  // ServiceWorkerNetworkProviderForServiceWorker::script_loader_factory()
+  // rather than the shadow page's loader.
   shadow_page_ = std::make_unique<WorkerShadowPage>(
       this, nullptr /* loader_factory */,
       std::move(worker_start_data_.privacy_preferences));
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad.cc b/third_party/blink/renderer/modules/gamepad/gamepad.cc
index 7e17e6b..a351250 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad.cc
@@ -41,11 +41,6 @@
 
 Gamepad::~Gamepad() = default;
 
-// static
-Gamepad* Gamepad::Create(ExecutionContext* context) {
-  return MakeGarbageCollected<Gamepad>(context);
-}
-
 const Gamepad::DoubleVector& Gamepad::axes() {
   is_axis_data_dirty_ = false;
   return axes_;
@@ -82,7 +77,7 @@
   if (buttons_.size() != count) {
     buttons_.resize(count);
     for (unsigned i = 0; i < count; ++i)
-      buttons_[i] = GamepadButton::Create();
+      buttons_[i] = MakeGarbageCollected<GamepadButton>();
   }
   for (unsigned i = 0; i < count; ++i)
     buttons_[i]->UpdateValuesFrom(data[i]);
@@ -103,7 +98,7 @@
   }
 
   if (!pose_)
-    pose_ = GamepadPose::Create();
+    pose_ = MakeGarbageCollected<GamepadPose>();
 
   pose_->SetPose(pose);
 }
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad.h b/third_party/blink/renderer/modules/gamepad/gamepad.h
index a0ece959..5bf19a8 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad.h
@@ -45,8 +45,6 @@
   USING_GARBAGE_COLLECTED_MIXIN(Gamepad);
 
  public:
-  static Gamepad* Create(ExecutionContext* context);
-
   explicit Gamepad(ExecutionContext*);
   ~Gamepad() override;
 
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_button.cc b/third_party/blink/renderer/modules/gamepad/gamepad_button.cc
index d60d596c..4d9a2b5 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_button.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_button.cc
@@ -6,10 +6,6 @@
 
 namespace blink {
 
-GamepadButton* GamepadButton::Create() {
-  return MakeGarbageCollected<GamepadButton>();
-}
-
 GamepadButton::GamepadButton() : value_(0.), pressed_(false), touched_(false) {}
 
 bool GamepadButton::IsEqual(const device::GamepadButton& device_button) const {
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_button.h b/third_party/blink/renderer/modules/gamepad/gamepad_button.h
index c2838e0..35099bcb 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_button.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_button.h
@@ -16,8 +16,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static GamepadButton* Create();
-
   GamepadButton();
 
   double value() const { return value_; }
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc b/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
index de383a9..96877dea 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_comparisons_test.cc
@@ -44,14 +44,16 @@
     InitGamepadVector(p.linear_acceleration);
   }
 
-  GamepadList* CreateEmptyGamepadList() { return GamepadList::Create(); }
+  GamepadList* CreateEmptyGamepadList() {
+    return MakeGarbageCollected<GamepadList>();
+  }
 
   GamepadList* CreateGamepadListWithNeutralGamepad() {
     double axes[1] = {0.0};
     device::GamepadButton buttons[1] = {{false, false, 0.0}};
     device::GamepadPose null_pose;
-    auto* list = GamepadList::Create();
-    auto* gamepad = Gamepad::Create(nullptr);
+    auto* list = MakeGarbageCollected<GamepadList>();
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
     gamepad->SetId("gamepad");
     gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
@@ -66,8 +68,8 @@
     double axes[1] = {0.95};
     device::GamepadButton buttons[1] = {{false, false, 0.0}};
 
-    auto* list = GamepadList::Create();
-    auto* gamepad = Gamepad::Create(nullptr);
+    auto* list = MakeGarbageCollected<GamepadList>();
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
     gamepad->SetId("gamepad");
     gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
@@ -81,8 +83,8 @@
     double axes[1] = {0.0};
     device::GamepadButton buttons[1] = {{true, true, 1.0}};
 
-    auto* list = GamepadList::Create();
-    auto* gamepad = Gamepad::Create(nullptr);
+    auto* list = MakeGarbageCollected<GamepadList>();
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
     gamepad->SetId("gamepad");
     gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
@@ -101,8 +103,8 @@
         device::GamepadButton::kDefaultButtonPressedThreshold - 0.01,
     }};
 
-    auto* list = GamepadList::Create();
-    auto* gamepad = Gamepad::Create(nullptr);
+    auto* list = MakeGarbageCollected<GamepadList>();
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
     gamepad->SetId("gamepad");
     gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
@@ -121,8 +123,8 @@
         device::GamepadButton::kDefaultButtonPressedThreshold + 0.01,
     }};
 
-    auto* list = GamepadList::Create();
-    auto* gamepad = Gamepad::Create(nullptr);
+    auto* list = MakeGarbageCollected<GamepadList>();
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
     gamepad->SetId("gamepad");
     gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
@@ -137,8 +139,8 @@
     device::GamepadButton buttons[1] = {{false, false, 0.0}};
     device::GamepadPose pose;
     InitGamepadPose(pose);
-    auto* list = GamepadList::Create();
-    auto* gamepad = Gamepad::Create(nullptr);
+    auto* list = MakeGarbageCollected<GamepadList>();
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
     gamepad->SetId("gamepad");
     gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
@@ -156,8 +158,8 @@
     InitGamepadPose(pose);
     // Modify the linear velocity.
     pose.linear_velocity.x = 100.f;
-    auto* list = GamepadList::Create();
-    auto* gamepad = Gamepad::Create(nullptr);
+    auto* list = MakeGarbageCollected<GamepadList>();
+    auto* gamepad = MakeGarbageCollected<Gamepad>(nullptr);
     gamepad->SetId("gamepad");
     gamepad->SetIndex(0);
     gamepad->SetAxes(1, axes);
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_list.h b/third_party/blink/renderer/modules/gamepad/gamepad_list.h
index 9bf5256..b4a203c7 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_list.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_list.h
@@ -38,8 +38,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static GamepadList* Create() { return MakeGarbageCollected<GamepadList>(); }
-
   GamepadList();
 
   void Set(unsigned index, Gamepad*);
diff --git a/third_party/blink/renderer/modules/gamepad/gamepad_pose.h b/third_party/blink/renderer/modules/gamepad/gamepad_pose.h
index 4bf4f6c4..0b4b2ef22 100644
--- a/third_party/blink/renderer/modules/gamepad/gamepad_pose.h
+++ b/third_party/blink/renderer/modules/gamepad/gamepad_pose.h
@@ -17,8 +17,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static GamepadPose* Create() { return MakeGarbageCollected<GamepadPose>(); }
-
   GamepadPose();
 
   bool hasOrientation() const { return has_orientation_; }
diff --git a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
index c7f1a9b..7e32a770 100644
--- a/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
+++ b/third_party/blink/renderer/modules/gamepad/navigator_gamepad.cc
@@ -128,7 +128,7 @@
     } else if (web_gamepad.connected) {
       Gamepad* gamepad = into->item(i);
       if (!gamepad)
-        gamepad = Gamepad::Create(context);
+        gamepad = MakeGarbageCollected<Gamepad>(context);
       SampleGamepad(i, *gamepad, web_gamepad, navigation_start, gamepads_start);
       into->Set(i, gamepad);
     } else {
@@ -183,7 +183,7 @@
 
   // Ensure |gamepads_| is not null.
   if (!gamepads_)
-    gamepads_ = GamepadList::Create();
+    gamepads_ = MakeGarbageCollected<GamepadList>();
 
   // Allow gamepad button presses to qualify as user activations if the page is
   // visible.
@@ -331,7 +331,7 @@
     if (GetPage()->IsPageVisible()) {
       // Allocate a buffer to hold the new gamepad state, if needed.
       if (!gamepads_back_)
-        gamepads_back_ = GamepadList::Create();
+        gamepads_back_ = MakeGarbageCollected<GamepadList>();
 
       bool include_xr_gamepads = ShouldIncludeXrGamepads(GetFrame());
 
diff --git a/third_party/blink/renderer/modules/geolocation/coordinates.h b/third_party/blink/renderer/modules/geolocation/coordinates.h
index 6a5281a..6296150 100644
--- a/third_party/blink/renderer/modules/geolocation/coordinates.h
+++ b/third_party/blink/renderer/modules/geolocation/coordinates.h
@@ -36,23 +36,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static Coordinates* Create(double latitude,
-                             double longitude,
-                             bool provides_altitude,
-                             double altitude,
-                             double accuracy,
-                             bool provides_altitude_accuracy,
-                             double altitude_accuracy,
-                             bool provides_heading,
-                             double heading,
-                             bool provides_speed,
-                             double speed) {
-    return MakeGarbageCollected<Coordinates>(
-        latitude, longitude, provides_altitude, altitude, accuracy,
-        provides_altitude_accuracy, altitude_accuracy, provides_heading,
-        heading, provides_speed, speed);
-  }
-
   Coordinates(double latitude,
               double longitude,
               bool provides_altitude,
diff --git a/third_party/blink/renderer/modules/geolocation/geo_notifier.cc b/third_party/blink/renderer/modules/geolocation/geo_notifier.cc
index 907b2bef..a7f2f6eb 100644
--- a/third_party/blink/renderer/modules/geolocation/geo_notifier.cc
+++ b/third_party/blink/renderer/modules/geolocation/geo_notifier.cc
@@ -22,7 +22,7 @@
       success_callback_(success_callback),
       error_callback_(error_callback),
       options_(options),
-      timer_(Timer::Create(
+      timer_(MakeGarbageCollected<Timer>(
           geolocation->GetDocument()->GetTaskRunner(TaskType::kMiscPlatformAPI),
           this,
           &GeoNotifier::TimerFired)),
@@ -132,8 +132,8 @@
 
   if (error_callback_) {
     error_callback_->InvokeAndReportException(
-        nullptr,
-        PositionError::Create(PositionError::kTimeout, "Timeout expired"));
+        nullptr, MakeGarbageCollected<PositionError>(PositionError::kTimeout,
+                                                     "Timeout expired"));
   }
 
   DEFINE_STATIC_LOCAL(CustomCountHistogram, timeout_expired_histogram,
diff --git a/third_party/blink/renderer/modules/geolocation/geo_notifier.h b/third_party/blink/renderer/modules/geolocation/geo_notifier.h
index beee26fe..a6a24e5 100644
--- a/third_party/blink/renderer/modules/geolocation/geo_notifier.h
+++ b/third_party/blink/renderer/modules/geolocation/geo_notifier.h
@@ -22,14 +22,6 @@
 class GeoNotifier final : public GarbageCollectedFinalized<GeoNotifier>,
                           public NameClient {
  public:
-  static GeoNotifier* Create(Geolocation* geolocation,
-                             V8PositionCallback* position_callback,
-                             V8PositionErrorCallback* position_error_callback,
-                             const PositionOptions* options) {
-    return MakeGarbageCollected<GeoNotifier>(geolocation, position_callback,
-                                             position_error_callback, options);
-  }
-
   GeoNotifier(Geolocation*,
               V8PositionCallback*,
               V8PositionErrorCallback*,
@@ -64,14 +56,6 @@
   // timer should be stopped beforehand.
   class Timer final : public GarbageCollectedFinalized<Timer> {
    public:
-    static Timer* Create(
-        scoped_refptr<base::SingleThreadTaskRunner> web_task_runner,
-        GeoNotifier* notifier,
-        void (GeoNotifier::*member_func)(TimerBase*)) {
-      return MakeGarbageCollected<Timer>(web_task_runner, notifier,
-                                         member_func);
-    }
-
     explicit Timer(scoped_refptr<base::SingleThreadTaskRunner> web_task_runner,
                    GeoNotifier* notifier,
                    void (GeoNotifier::*member_func)(TimerBase*))
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation.cc b/third_party/blink/renderer/modules/geolocation/geolocation.cc
index 5d6de494..c37d3cb1 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation.cc
+++ b/third_party/blink/renderer/modules/geolocation/geolocation.cc
@@ -56,15 +56,16 @@
 
 Geoposition* CreateGeoposition(
     const device::mojom::blink::Geoposition& position) {
-  Coordinates* coordinates = Coordinates::Create(
+  auto* coordinates = MakeGarbageCollected<Coordinates>(
       position.latitude, position.longitude,
       // Lowest point on land is at approximately -400 meters.
       position.altitude > -10000., position.altitude, position.accuracy,
       position.altitude_accuracy >= 0., position.altitude_accuracy,
       position.heading >= 0. && position.heading <= 360., position.heading,
       position.speed >= 0., position.speed);
-  return Geoposition::Create(coordinates, ConvertSecondsToDOMTimeStamp(
-                                              position.timestamp.ToDoubleT()));
+  return MakeGarbageCollected<Geoposition>(
+      coordinates,
+      ConvertSecondsToDOMTimeStamp(position.timestamp.ToDoubleT()));
 }
 
 PositionError* CreatePositionError(
@@ -83,7 +84,7 @@
       NOTREACHED();
       break;
   }
-  return PositionError::Create(error_code, error);
+  return MakeGarbageCollected<PositionError>(error_code, error);
 }
 
 static void ReportGeolocationViolation(Document* doc) {
@@ -185,8 +186,8 @@
 
   probe::BreakableLocation(GetDocument(), "Geolocation.getCurrentPosition");
 
-  GeoNotifier* notifier =
-      GeoNotifier::Create(this, success_callback, error_callback, options);
+  auto* notifier = MakeGarbageCollected<GeoNotifier>(this, success_callback,
+                                                     error_callback, options);
 
   one_shots_.insert(notifier);
 
@@ -201,8 +202,8 @@
 
   probe::BreakableLocation(GetDocument(), "Geolocation.watchPosition");
 
-  GeoNotifier* notifier =
-      GeoNotifier::Create(this, success_callback, error_callback, options);
+  auto* notifier = MakeGarbageCollected<GeoNotifier>(this, success_callback,
+                                                     error_callback, options);
 
   int watch_id;
   // Keep asking for the next id until we're given one that we don't already
@@ -221,8 +222,8 @@
   String error_message;
   if (!GetFrame()->GetSettings()->GetAllowGeolocationOnInsecureOrigins() &&
       !GetExecutionContext()->IsSecureContext(error_message)) {
-    notifier->SetFatalError(
-        PositionError::Create(PositionError::kPermissionDenied, error_message));
+    notifier->SetFatalError(MakeGarbageCollected<PositionError>(
+        PositionError::kPermissionDenied, error_message));
     return;
   }
 
@@ -231,7 +232,7 @@
           ReportOptions::kReportOnFailure, kFeaturePolicyConsoleWarning)) {
     UseCounter::Count(GetDocument(),
                       WebFeature::kGeolocationDisabledByFeaturePolicy);
-    notifier->SetFatalError(PositionError::Create(
+    notifier->SetFatalError(MakeGarbageCollected<PositionError>(
         PositionError::kPermissionDenied, kFeaturePolicyErrorMessage));
     return;
   }
@@ -509,8 +510,8 @@
   StopUpdating();
   // The only reason that we would fail to get a ConnectionError is if we lack
   // sufficient permission.
-  PositionError* error = PositionError::Create(PositionError::kPermissionDenied,
-                                               kPermissionDeniedErrorMessage);
+  auto* error = MakeGarbageCollected<PositionError>(
+      PositionError::kPermissionDenied, kPermissionDeniedErrorMessage);
   error->SetIsFatal(true);
   HandleError(error);
 }
diff --git a/third_party/blink/renderer/modules/geolocation/geolocation_error.h b/third_party/blink/renderer/modules/geolocation/geolocation_error.h
index da1cfdf..d27b7ab 100644
--- a/third_party/blink/renderer/modules/geolocation/geolocation_error.h
+++ b/third_party/blink/renderer/modules/geolocation/geolocation_error.h
@@ -34,10 +34,6 @@
  public:
   enum ErrorCode { kPermissionDenied, kPositionUnavailable };
 
-  static GeolocationError* Create(ErrorCode code, const String& message) {
-    return MakeGarbageCollected<GeolocationError>(code, message);
-  }
-
   GeolocationError(ErrorCode code, const String& message)
       : code_(code), message_(message) {}
 
diff --git a/third_party/blink/renderer/modules/geolocation/geoposition.h b/third_party/blink/renderer/modules/geolocation/geoposition.h
index 02b867b..9b39b62 100644
--- a/third_party/blink/renderer/modules/geolocation/geoposition.h
+++ b/third_party/blink/renderer/modules/geolocation/geoposition.h
@@ -39,10 +39,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static Geoposition* Create(Coordinates* coordinates, DOMTimeStamp timestamp) {
-    return MakeGarbageCollected<Geoposition>(coordinates, timestamp);
-  }
-
   Geoposition(Coordinates* coordinates, DOMTimeStamp timestamp)
       : coordinates_(coordinates), timestamp_(timestamp) {
     DCHECK(coordinates_);
diff --git a/third_party/blink/renderer/modules/geolocation/position_error.h b/third_party/blink/renderer/modules/geolocation/position_error.h
index ae30a3a..5aea482 100644
--- a/third_party/blink/renderer/modules/geolocation/position_error.h
+++ b/third_party/blink/renderer/modules/geolocation/position_error.h
@@ -42,10 +42,6 @@
     kTimeout = 3
   };
 
-  static PositionError* Create(ErrorCode code, const String& message) {
-    return MakeGarbageCollected<PositionError>(code, message);
-  }
-
   PositionError(ErrorCode code, const String& message)
       : code_(code), message_(message), is_fatal_(false) {}
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
index 47b3bd2..480a24e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -1542,7 +1542,7 @@
                           crypto_algorithm, &error)) {
     // Reject generateCertificate with the same error as was produced by
     // WebCrypto. |result| is garbage collected, no need to delete.
-    CryptoResultImpl* result = CryptoResultImpl::Create(script_state);
+    auto* result = MakeGarbageCollected<CryptoResultImpl>(script_state);
     ScriptPromise promise = result->Promise();
     result->CompleteWithError(error.error_type, error.error_details);
     return promise;
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.cc b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
index a410eae7..f8a9f63 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image.cc
@@ -34,6 +34,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
 #include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
+#include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
 #include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
 #include "third_party/blink/renderer/platform/graphics/image_observer.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
@@ -431,4 +432,11 @@
   ResetAnimation();
 }
 
+DarkModeClassification BitmapImage::ClassifyImageForDarkMode(
+    const FloatRect& src_rect) {
+  DarkModeImageClassifier dark_mode_image_classifier;
+  return dark_mode_image_classifier.ClassifyBitmapImageForDarkMode(*this,
+                                                                   src_rect);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image.h b/third_party/blink/renderer/platform/graphics/bitmap_image.h
index eb3d0f6..b99d1bb3 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image.h
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image.h
@@ -144,6 +144,9 @@
 
   int RepetitionCount();
 
+  DarkModeClassification ClassifyImageForDarkMode(
+      const FloatRect& src_rect) override;
+
   std::unique_ptr<DeferredImageDecoder> decoder_;
   mutable IntSize size_;  // The size to use for the overall image (will just
                           // be the size of the first image).
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc b/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc
index ae1ed8a3..51cef1c 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.cc
@@ -39,43 +39,23 @@
 DarkModeImageClassifier::DarkModeImageClassifier()
     : pixels_to_sample_(kPixelsToSample) {}
 
-bool DarkModeImageClassifier::ShouldApplyDarkModeFilterToImage(
+DarkModeClassification DarkModeImageClassifier::ClassifyBitmapImageForDarkMode(
     Image& image,
     const FloatRect& src_rect) {
-  DarkModeClassification result = image.GetDarkModeClassification(src_rect);
-  // Check if the image has already been classified.
-  if (result != DarkModeClassification::kNotClassified)
-    return result == DarkModeClassification::kApplyDarkModeFilter;
-
   std::vector<SkColor> sampled_pixels;
   if (src_rect.Width() < kMinImageSizeForClassification1D ||
-      src_rect.Height() < kMinImageSizeForClassification1D) {
-    result = DarkModeClassification::kApplyDarkModeFilter;
-  } else {
-    std::vector<float> features;
-    if (!ComputeImageFeatures(image, src_rect, &features, &sampled_pixels)) {
-      // TODO(v.paturi): Implement an SVG classifier which can decide if a
-      // filter should be applied based on the image's content and it's
-      // visibility on a dark background.
-      // Force this function to return true for any SVG image so that the
-      // filter will be set in the PaintFlags in GraphicsContext::DrawImage.
-      if (image.IsSVGImage() || image.IsSVGImageForContainer()) {
-        result = DarkModeClassification::kApplyDarkModeFilter;
-      } else {
-        result = DarkModeClassification::kDoNotApplyDarkModeFilter;
-      }
-    } else {
-      result = ClassifyImage(features);
-    }
+      src_rect.Height() < kMinImageSizeForClassification1D)
+    return DarkModeClassification::kApplyDarkModeFilter;
+
+  std::vector<float> features;
+  if (!ComputeImageFeatures(image, src_rect, &features, &sampled_pixels)) {
+    // TODO(https://crbug.com/945434): Do not cache the classification when
+    // the correct resource is not loaded
+    image.SetShouldCacheDarkModeClassification(sampled_pixels.size() != 0);
+    return DarkModeClassification::kDoNotApplyDarkModeFilter;
   }
 
-  // Store the classification result in the image object using src_rect's
-  // location as a key for the map.
-  // TODO(https://crbug.com/945434): Do not cache the classification when
-  // the correct resource is not loaded
-  if (sampled_pixels.size() != 0)
-    image.AddDarkModeClassification(src_rect, result);
-  return result == DarkModeClassification::kApplyDarkModeFilter;
+  return ClassifyImage(features);
 }
 
 // This function computes a single feature vector based on a sample set of image
@@ -108,7 +88,8 @@
 bool DarkModeImageClassifier::GetBitmap(Image& image,
                                         const FloatRect& src_rect,
                                         SkBitmap* bitmap) {
-  if (!image.IsBitmapImage() || !src_rect.Width() || !src_rect.Height())
+  DCHECK(image.IsBitmapImage());
+  if (!src_rect.Width() || !src_rect.Height())
     return false;
 
   SkScalar sx = SkFloatToScalar(src_rect.X());
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h b/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h
index 6712493..5f2bbb4 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h
@@ -23,13 +23,9 @@
   DarkModeImageClassifier();
   ~DarkModeImageClassifier() = default;
 
-  // Decides if a dark mode filter should be applied to the image or not.
-  // |src_rect| is needed in case of image sprites for the location and
-  // size of the smaller images that the sprite holds.
-  // For images that come from sprites the |src_rect.X| and |src_rect.Y|
-  // can be non-zero. But for normal images they are both zero.
-  bool ShouldApplyDarkModeFilterToImage(Image& image,
-                                        const FloatRect& src_rect);
+  DarkModeClassification ClassifyBitmapImageForDarkMode(
+      Image& image,
+      const FloatRect& src_rect);
 
   bool ComputeImageFeaturesForTesting(Image& image,
                                       std::vector<float>* features) {
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc b/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc
index bdd0ab8..66c0932 100644
--- a/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc
+++ b/third_party/blink/renderer/platform/graphics/dark_mode_image_classifier_test.cc
@@ -26,6 +26,16 @@
 
   int GetMapSize() { return dark_mode_classifications_.size(); }
 
+  DarkModeClassification GetClassification(const FloatRect& src_rect) {
+    return GetDarkModeClassification(src_rect);
+  }
+
+  void AddClassification(
+      const FloatRect& src_rect,
+      const DarkModeClassification dark_mode_classification) {
+    AddDarkModeClassification(src_rect, dark_mode_classification);
+  }
+
   // Pure virtual functions that have to be overridden.
   bool CurrentFrameKnownToBeOpaque() override { return false; }
   IntSize Size() const override { return IntSize(0, 0); }
@@ -49,8 +59,9 @@
     SCOPED_TRACE(file_name);
     scoped_refptr<BitmapImage> image = LoadImage(file_name);
     classifier_.ComputeImageFeaturesForTesting(*image.get(), features);
-    return classifier_.ShouldApplyDarkModeFilterToImage(
+    DarkModeClassification result = classifier_.ClassifyBitmapImageForDarkMode(
         *image.get(), FloatRect(0, 0, image->width(), image->height()));
+    return result == DarkModeClassification::kApplyDarkModeFilter;
   }
 
   void AssertFeaturesEqual(const std::vector<float>& features,
@@ -145,25 +156,25 @@
   FloatRect src_rect2(5, 20, 100, 100);
   FloatRect src_rect3(6, -9, 50, 50);
 
-  EXPECT_EQ(image->GetDarkModeClassification(src_rect1),
+  EXPECT_EQ(image->GetClassification(src_rect1),
             DarkModeClassification::kNotClassified);
-  image->AddDarkModeClassification(
-      src_rect1, DarkModeClassification::kApplyDarkModeFilter);
-  EXPECT_EQ(image->GetDarkModeClassification(src_rect1),
+  image->AddClassification(src_rect1,
+                           DarkModeClassification::kApplyDarkModeFilter);
+  EXPECT_EQ(image->GetClassification(src_rect1),
             DarkModeClassification::kApplyDarkModeFilter);
 
-  EXPECT_EQ(image->GetDarkModeClassification(src_rect2),
+  EXPECT_EQ(image->GetClassification(src_rect2),
             DarkModeClassification::kNotClassified);
-  image->AddDarkModeClassification(
-      src_rect2, DarkModeClassification::kDoNotApplyDarkModeFilter);
-  EXPECT_EQ(image->GetDarkModeClassification(src_rect2),
+  image->AddClassification(src_rect2,
+                           DarkModeClassification::kDoNotApplyDarkModeFilter);
+  EXPECT_EQ(image->GetClassification(src_rect2),
             DarkModeClassification::kDoNotApplyDarkModeFilter);
 
-  EXPECT_EQ(image->GetDarkModeClassification(src_rect3),
+  EXPECT_EQ(image->GetClassification(src_rect3),
             DarkModeClassification::kNotClassified);
-  image->AddDarkModeClassification(
-      src_rect3, DarkModeClassification::kApplyDarkModeFilter);
-  EXPECT_EQ(image->GetDarkModeClassification(src_rect3),
+  image->AddClassification(src_rect3,
+                           DarkModeClassification::kApplyDarkModeFilter);
+  EXPECT_EQ(image->GetClassification(src_rect3),
             DarkModeClassification::kApplyDarkModeFilter);
 
   EXPECT_EQ(image->GetMapSize(), 3);
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index e11034e..2adf55d 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -1422,8 +1422,7 @@
 
   switch (dark_mode_settings_.image_policy) {
     case DarkModeImagePolicy::kFilterSmart:
-      return dark_mode_image_classifier_.ShouldApplyDarkModeFilterToImage(
-          image, src_rect);
+      return image.ShouldApplyDarkModeFilter(src_rect);
     case DarkModeImagePolicy::kFilterAll:
       return true;
     default:
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h
index 8c28c928..bc9f657 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -33,7 +33,6 @@
 #include "base/macros.h"
 #include "cc/paint/node_holder.h"
 #include "third_party/blink/renderer/platform/fonts/font.h"
-#include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
 #include "third_party/blink/renderer/platform/graphics/dark_mode_settings.h"
 #include "third_party/blink/renderer/platform/graphics/dash_array.h"
 #include "third_party/blink/renderer/platform/graphics/draw_looper_builder.h"
@@ -504,7 +503,6 @@
 
   DarkModeSettings dark_mode_settings_;
   sk_sp<SkColorFilter> dark_mode_filter_;
-  DarkModeImageClassifier dark_mode_image_classifier_;
 
   unsigned printing_ : 1;
   unsigned in_drawing_recorder_ : 1;
diff --git a/third_party/blink/renderer/platform/graphics/image.cc b/third_party/blink/renderer/platform/graphics/image.cc
index c7925e9..0390423 100644
--- a/third_party/blink/renderer/platform/graphics/image.cc
+++ b/third_party/blink/renderer/platform/graphics/image.cc
@@ -393,4 +393,20 @@
   dark_mode_classifications_[key] = dark_mode_classification;
 }
 
+bool Image::ShouldApplyDarkModeFilter(const FloatRect& src_rect) {
+  // Check if the image has already been classified.
+  DarkModeClassification result = GetDarkModeClassification(src_rect);
+  if (result != DarkModeClassification::kNotClassified)
+    return result == DarkModeClassification::kApplyDarkModeFilter;
+
+  result = ClassifyImageForDarkMode(src_rect);
+
+  // Store the classification result using src_rect's location
+  // as a key for the map.
+  if (ShouldCacheDarkModeClassification())
+    AddDarkModeClassification(src_rect, result);
+
+  return result == DarkModeClassification::kApplyDarkModeFilter;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/image.h b/third_party/blink/renderer/platform/graphics/image.h
index c6738b7e..75394d5 100644
--- a/third_party/blink/renderer/platform/graphics/image.h
+++ b/third_party/blink/renderer/platform/graphics/image.h
@@ -88,11 +88,6 @@
       InterpolationQuality = kInterpolationNone);
 
   virtual bool IsSVGImage() const { return false; }
-  // TODO(v.paturi): Remove this function once
-  // |ShouldApplyDarkModeFilterToImage| is refactored.
-  // It's suggested that this function not be used anywhere
-  // else in the mean time.
-  virtual bool IsSVGImageForContainer() const { return false; }
   virtual bool IsBitmapImage() const { return false; }
   virtual bool IsStaticBitmapImage() const { return false; }
   virtual bool IsPlaceholderImage() const { return false; }
@@ -241,13 +236,20 @@
     return nullptr;
   }
 
-  DarkModeClassification GetDarkModeClassification(const FloatRect& src_rect);
+  void SetShouldCacheDarkModeClassification(bool should_cache_result) {
+    should_cache_dark_mode_classification_ = should_cache_result;
+  }
 
-  // Dark mode classification result is cached to be consistent and have
-  // higher performance for future paints.
-  void AddDarkModeClassification(
-      const FloatRect& src_rect,
-      const DarkModeClassification dark_mode_classification);
+  bool ShouldCacheDarkModeClassification() {
+    return should_cache_dark_mode_classification_;
+  }
+
+  // Decides if a dark mode filter should be applied to the image or not.
+  // |src_rect| is needed in case of image sprites for the location and
+  // size of the smaller images that the sprite holds.
+  // For images that come from sprites the |src_rect.X| and |src_rect.Y|
+  // can be non-zero. But for other images they are both zero.
+  bool ShouldApplyDarkModeFilter(const FloatRect& src_rect);
 
   PaintImage::Id paint_image_id() const { return stable_image_id_; }
 
@@ -272,11 +274,24 @@
   // Whether or not size is available yet.
   virtual bool IsSizeAvailable() { return true; }
 
+  DarkModeClassification GetDarkModeClassification(const FloatRect& src_rect);
+
+  // Dark mode classification result is cached to be consistent and have
+  // higher performance for future paints.
+  void AddDarkModeClassification(
+      const FloatRect& src_rect,
+      const DarkModeClassification dark_mode_classification);
+
   typedef std::pair<float, float> ClassificationKey;
   std::map<ClassificationKey, DarkModeClassification>
       dark_mode_classifications_;
 
  private:
+  virtual DarkModeClassification ClassifyImageForDarkMode(
+      const FloatRect& src_rect) {
+    return DarkModeClassification::kDoNotApplyDarkModeFilter;
+  }
+
   bool image_observer_disabled_;
   scoped_refptr<SharedBuffer> encoded_image_data_;
   // TODO(Oilpan): consider having Image on the Oilpan heap and
@@ -289,6 +304,7 @@
   WeakPersistent<ImageObserver> image_observer_;
   PaintImage::Id stable_image_id_;
   const bool is_multipart_;
+  bool should_cache_dark_mode_classification_ = true;
   DISALLOW_COPY_AND_ASSIGN(Image);
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc
index 71a24f2..92bc9e3 100644
--- a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc
+++ b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.cc
@@ -6,12 +6,31 @@
 
 #include <utility>
 
+#include "base/synchronization/waitable_event.h"
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_record.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 
 namespace blink {
 
+namespace {
+class AutoSignal {
+ public:
+  explicit AutoSignal(base::WaitableEvent* event) : event_(event) {
+    DCHECK(event);
+  }
+  ~AutoSignal() { event_->Signal(); }
+
+ private:
+  base::WaitableEvent* event_;
+
+  DISALLOW_COPY_AND_ASSIGN(AutoSignal);
+};
+}  // namespace
+
 // static
 std::unique_ptr<PlatformPaintWorkletLayerPainter>
 PaintWorkletPaintDispatcher::CreateCompositorThreadPainter(
@@ -31,6 +50,7 @@
                "PaintWorkletPaintDispatcher::RegisterPaintWorkletPainter");
 
   DCHECK(painter);
+  MutexLocker lock(painter_map_mutex_);
   DCHECK(painter_map_.find(painter) == painter_map_.end());
   painter_map_.insert(painter, painter_runner);
 }
@@ -41,6 +61,7 @@
                "PaintWorkletPaintDispatcher::"
                "UnregisterPaintWorkletPainter");
   DCHECK(painter);
+  MutexLocker lock(painter_map_mutex_);
   DCHECK(painter_map_.find(painter) != painter_map_.end());
   painter_map_.erase(painter);
 }
@@ -52,7 +73,43 @@
     cc::PaintWorkletInput* input) {
   TRACE_EVENT0("cc", "PaintWorkletPaintDispatcher::Paint");
   sk_sp<cc::PaintRecord> output = sk_make_sp<cc::PaintOpBuffer>();
-  // TODO(xidachen): call the actual paint function in PaintWorkletProxyClient.
+
+  PaintWorkletPainterToTaskRunnerMap copied_painter_map;
+  {
+    MutexLocker lock(painter_map_mutex_);
+    if (painter_map_.IsEmpty())
+      return output;
+    // TODO(xidachen): copying is a temporary solution to prevent deadlock. It
+    // should be automatically solved with CC work.
+    copied_painter_map = painter_map_;
+  }
+
+  base::WaitableEvent done_event;
+
+  for (auto& pair : copied_painter_map) {
+    if (pair.key->GetWorkletId() != input->WorkletId())
+      continue;
+    PaintWorkletPainter* painter = pair.key;
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner = pair.value;
+
+    DCHECK(!task_runner->BelongsToCurrentThread());
+    std::unique_ptr<AutoSignal> done =
+        std::make_unique<AutoSignal>(&done_event);
+
+    PostCrossThreadTask(
+        *task_runner, FROM_HERE,
+        CrossThreadBind(
+            [](PaintWorkletPainter* painter, cc::PaintWorkletInput* input,
+               std::unique_ptr<AutoSignal> completion,
+               sk_sp<cc::PaintRecord>* output) {
+              *output = painter->Paint(input);
+            },
+            WrapCrossThreadPersistent(painter), CrossThreadUnretained(input),
+            WTF::Passed(std::move(done)), CrossThreadUnretained(&output)));
+  }
+
+  done_event.Wait();
+
   return output;
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h
index a86eaeb..6534033 100644
--- a/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h
+++ b/third_party/blink/renderer/platform/graphics/paint_worklet_paint_dispatcher.h
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
 
 namespace blink {
 
@@ -51,6 +52,11 @@
 
   PaintWorkletPainterToTaskRunnerMap painter_map_;
 
+  // The (Un)registerPaintWorkletPainter comes from the worklet thread, and the
+  // Paint call is initiated from the raster threads, this mutex ensures that
+  // accessing / updating the |painter_map_| is thread safe.
+  Mutex painter_map_mutex_;
+
   DISALLOW_COPY_AND_ASSIGN(PaintWorkletPaintDispatcher);
 };
 
diff --git a/third_party/blink/renderer/platform/graphics/paint_worklet_painter.h b/third_party/blink/renderer/platform/graphics/paint_worklet_painter.h
index d900eba3..23aeb4b 100644
--- a/third_party/blink/renderer/platform/graphics/paint_worklet_painter.h
+++ b/third_party/blink/renderer/platform/graphics/paint_worklet_painter.h
@@ -29,6 +29,9 @@
 class PLATFORM_EXPORT PaintWorkletPainter : public GarbageCollectedMixin {
  public:
   virtual ~PaintWorkletPainter() = default;
+
+  virtual int GetWorkletId() const = 0;
+  virtual sk_sp<PaintRecord> Paint(cc::PaintWorkletInput*) = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h b/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h
index 53ea8e8..4cc7a0c6 100644
--- a/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h
+++ b/third_party/blink/renderer/platform/graphics/platform_paint_worklet_layer_painter.h
@@ -13,6 +13,8 @@
 
 namespace blink {
 
+using CompositorPaintWorkletInput = cc::PaintWorkletInput;
+
 class PaintWorkletPaintDispatcher;
 
 // This class serves as a bridge which connects the compositor and the paint
diff --git a/third_party/blink/renderer/platform/heap/blink_gc.h b/third_party/blink/renderer/platform/heap/blink_gc.h
index 59deeea..f1cda113 100644
--- a/third_party/blink/renderer/platform/heap/blink_gc.h
+++ b/third_party/blink/renderer/platform/heap/blink_gc.h
@@ -92,11 +92,11 @@
     // kIdleGC = 0,
     kPreciseGC = 1,
     kConservativeGC = 2,
-    kForcedGC = 3,
+    kForcedGCForTesting = 3,
     kMemoryPressureGC = 4,
     kPageNavigationGC = 5,
     kThreadTerminationGC = 6,
-    kTesting = 7,
+    // kTesting = 7,
     // kIncrementalIdleGC = 8,
     kIncrementalV8FollowupGC = 9,
     kUnifiedHeapGC = 10,
diff --git a/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.cc b/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.cc
index deae8e9..e8ef8c4e 100644
--- a/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.cc
+++ b/third_party/blink/renderer/platform/heap/blink_gc_memory_dump_provider.cc
@@ -52,7 +52,7 @@
   if (level_of_detail == MemoryDumpLevelOfDetail::DETAILED) {
     ThreadState::Current()->CollectGarbage(
         BlinkGC::kNoHeapPointersOnStack, BlinkGC::kTakeSnapshot,
-        BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+        BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGCForTesting);
   }
   DumpMemoryTotals(memory_dump);
 
diff --git a/third_party/blink/renderer/platform/heap/heap_compact.cc b/third_party/blink/renderer/platform/heap/heap_compact.cc
index d1118188..8766f7f 100644
--- a/third_party/blink/renderer/platform/heap/heap_compact.cc
+++ b/third_party/blink/renderer/platform/heap/heap_compact.cc
@@ -374,13 +374,12 @@
   if (stack_state == BlinkGC::kHeapPointersOnStack)
     return false;
 
-  if (reason == BlinkGC::GCReason::kTesting) {
+  if (reason == BlinkGC::GCReason::kForcedGCForTesting) {
     UpdateHeapResidency();
     return force_compaction_gc_;
   }
 
-  if (reason != BlinkGC::GCReason::kPreciseGC &&
-      reason != BlinkGC::GCReason::kForcedGC)
+  if (reason != BlinkGC::GCReason::kPreciseGC)
     return false;
 
   // TODO(keishi): crbug.com/918064 Heap compaction for incremental marking
diff --git a/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc b/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
index a80200e..f92147b 100644
--- a/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_stats_collector_test.cc
@@ -14,7 +14,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, InitialEmpty) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   for (int i = 0; i < ThreadHeapStatsCollector::kNumScopeIds; i++) {
     EXPECT_EQ(TimeDelta(), stats_collector.current().scope_data[i]);
   }
@@ -24,7 +24,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, IncreaseScopeTime) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kIncrementalMarkingStep,
       TimeDelta::FromMilliseconds(1));
@@ -37,7 +37,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, StopMovesCurrentToPrevious) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kIncrementalMarkingStep,
       TimeDelta::FromMilliseconds(1));
@@ -50,7 +50,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, StopResetsCurrent) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kIncrementalMarkingStep,
       TimeDelta::FromMilliseconds(1));
@@ -64,7 +64,7 @@
 TEST(ThreadHeapStatsCollectorTest, StartStop) {
   ThreadHeapStatsCollector stats_collector;
   EXPECT_FALSE(stats_collector.is_started());
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_TRUE(stats_collector.is_started());
   stats_collector.NotifyMarkingCompleted();
   stats_collector.NotifySweepingCompleted();
@@ -79,16 +79,17 @@
 
 TEST(ThreadHeapStatsCollectorTest, UpdateReason) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
-  stats_collector.UpdateReason(BlinkGC::GCReason::kForcedGC);
+  stats_collector.UpdateReason(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifySweepingCompleted();
-  EXPECT_EQ(BlinkGC::GCReason::kForcedGC, stats_collector.previous().reason);
+  EXPECT_EQ(BlinkGC::GCReason::kForcedGCForTesting,
+            stats_collector.previous().reason);
 }
 
 TEST(ThreadHeapStatsCollectorTest, InitialEstimatedObjectSizeInBytes) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_EQ(0u, stats_collector.object_size_in_bytes());
   stats_collector.NotifyMarkingCompleted();
   stats_collector.NotifySweepingCompleted();
@@ -96,7 +97,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeInBytesNoMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseAllocatedObjectSize(512);
   EXPECT_EQ(512u, stats_collector.object_size_in_bytes());
   stats_collector.NotifyMarkingCompleted();
@@ -105,11 +106,11 @@
 
 TEST(ThreadHeapStatsCollectorTest, EstimatedObjectSizeInBytesWithMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseAllocatedObjectSize(512);
   EXPECT_EQ(640u, stats_collector.object_size_in_bytes());
@@ -119,11 +120,11 @@
 TEST(ThreadHeapStatsCollectorTest,
      EstimatedObjectSizeInBytesDoNotCountCurrentlyMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   // Currently marked bytes should not account to the estimated object size.
@@ -136,7 +137,7 @@
   // Checks that a marking time estimate can be retrieved before the first
   // garbage collection triggers.
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_LT(0u, stats_collector.estimated_marking_time_in_seconds());
   stats_collector.NotifyMarkingCompleted();
   stats_collector.NotifySweepingCompleted();
@@ -144,13 +145,13 @@
 
 TEST(ThreadHeapStatsCollectorTest, EstimatedMarkingTime1) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kAtomicPhaseMarking, TimeDelta::FromSeconds(1));
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(1024);
   stats_collector.NotifySweepingCompleted();
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_DOUBLE_EQ(1.0, stats_collector.estimated_marking_time_in_seconds());
   stats_collector.NotifyMarkingCompleted();
   stats_collector.NotifySweepingCompleted();
@@ -158,13 +159,13 @@
 
 TEST(ThreadHeapStatsCollectorTest, EstimatedMarkingTime2) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kAtomicPhaseMarking, TimeDelta::FromSeconds(1));
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(1024);
   stats_collector.NotifySweepingCompleted();
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseAllocatedObjectSize(512);
   EXPECT_DOUBLE_EQ(1.5, stats_collector.estimated_marking_time_in_seconds());
   stats_collector.NotifyMarkingCompleted();
@@ -174,7 +175,7 @@
 TEST(ThreadHeapStatsCollectorTest, AllocatedSpaceInBytesInitialZero) {
   ThreadHeapStatsCollector stats_collector;
   EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
   stats_collector.NotifyMarkingCompleted();
   EXPECT_EQ(0u, stats_collector.allocated_space_bytes());
@@ -201,7 +202,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventPrevGCMarkedObjectSize) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(1024);
   stats_collector.NotifySweepingCompleted();
@@ -210,7 +211,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeInMsFromIncrementalGC) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kIncrementalMarkingStartMarking,
       TimeDelta::FromMilliseconds(7));
@@ -231,7 +232,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventMarkingTimeInMsFromFullGC) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kAtomicPhaseMarking,
       TimeDelta::FromMilliseconds(11));
@@ -242,7 +243,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventMarkingTimePerByteInS) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseMarkedObjectSize(1000);
   stats_collector.IncreaseScopeTime(
       ThreadHeapStatsCollector::kAtomicPhaseMarking, TimeDelta::FromSeconds(1));
@@ -254,7 +255,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventSweepingTimeInMs) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kLazySweepInIdle,
                                     TimeDelta::FromMilliseconds(1));
   stats_collector.IncreaseScopeTime(ThreadHeapStatsCollector::kLazySweepInIdle,
@@ -274,7 +275,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedBytes) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseCompactionFreedSize(512);
   stats_collector.NotifySweepingCompleted();
@@ -283,7 +284,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventCompactionFreedPages) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseCompactionFreedPages(3);
   stats_collector.NotifySweepingCompleted();
@@ -292,7 +293,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventInitialEstimatedLiveObjectRate) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(0.0, stats_collector.previous().live_object_rate);
@@ -301,11 +302,11 @@
 TEST(ThreadHeapStatsCollectorTest,
      EventEstimatedLiveObjectRateSameMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
@@ -315,11 +316,11 @@
 TEST(ThreadHeapStatsCollectorTest,
      EventEstimatedLiveObjectRateHalfMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(256);
   stats_collector.NotifySweepingCompleted();
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
@@ -328,11 +329,11 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventEstimatedLiveObjectRateNoMarkedBytes) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(256);
   stats_collector.NotifySweepingCompleted();
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(0.0, stats_collector.previous().live_object_rate);
 }
@@ -340,12 +341,12 @@
 TEST(ThreadHeapStatsCollectorTest,
      EventEstimatedLiveObjectRateWithAllocatedBytes1) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
   stats_collector.IncreaseAllocatedObjectSize(128);
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
@@ -355,11 +356,11 @@
 TEST(ThreadHeapStatsCollectorTest,
      EventEstimatedLiveObjectRateWithAllocatedBytes2) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.NotifySweepingCompleted();
   stats_collector.IncreaseAllocatedObjectSize(128);
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
@@ -369,7 +370,7 @@
 TEST(ThreadHeapStatsCollectorTest,
      EventEstimatedLiveObjectRateWithAllocatedBytes3) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(0, stats_collector.previous().live_object_rate);
@@ -378,11 +379,11 @@
 TEST(ThreadHeapStatsCollectorTest,
      EventEstimatedLiveObjectRateWithAllocatedBytes4) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseMarkedObjectSize(128);
   stats_collector.NotifySweepingCompleted();
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.NotifySweepingCompleted();
   EXPECT_DOUBLE_EQ(0, stats_collector.previous().live_object_rate);
@@ -390,7 +391,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventAllocatedSpaceBeforeSweeping1) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseAllocatedSpace(1024);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.IncreaseAllocatedSpace(2048);
@@ -402,7 +403,7 @@
 
 TEST(ThreadHeapStatsCollectorTest, EventAllocatedSpaceBeforeSweeping2) {
   ThreadHeapStatsCollector stats_collector;
-  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kTesting);
+  stats_collector.NotifyMarkingStarted(BlinkGC::GCReason::kForcedGCForTesting);
   stats_collector.IncreaseAllocatedSpace(1024);
   stats_collector.NotifyMarkingCompleted();
   stats_collector.DecreaseAllocatedSpace(1024);
diff --git a/third_party/blink/renderer/platform/heap/heap_test.cc b/third_party/blink/renderer/platform/heap/heap_test.cc
index 285f8b18..422a25d 100644
--- a/third_party/blink/renderer/platform/heap/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test.cc
@@ -377,7 +377,7 @@
       : TestGCCollectGarbageScope(state),
         atomic_pause_scope_(ThreadState::Current()) {
     ThreadState::Current()->Heap().stats_collector()->NotifyMarkingStarted(
-        BlinkGC::GCReason::kTesting);
+        BlinkGC::GCReason::kForcedGCForTesting);
     ThreadState::Current()->AtomicPausePrologue(state, BlinkGC::kAtomicMarking,
                                                 BlinkGC::GCReason::kPreciseGC);
   }
@@ -1876,7 +1876,7 @@
     MakeGarbageCollected<SimpleFinalizedObject>();
   ThreadState::Current()->CollectGarbage(
       BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGC);
+      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
   for (int i = 0; i < 10000; i++)
     MakeGarbageCollected<SimpleFinalizedObject>();
@@ -1904,7 +1904,7 @@
     MakeGarbageCollected<LargeHeapObject>();
   ThreadState::Current()->CollectGarbage(
       BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGC);
+      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_EQ(0, LargeHeapObject::destructor_calls_);
   for (int i = 0; i < 10; i++) {
     MakeGarbageCollected<LargeHeapObject>();
@@ -1915,7 +1915,7 @@
   EXPECT_EQ(10, LargeHeapObject::destructor_calls_);
   ThreadState::Current()->CollectGarbage(
       BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGC);
+      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_EQ(10, LargeHeapObject::destructor_calls_);
   PreciselyCollectGarbage();
   EXPECT_EQ(22, LargeHeapObject::destructor_calls_);
@@ -1978,7 +1978,7 @@
     MakeGarbageCollected<SimpleFinalizedObjectInstanceOfTemplate>();
   ThreadState::Current()->CollectGarbage(
       BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGC);
+      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGCForTesting);
   EXPECT_EQ(0, SimpleFinalizedObject::destructor_calls_);
   EXPECT_EQ(100, SimpleFinalizedEagerObject::destructor_calls_);
   EXPECT_EQ(100, SimpleFinalizedObjectInstanceOfTemplate::destructor_calls_);
diff --git a/third_party/blink/renderer/platform/heap/heap_test_utilities.cc b/third_party/blink/renderer/platform/heap/heap_test_utilities.cc
index 1d01f7b..2fb98c3 100644
--- a/third_party/blink/renderer/platform/heap/heap_test_utilities.cc
+++ b/third_party/blink/renderer/platform/heap/heap_test_utilities.cc
@@ -13,13 +13,13 @@
 void PreciselyCollectGarbage() {
   ThreadState::Current()->CollectGarbage(
       BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGCForTesting);
 }
 
 void ConservativelyCollectGarbage(BlinkGC::SweepingType sweeping_type) {
-  ThreadState::Current()->CollectGarbage(BlinkGC::kHeapPointersOnStack,
-                                         BlinkGC::kAtomicMarking, sweeping_type,
-                                         BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectGarbage(
+      BlinkGC::kHeapPointersOnStack, BlinkGC::kAtomicMarking, sweeping_type,
+      BlinkGC::GCReason::kForcedGCForTesting);
 }
 
 // Do several GCs to make sure that later GCs don't free up old memory from
diff --git a/third_party/blink/renderer/platform/heap/heap_thread_test.cc b/third_party/blink/renderer/platform/heap/heap_thread_test.cc
index f5d687b6..9d6facd 100644
--- a/third_party/blink/renderer/platform/heap/heap_thread_test.cc
+++ b/third_party/blink/renderer/platform/heap/heap_thread_test.cc
@@ -254,7 +254,7 @@
     // Step 4: Run a GC.
     ThreadState::Current()->CollectGarbage(
         BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-        BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+        BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGCForTesting);
     SwitchToMainThread();
   }
 
diff --git a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
index 7abd302..b947057 100644
--- a/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
+++ b/third_party/blink/renderer/platform/heap/incremental_marking_test.cc
@@ -1542,7 +1542,8 @@
   }
 
   void Start() {
-    thread_state_->IncrementalMarkingStart(BlinkGC::GCReason::kTesting);
+    thread_state_->IncrementalMarkingStart(
+        BlinkGC::GCReason::kForcedGCForTesting);
   }
 
   bool SingleStep(BlinkGC::StackState stack_state =
diff --git a/third_party/blink/renderer/platform/heap/thread_state.cc b/third_party/blink/renderer/platform/heap/thread_state.cc
index c0b40b37..bc8242b 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state.cc
@@ -105,16 +105,14 @@
       return "PreciseGC";
     case BlinkGC::GCReason::kConservativeGC:
       return "ConservativeGC";
-    case BlinkGC::GCReason::kForcedGC:
-      return "ForcedGC";
+    case BlinkGC::GCReason::kForcedGCForTesting:
+      return "ForcedGCForTesting";
     case BlinkGC::GCReason::kMemoryPressureGC:
       return "MemoryPressureGC";
     case BlinkGC::GCReason::kPageNavigationGC:
       return "PageNavigationGC";
     case BlinkGC::GCReason::kThreadTerminationGC:
       return "ThreadTerminationGC";
-    case BlinkGC::GCReason::kTesting:
-      return "TestingGC";
     case BlinkGC::GCReason::kIncrementalV8FollowupGC:
       return "IncrementalV8FollowupGC";
     case BlinkGC::GCReason::kUnifiedHeapGC:
@@ -171,7 +169,7 @@
       end_of_stack_(reinterpret_cast<intptr_t*>(WTF::GetStackStart())),
       gc_state_(kNoGCScheduled),
       gc_phase_(GCPhase::kNone),
-      reason_for_scheduled_gc_(BlinkGC::GCReason::kMaxValue),
+      reason_for_scheduled_gc_(BlinkGC::GCReason::kForcedGCForTesting),
 #if defined(ADDRESS_SANITIZER)
       asan_fake_stack_(__asan_get_current_fake_stack()),
 #endif
@@ -638,7 +636,7 @@
       RuntimeEnabledFeatures::HeapIncrementalMarkingStressEnabled()) {
     VLOG(2) << "[state:" << this << "] "
             << "ScheduleGCIfNeeded: Scheduled incremental marking for testing";
-    IncrementalMarkingStart(BlinkGC::GCReason::kTesting);
+    IncrementalMarkingStart(BlinkGC::GCReason::kForcedGCForTesting);
     return;
   }
 }
@@ -953,7 +951,8 @@
     SweepForbiddenScope scope(this);
     ThreadHeapStatsCollector::EnabledScope stats_scope(
         Heap().stats_collector(), ThreadHeapStatsCollector::kCompleteSweep,
-        "forced", current_gc_data_.reason == BlinkGC::GCReason::kForcedGC);
+        "forced",
+        current_gc_data_.reason == BlinkGC::GCReason::kForcedGCForTesting);
     Heap().CompleteSweep();
     if (!was_in_atomic_pause)
       LeaveAtomicPause();
@@ -1156,11 +1155,10 @@
     // COUNT_BY_GC_REASON(IdleGC)
     COUNT_BY_GC_REASON(PreciseGC)
     COUNT_BY_GC_REASON(ConservativeGC)
-    COUNT_BY_GC_REASON(ForcedGC)
+    COUNT_BY_GC_REASON(ForcedGCForTesting)
     COUNT_BY_GC_REASON(MemoryPressureGC)
     COUNT_BY_GC_REASON(PageNavigationGC)
     COUNT_BY_GC_REASON(ThreadTerminationGC)
-    COUNT_BY_GC_REASON(Testing)
     COUNT_BY_GC_REASON(IncrementalV8FollowupGC)
     COUNT_BY_GC_REASON(UnifiedHeapGC)
 
@@ -1464,7 +1462,8 @@
   // mentioned below. In this case we will follow up with a regular full
   // garbage collection.
   const bool should_do_full_gc =
-      !was_incremental_marking || reason == BlinkGC::GCReason::kForcedGC ||
+      !was_incremental_marking ||
+      reason == BlinkGC::GCReason::kForcedGCForTesting ||
       reason == BlinkGC::GCReason::kMemoryPressureGC ||
       reason == BlinkGC::GCReason::kThreadTerminationGC;
   if (should_do_full_gc) {
@@ -1489,11 +1488,10 @@
   switch (reason) {
     COUNT_BY_GC_REASON(PreciseGC)
     COUNT_BY_GC_REASON(ConservativeGC)
-    COUNT_BY_GC_REASON(ForcedGC)
+    COUNT_BY_GC_REASON(ForcedGCForTesting)
     COUNT_BY_GC_REASON(MemoryPressureGC)
     COUNT_BY_GC_REASON(PageNavigationGC)
     COUNT_BY_GC_REASON(ThreadTerminationGC)
-    COUNT_BY_GC_REASON(Testing)
     COUNT_BY_GC_REASON(IncrementalV8FollowupGC)
     COUNT_BY_GC_REASON(UnifiedHeapGC)
   }
@@ -1553,7 +1551,7 @@
   {
     ThreadHeapStatsCollector::DevToolsScope stats1(
         Heap().stats_collector(), ThreadHeapStatsCollector::kAtomicPhase,
-        "forced", reason == BlinkGC::GCReason::kForcedGC);
+        "forced", reason == BlinkGC::GCReason::kForcedGCForTesting);
     {
       AtomicPauseScope atomic_pause_scope(this);
       ThreadHeapStatsCollector::EnabledScope stats2(
@@ -1737,12 +1735,13 @@
   Heap().VerifyMarking();
 }
 
-void ThreadState::CollectAllGarbageForTesting() {
+void ThreadState::CollectAllGarbageForTesting(BlinkGC::StackState stack_state) {
   // We need to run multiple GCs to collect a chain of persistent handles.
   size_t previous_live_objects = 0;
   for (int i = 0; i < 5; ++i) {
-    CollectGarbage(BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-                   BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+    CollectGarbage(stack_state, BlinkGC::kAtomicMarking,
+                   BlinkGC::kEagerSweeping,
+                   BlinkGC::GCReason::kForcedGCForTesting);
     const size_t live_objects =
         Heap().stats_collector()->previous().marked_bytes;
     if (live_objects == previous_live_objects)
diff --git a/third_party/blink/renderer/platform/heap/thread_state.h b/third_party/blink/renderer/platform/heap/thread_state.h
index b9cadd1..4c91b5d 100644
--- a/third_party/blink/renderer/platform/heap/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/thread_state.h
@@ -375,11 +375,16 @@
 
   v8::Isolate* GetIsolate() const { return isolate_; }
 
+  // Use CollectAllGarbageForTesting below for testing!
   void CollectGarbage(BlinkGC::StackState,
                       BlinkGC::MarkingType,
                       BlinkGC::SweepingType,
                       BlinkGC::GCReason);
-  void CollectAllGarbageForTesting();
+
+  // Forced garbage collection for testing.
+  void CollectAllGarbageForTesting(
+      BlinkGC::StackState stack_state =
+          BlinkGC::StackState::kNoHeapPointersOnStack);
 
   // Register the pre-finalizer for the |self| object. The class T must have
   // USING_PRE_FINALIZER().
diff --git a/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc b/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc
index 415b358a..1cb35b56 100644
--- a/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc
+++ b/third_party/blink/renderer/platform/heap/thread_state_scheduling_test.cc
@@ -28,7 +28,7 @@
   void StartIncrementalMarking() {
     RuntimeEnabledFeatures::SetHeapIncrementalMarkingEnabled(true);
     EXPECT_EQ(ThreadState::kNoGCScheduled, state_->GetGCState());
-    state_->ScheduleIncrementalGC(BlinkGC::GCReason::kTesting);
+    state_->ScheduleIncrementalGC(BlinkGC::GCReason::kForcedGCForTesting);
     state_->RunScheduledGC(BlinkGC::kNoHeapPointersOnStack);
     EXPECT_EQ(ThreadState::kIncrementalMarkingStepScheduled,
               state_->GetGCState());
diff --git a/third_party/blink/renderer/platform/lifecycle_context_test.cc b/third_party/blink/renderer/platform/lifecycle_context_test.cc
index 3b407dee..373e4e5 100644
--- a/third_party/blink/renderer/platform/lifecycle_context_test.cc
+++ b/third_party/blink/renderer/platform/lifecycle_context_test.cc
@@ -145,7 +145,7 @@
   bool was_enabled = RuntimeEnabledFeatures::HeapIncrementalMarkingEnabled();
   RuntimeEnabledFeatures::SetHeapIncrementalMarkingEnabled(true);
   ThreadState* thread_state = ThreadState::Current();
-  thread_state->IncrementalMarkingStart(BlinkGC::GCReason::kTesting);
+  thread_state->IncrementalMarkingStart(BlinkGC::GCReason::kForcedGCForTesting);
 
   DummyContext* context = DummyContext::Create();
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
index 79ae10c..9031a51 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache_test.cc
@@ -320,9 +320,8 @@
   WeakPersistent<Resource> resource1_weak = resource1;
   WeakPersistent<Resource> resource2_weak = resource2;
 
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   // Resources are garbage-collected (WeakMemoryCache) and thus removed
   // from MemoryCache.
   EXPECT_FALSE(resource1_weak);
diff --git a/third_party/blink/renderer/platform/timer_test.cc b/third_party/blink/renderer/platform/timer_test.cc
index fa9e765..4ff74e0 100644
--- a/third_party/blink/renderer/platform/timer_test.cc
+++ b/third_party/blink/renderer/platform/timer_test.cc
@@ -636,9 +636,8 @@
   owner->StartOneShot(TimeDelta(), FROM_HERE);
 
   owner = nullptr;
-  ThreadState::Current()->CollectGarbage(
-      BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kEagerSweeping, BlinkGC::GCReason::kForcedGC);
+  ThreadState::Current()->CollectAllGarbageForTesting(
+      BlinkGC::kNoHeapPointersOnStack);
   EXPECT_TRUE(record->OwnerIsDestructed());
 
   EXPECT_FALSE(record->TimerHasFired());
@@ -656,9 +655,11 @@
   owner->StartOneShot(TimeDelta(), FROM_HERE);
 
   owner = nullptr;
+  // Explicit regular GC call to allow lazy sweeping.
   ThreadState::Current()->CollectGarbage(
       BlinkGC::kNoHeapPointersOnStack, BlinkGC::kAtomicMarking,
-      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGC);
+      BlinkGC::kLazySweeping, BlinkGC::GCReason::kForcedGCForTesting);
+  // Since the heap is laziy swept, owner is not yet destructed.
   EXPECT_FALSE(record->OwnerIsDestructed());
 
   {
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 747f0559..6d231b94 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3026,6 +3026,33 @@
 crbug.com/939181 virtual/not-site-per-process/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Failure Timeout ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-048-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-046a-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-082-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-048a-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-085-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-044-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-097a-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-001-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-090-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-092-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-095-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-095a-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-040a-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-096a-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-002-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-045-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-091-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-092a-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-004-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-096-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-040-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-003-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-091a-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-097-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-041-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-049-manual.html [ Skip ]
+crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-line-090a-manual.html [ Skip ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-090a-manual.html [ Skip ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-046a-manual.html [ Skip ]
 crbug.com/626703 external/wpt/css/css-text-decor/text-decoration-091-manual.html [ Skip ]
@@ -5904,6 +5931,11 @@
 crbug.com/926170 external/wpt/webrtc/RTCIceConnectionState-candidate-pair.https.html [ Pass Timeout ]
 crbug.com/926170 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCIceConnectionState-candidate-pair.https.html [ Pass Timeout ]
 
+# TODO(guidou): Remove these expectations once crbug.com/740501 is fixed in third_party/webrtc.
+crbug.com/740501 external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded.html [ Pass Failure Timeout ]
+crbug.com/740501 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded.html [ Pass Failure Timeout ]
+crbug.com/740501 fast/peerconnection/RTCPeerConnection-AddRemoveStream.html [ Pass Failure Timeout ]
+
 # Sheriff 2019-01-18
 crbug.com/922970 external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html [ Failure Pass ]
 
@@ -6211,3 +6243,6 @@
 crbug.com/949444 [ Mac10.13 Debug ] virtual/mouseevent_fractional/fast/events/sequential-focus-navigation-starting-point.html [ Pass Crash ]
 crbug.com/949444 [ Mac10.13 Debug ] virtual/user-activation-v2/fast/events/sequential-focus-navigation-starting-point.html [ Pass Crash ]
 crbug.com/949445 [ Mac ] fast/forms/text/input-text-scroll-left-on-blur.html [ Failure ]
+
+# Sheriff 2019-04-05
+crbug.com/ [ Win7 ] virtual/streams-native/http/tests/fetch/serviceworker/referrer-base-https-other-https.html [ Pass Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index cd00ea6..552c4c70 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -481,6 +481,16 @@
     "args": ["--blink-settings=darkMode=3,darkModePagePolicy=1"]
   },
   {
+    "prefix": "dark-mode",
+    "base": "paint/dark-mode/native-theme-off",
+    "args": ["--blink-settings=darkMode=3"]
+  },
+  {
+    "prefix": "dark-mode",
+    "base": "paint/dark-mode/native-theme-on",
+    "args": ["--blink-settings=darkMode=3"]
+  },
+  {
     "prefix": "outofblink-cors",
     "base": "external/wpt/fetch",
     "args": ["--enable-features=OutOfBlinkCors,NetworkService"]
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-entities-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-entities-expected.txt
deleted file mode 100644
index 4ced9348..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-entities-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Test that XMLSerializer quotes the attribute characters specified in the W3C spec.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS htmlElement.outerHTML is "<div quoteme=\"< > &amp; &quot; ' &nbsp;\"></div>"
-PASS (new XMLSerializer()).serializeToString(xmlElement) is "<div xmlns=\"http://www.w3.org/1999/xhtml\" quoteme=\"&lt; &gt; &amp; &quot; ' \xA0\"></div>"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-entities.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-entities.html
index e5a89cd..a3ecdd9 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-entities.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-entities.html
@@ -1,9 +1,8 @@
 <!DOCTYPE html>
-<script src="../../resources/js-test.js"></script>
-
+<title>Test that XMLSerializer quotes the attribute characters specified in 'https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#dfn-concept-serialize-xml-attributes', the W3C spec</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
-description('Test that XMLSerializer quotes the attribute characters specified in <a href="https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#dfn-concept-serialize-xml-attributes">the W3C spec</a>.');
-
 var attrValue = '< > & " \' \xA0';
 
 // HTML case.
@@ -11,7 +10,7 @@
 // Steps 1-3 under the "Escaping a string" heading are relevant to attributes.
 window.htmlElement = document.createElement('div');
 htmlElement.setAttribute('quoteme', attrValue);
-shouldBe('htmlElement.outerHTML', '"<div quoteme=\\"< > &amp; &quot; \' &nbsp;\\"><' + '/div>"');
+assert_equals(htmlElement.outerHTML, "<div quoteme=\"< > &amp; &quot; ' &nbsp;\"></div>");
 
 // XML case.
 // DOM parsing and serialization: https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#dfn-concept-serialize-xml-attributes
@@ -19,5 +18,6 @@
 var xmlDocument = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null);
 window.xmlElement = xmlDocument.createElement('div');
 xmlElement.setAttribute('quoteme', attrValue);
-shouldBe('(new XMLSerializer()).serializeToString(xmlElement)', '"<div xmlns=\\"http://www.w3.org/1999/xhtml\\" quoteme=\\"&lt; &gt; &amp; &quot; \' \\xA0\\"><' + '/div>"');
+assert_equals((new XMLSerializer()).serializeToString(xmlElement), "<div xmlns=\"http://www.w3.org/1999/xhtml\" quoteme=\"&lt; &gt; &amp; &quot; ' \xA0\"></div>");
+done();
 </script>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-namespace-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-namespace-expected.txt
deleted file mode 100644
index 533ffaa..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-namespace-expected.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-Tests the serialization of XML namespaces on attributes, as reported in bug 248044.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS doc.documentElement.getAttributeNS(null, "attr1") is "value1"
-PASS doc.documentElement.getAttributeNS("http://www.example.com", "foo") is "bar"
-PASS doc.documentElement.getAttributeNS("http://www.example.com", "foo2") is "bar2"
-PASS doc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz") is "buzz"
-PASS doc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz2") is "buzz2"
-PASS doc.documentElement.getAttributeNS("http://www.example.com/ns2", "name") is "value"
-PASS doc.documentElement.getAttributeNS("http://www.example.com/ns2", "name2") is "value2"
-PASS doc.documentElement.getAttributeNS("http://www.w3.org/XML/1998/namespace", "id") is "outer"
-PASS doc.querySelector("inner").localName is "inner"
-PASS doc.querySelector("inner").namespaceURI is "http://www.example.com"
-PASS doc.querySelector("inner").getAttributeNS(null, "id") is "inner"
-PASS parsedDoc.documentElement.getAttributeNS(null, "attr1") is "value1"
-PASS parsedDoc.documentElement.getAttributeNS("http://www.example.com", "foo") is "bar"
-PASS parsedDoc.documentElement.getAttributeNS("http://www.example.com", "foo2") is "bar2"
-PASS parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz") is "buzz"
-PASS parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz2") is "buzz2"
-PASS parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns2", "name") is "value"
-PASS parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns2", "name2") is "value2"
-PASS parsedDoc.documentElement.getAttributeNS("http://www.w3.org/XML/1998/namespace", "id") is "outer"
-PASS parsedDoc.querySelector("inner").localName is "inner"
-PASS parsedDoc.querySelector("inner").namespaceURI is "http://www.example.com"
-PASS parsedDoc.querySelector("inner").getAttributeNS(null, "id") is "inner"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-namespace.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-namespace.html
index be727ee..215a79a5 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-namespace.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-namespace.html
@@ -1,7 +1,8 @@
 <!DOCTYPE html>
-<script src="../../resources/js-test.js"></script>
+<title>Tests the serialization of XML namespaces on attributes, as reported in 'http://crbug.com/248044', bug 248044</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
-description('Tests the serialization of XML namespaces on attributes, as reported in <a href="http://crbug.com/248044">bug 248044</a>.');
 window.doc = (new DOMParser()).parseFromString('<outer attr1="value1" xmlns="http://www.example.com"/>', 'text/xml');
 // XML-namespaced attributes should get the "xml" prefix.
 doc.documentElement.setAttributeNS('http://www.w3.org/XML/1998/namespace', 'id', 'outer');
@@ -24,31 +25,32 @@
 innerElement.setAttributeNS(null, 'id', 'inner');
 
 // Check the DOM construction.
-shouldBe('doc.documentElement.getAttributeNS(null, "attr1")', '"value1"');
-shouldBe('doc.documentElement.getAttributeNS("http://www.example.com", "foo")', '"bar"');
-shouldBe('doc.documentElement.getAttributeNS("http://www.example.com", "foo2")', '"bar2"');
-shouldBe('doc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz")', '"buzz"');
-shouldBe('doc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz2")', '"buzz2"');
-shouldBe('doc.documentElement.getAttributeNS("http://www.example.com/ns2", "name")', '"value"');
-shouldBe('doc.documentElement.getAttributeNS("http://www.example.com/ns2", "name2")', '"value2"');
-shouldBe('doc.documentElement.getAttributeNS("http://www.w3.org/XML/1998/namespace", "id")', '"outer"');
-shouldBe('doc.querySelector("inner").localName', '"inner"');
-shouldBe('doc.querySelector("inner").namespaceURI', '"http://www.example.com"');
-shouldBe('doc.querySelector("inner").getAttributeNS(null, "id")', '"inner"');
+assert_equals(doc.documentElement.getAttributeNS(null, "attr1"), "value1");
+assert_equals(doc.documentElement.getAttributeNS("http://www.example.com", "foo"), "bar");
+assert_equals(doc.documentElement.getAttributeNS("http://www.example.com", "foo2"), "bar2");
+assert_equals(doc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz"), "buzz");
+assert_equals(doc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz2"), "buzz2");
+assert_equals(doc.documentElement.getAttributeNS("http://www.example.com/ns2", "name"), "value");
+assert_equals(doc.documentElement.getAttributeNS("http://www.example.com/ns2", "name2"), "value2");
+assert_equals(doc.documentElement.getAttributeNS("http://www.w3.org/XML/1998/namespace", "id"), "outer");
+assert_equals(doc.querySelector("inner").localName, "inner");
+assert_equals(doc.querySelector("inner").namespaceURI, "http://www.example.com");
+assert_equals(doc.querySelector("inner").getAttributeNS(null, "id"), "inner");
 
 window.xmlText = (new XMLSerializer()).serializeToString(doc); // exported for debugging
 window.parsedDoc = (new DOMParser()).parseFromString(xmlText, 'text/xml');
 
 // Check the serialization result.
-shouldBe('parsedDoc.documentElement.getAttributeNS(null, "attr1")', '"value1"');
-shouldBe('parsedDoc.documentElement.getAttributeNS("http://www.example.com", "foo")', '"bar"');
-shouldBe('parsedDoc.documentElement.getAttributeNS("http://www.example.com", "foo2")', '"bar2"');
-shouldBe('parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz")', '"buzz"');
-shouldBe('parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz2")', '"buzz2"');
-shouldBe('parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns2", "name")', '"value"');
-shouldBe('parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns2", "name2")', '"value2"');
-shouldBe('parsedDoc.documentElement.getAttributeNS("http://www.w3.org/XML/1998/namespace", "id")', '"outer"');
-shouldBe('parsedDoc.querySelector("inner").localName', '"inner"');
-shouldBe('parsedDoc.querySelector("inner").namespaceURI', '"http://www.example.com"');
-shouldBe('parsedDoc.querySelector("inner").getAttributeNS(null, "id")', '"inner"');
+assert_equals(parsedDoc.documentElement.getAttributeNS(null, "attr1"), "value1");
+assert_equals(parsedDoc.documentElement.getAttributeNS("http://www.example.com", "foo"), "bar");
+assert_equals(parsedDoc.documentElement.getAttributeNS("http://www.example.com", "foo2"), "bar2");
+assert_equals(parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz"), "buzz");
+assert_equals(parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns1", "fizz2"), "buzz2");
+assert_equals(parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns2", "name"), "value");
+assert_equals(parsedDoc.documentElement.getAttributeNS("http://www.example.com/ns2", "name2"), "value2");
+assert_equals(parsedDoc.documentElement.getAttributeNS("http://www.w3.org/XML/1998/namespace", "id"), "outer");
+assert_equals(parsedDoc.querySelector("inner").localName, "inner");
+assert_equals(parsedDoc.querySelector("inner").namespaceURI, "http://www.example.com");
+assert_equals(parsedDoc.querySelector("inner").getAttributeNS(null, "id"), "inner");
+done();
 </script>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-ns-prefix-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-ns-prefix-expected.txt
deleted file mode 100644
index b35326d7..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-ns-prefix-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This tests that XMLSerializer emits a correct namespace declaration for the  element. The first line is serialized as part of a DocumentFragment. The second line is serialized as part of the Document.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><image xlink:href="blah"/></svg>
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><image xlink:href="blah"/></svg>
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-ns-prefix.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-ns-prefix.html
index 5131ea1..9004ece 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-ns-prefix.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-ns-prefix.html
@@ -1,9 +1,13 @@
 <!DOCTYPE html>
+<title>This tests that XMLSerializer emits a correct namespace declaration for the <image> element. The first line is serialized as part of a DocumentFragment. The second line is serialized as part of the Document</title>
 <body>
-<script src="../../resources/js-test.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
-
-description('This tests that XMLSerializer emits a correct namespace declaration for the <image> element. The first line is serialized as part of a DocumentFragment. The second line is serialized as part of the Document.');
+function escapeHTML(text)
+{
+    return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/\0/g, "\\0");
+}
 
 var svgDoc = new DOMParser().parseFromString('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"></svg>', 'text/xml');
 var svgEl = svgDoc.firstChild;
@@ -13,9 +17,13 @@
 
 var serializer = new XMLSerializer();
 
-debug(escapeHTML(serializer.serializeToString(svgEl)));
+var span = document.createElement("div");
+span.innerHTML = escapeHTML(serializer.serializeToString(svgEl));
+assert_equals(span.textContent, "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><image xlink:href=\"blah\"/></svg>");
 document.body.appendChild(svgDoc.removeChild(svgEl));
-debug(escapeHTML(serializer.serializeToString(svgEl)));
+span.innerHTML = escapeHTML(serializer.serializeToString(svgEl));
+assert_equals(span.textContent, "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"><image xlink:href=\"blah\"/></svg>");
 
 document.body.removeChild(svgEl);
+done();
 </script>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-special-namespaces-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-special-namespaces-expected.txt
deleted file mode 100644
index 2ff9fa4..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-special-namespaces-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Tests the serialization of special XML namespaces on attributes, as reported in bug 395950.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS xmlDocument.querySelector("inner1").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr") is "value"
-PASS xmlDocument.querySelector("inner2").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr") is "value"
-PASS xmlDocument.querySelector("inner2").getAttribute("xml:attr") is "value"
-PASS parsedDoc.querySelector("inner1").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr") is "value"
-PASS parsedDoc.querySelector("inner2").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr") is "value"
-PASS parsedDoc.querySelector("inner2").getAttribute("xml:attr") is "value"
-PASS xmlString.indexOf("xmlns:xml") is -1
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-special-namespaces.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-special-namespaces.html
index f4fe193..0256c490 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-special-namespaces.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-attribute-special-namespaces.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
-<script src="../../resources/js-test.js"></script>
+<title>Tests the serialization of special XML namespaces on attributes, as reported in http://crbug.com/395950, bug 395950</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
-description('Tests the serialization of special XML namespaces on attributes, as reported in <a href="http://crbug.com/395950">bug 395950</a>.');
-
 var xmlDocument  = (new DOMParser()).parseFromString('<outer />', 'text/xml');
 
 var inner1 = xmlDocument.createElementNS(null, 'inner1');
@@ -16,18 +16,19 @@
 xmlDocument.firstChild.appendChild(inner2);
 
 // Check the DOM construction.
-shouldBeEqualToString('xmlDocument.querySelector("inner1").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr")', 'value');
-shouldBeEqualToString('xmlDocument.querySelector("inner2").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr")', 'value');
-shouldBeEqualToString('xmlDocument.querySelector("inner2").getAttribute("xml:attr")', 'value');
+assert_equals(xmlDocument.querySelector("inner1").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr"), 'value');
+assert_equals(xmlDocument.querySelector("inner2").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr"), 'value');
+assert_equals(xmlDocument.querySelector("inner2").getAttribute("xml:attr"), 'value');
 
 var xmlString = (new XMLSerializer()).serializeToString(xmlDocument);
 
 // Check the serialization result.
 var parsedDoc = (new DOMParser()).parseFromString(xmlString, 'text/xml');
-shouldBeEqualToString('parsedDoc.querySelector("inner1").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr")', 'value');
-shouldBeEqualToString('parsedDoc.querySelector("inner2").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr")', 'value');
-shouldBeEqualToString('parsedDoc.querySelector("inner2").getAttribute("xml:attr")', 'value');
+assert_equals(parsedDoc.querySelector("inner1").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr"), 'value');
+assert_equals(parsedDoc.querySelector("inner2").getAttributeNS("http://www.w3.org/XML/1998/namespace", "attr"), 'value');
+assert_equals(parsedDoc.querySelector("inner2").getAttribute("xml:attr"), 'value');
 
 // Check that the correct xmlns definitions were emitted.
-shouldBeEqualToNumber('xmlString.indexOf("xmlns:xml")', -1);
+assert_equals(xmlString.indexOf("xmlns:xml"), -1);
+done();
 </script>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype-expected.txt
deleted file mode 100644
index 79cc065..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-This tests XMLSerializer.serializeToString() on a DocumentType node that has a document associated with it.
-PASS: the DocumentType node has been successfully serialize to "<!DOCTYPE aDocTypeName PUBLIC "aPublicID" "aSystemID">".
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype.html
index c4e984b..134631f 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype.html
@@ -1,33 +1,19 @@
 <html>
 <head>
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
     <script>
-        function debug(str) {
-            li = document.createElement('li');
-            li.appendChild(document.createTextNode(str));
-            document.getElementById('console').appendChild(li);
-        }
-
-        function runTests() {
-            if (window.testRunner) {
-                testRunner.dumpAsText();
-            }
-
+        test(() => {
             var docType = window.document.implementation.createDocumentType("aDocTypeName", "aPublicID", "aSystemID");
             var doc = window.document.implementation.createDocument("", "", docType);
 
             var serializer = new XMLSerializer();
 
             var result = serializer.serializeToString(docType);
-            if (result == '<!DOCTYPE aDocTypeName PUBLIC "aPublicID" "aSystemID">')
-                debug('PASS: the DocumentType node has been successfully serialize to "' + result + '".');
-            else
-                debug('FAIL: the DocumentType node has not been successfully serialized.');
-        }
+            assert_equals(result, '<!DOCTYPE aDocTypeName PUBLIC "aPublicID" "aSystemID">', 'The DocumentType node should serialize to "' + result + '"');
+        }, "This tests XMLSerializer.serializeToString() on a DocumentType node that has a document associated with it");
     </script>
 </head>
-<body onload="runTests()">
-This tests XMLSerializer.serializeToString() on a DocumentType node that has a document associated with it.
-<ul id="console">
-</ul>
+<body>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype2-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype2-expected.txt
deleted file mode 100644
index c0c51298..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype2-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-This tests XMLSerializer.serializeToString() on a newly created DocumentType node does not throw since the node has an associated document.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS text = serializer.serializeToString(docType) did not throw exception.
-PASS text is "<!DOCTYPE aDocTypeName PUBLIC \"aPublicID\" \"aSystemID\">"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype2.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype2.html
index 749a03a..4b0f6d8 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype2.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-doctype2.html
@@ -1,18 +1,22 @@
 <html>
 <head>
-<script src="../../resources/js-test.js"></script>
+<title>This tests XMLSerializer.serializeToString() on a newly created DocumentType node does not throw since the node has an associated document</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 </head>
 <body>
 <script>
-description("This tests XMLSerializer.serializeToString() on a newly created DocumentType node does not throw since the node has an associated document.");
-
 var docType = window.document.implementation.createDocumentType("aDocTypeName", "aPublicID", "aSystemID");
 var serializer = new XMLSerializer();
 
 var text;
-shouldNotThrow("text = serializer.serializeToString(docType)");
-shouldBeEqualToString("text", "<!DOCTYPE aDocTypeName PUBLIC \"aPublicID\" \"aSystemID\">");
+try {
+  text = serializer.serializeToString(docType);
+} catch (e) {
+  assert_unreached('Should not throw exception');
+}
+assert_equals(text, "<!DOCTYPE aDocTypeName PUBLIC \"aPublicID\" \"aSystemID\">");
+done();
 </script>
-<script src="../../resources/js-test.js"></script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-double-xmlns-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-double-xmlns-expected.txt
deleted file mode 100644
index 369445d..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-double-xmlns-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Checks that attributes created setAttributeNS() don't cause two identical xmlns attributes to be serialized
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-PASS doc.documentElement.getAttributeNS("http://www.example.com", "attr") is "value"
-PASS parsedDoc.documentElement.getAttributeNS("http://www.example.com", "attr") is "value"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-double-xmlns.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-double-xmlns.html
index b15fb1b..f15bd7a 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-double-xmlns.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-double-xmlns.html
@@ -1,17 +1,19 @@
 <!DOCTYPE html>
-<script src="../../resources/js-test.js"></script>
+<title>Checks that attributes created setAttributeNS() don't cause two identical xmlns attributes to be serialized</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
-description("Checks that attributes created setAttributeNS() don't cause two identical xmlns attributes to be serialized");
 window.doc = (new DOMParser()).parseFromString('<test/>', 'text/xml');
 doc.documentElement.setAttributeNS('http://www.example.com', 'ns:attr', 'value');
 doc.documentElement.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:ns', 'http://www.example.com');
 
 // Check the DOM construction.
-shouldBe('doc.documentElement.getAttributeNS("http://www.example.com", "attr")', '"value"');
+assert_equals(doc.documentElement.getAttributeNS("http://www.example.com", "attr"), "value");
 
 window.xmlText = (new XMLSerializer()).serializeToString(doc); // exported for debugging
 window.parsedDoc = (new DOMParser()).parseFromString(xmlText, 'text/xml');
 
 // Check the serialization result.
-shouldBe('parsedDoc.documentElement.getAttributeNS("http://www.example.com", "attr")', '"value"');
+assert_equals(parsedDoc.documentElement.getAttributeNS("http://www.example.com", "attr"), "value");
+done();
 </script>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-entities-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-entities-expected.txt
deleted file mode 100644
index 4d19c89..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-entities-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml"><p>(0 &lt; 1 &amp;&amp; 1 &gt; 0)</p><script>if (0 &lt; 1 &amp;&amp; 1 &gt; 0) { };</script><style>/* &lt; &gt; &amp; */</style></html>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-entities.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-entities.html
index dd1ad68..db82f85 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-entities.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-entities.html
@@ -1,11 +1,9 @@
 <html>
 <head>
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
     <script type="text/javascript">
-    function runTest()
-    {
-        if (window.testRunner)
-            testRunner.dumpAsText();
-
+    test(() => {
         var ns = 'http://www.w3.org/1999/xhtml';
         var xhtml = document.implementation.createDocument(ns , 'html', null);
 
@@ -24,12 +22,10 @@
         var serializer = new XMLSerializer();
         var xmlString = serializer.serializeToString(xhtml);
 
-        var outputText = document.getElementById("output");
-        outputText.textContent = xmlString;
-    }
+        assert_equals(xmlString, "<html xmlns=\"http://www.w3.org/1999/xhtml\"><p>(0 &lt; 1 &amp;&amp; 1 &gt; 0)</p><script>if (0 &lt; 1 &amp;&amp; 1 &gt; 0) { };<\/script><style>/* &lt; &gt; &amp; */</style></html>");
+    }, 'XMLSerializer: Serializes entities to XML-compatible format');
     </script>
 </head>
-    <body onload="runTest()">
-        <div id="output"/>
+    <body>
     </body>
 </html>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-serialize-to-string-exception-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-serialize-to-string-exception-expected.txt
deleted file mode 100644
index c35e094..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-serialize-to-string-exception-expected.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-This tests XMLSerializer.serializeToString() throwing exception when node value is invalid and passing otherwise.
-
-
-1. Verifying XMLSerializer.serializeToString() should THROW exception with no argument
-Exception thrown = [TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': 1 argument required, but only 0 present.]
-PASS
-
-
-2. Verifying XMLSerializer.serializeToString() should THROW exception with argument null
-Exception thrown = [TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.]
-PASS
-
-
-3. Verifying XMLSerializer.serializeToString() should THROW exception with argument undefined
-Exception thrown = [TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.]
-PASS
-
-
-4. Verifying XMLSerializer.serializeToString() should THROW exception with argument <html><title>Hello World</title></html>
-Exception thrown = [TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.]
-PASS
-
-
-5. Verifying XMLSerializer.serializeToString() should THROW exception with argument [object HTMLCollection]
-Exception thrown = [TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.]
-PASS
-
-
-6. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object HTMLDocument]
-PASS
-
-
-7. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object HTMLHtmlElement]
-PASS
-
-
-8. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object HTMLHtmlElement]
-PASS
-
-
-9. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object HTMLDivElement]
-PASS
-
-
-10. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object HTMLHeadingElement]
-PASS
-
-
-11. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object HTMLUnknownElement]
-PASS
-
-
-12. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object HTMLDocument]
-PASS
-
-
-13. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object HTMLHtmlElement]
-PASS
-
-
-14. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object XMLDocument]
-PASS
-
-
-15. Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument [object Element]
-PASS
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-serialize-to-string-exception.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-serialize-to-string-exception.html
index 4783a0c..ac18ee2 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-serialize-to-string-exception.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-serialize-to-string-exception.html
@@ -11,69 +11,32 @@
     font-weight: bold;
 }
 </style>
+<script src=../../resources/testharness.js></script>
+<script src=../../resources/testharnessreport.js></script>
+</head>
+<body>
+<h1 id="heading"/>
 <script>
-if (window.testRunner)
-    testRunner.dumpAsText();
 var count = 0;
 
 function shouldThrowException(node)
 {
-    document.body.appendChild(document.createElement("br"));
-    var header = document.createElement("div");
-    header.textContent = ++count + ". Verifying XMLSerializer.serializeToString() should THROW exception with " + (arguments.length ? "argument " + node : "no argument");
-    document.body.appendChild(header);
-
     var xs = new XMLSerializer();
-    try {
-        var str = xs.serializeToString.apply(xs, arguments);
-
-        var result = document.createElement("div");
-        result.className = "failed"
-        result.textContent = "FAIL";
-        document.body.appendChild(result);
-    } catch (exception) {
-        var err = "Exception thrown = [" + exception.name + ": " + exception.message + "]";
-        var content = document.createElement("div");
-        content.textContent = err;
-        document.body.appendChild(content);
-
-        var result = document.createElement("div");
-        result.className = "passed"
-        result.textContent = "PASS";
-        document.body.appendChild(result);
-    }
+    assert_throws(new TypeError(), () => { xs.serializeToString.apply(xs, arguments); }, ++count + ". Verifying XMLSerializer.serializeToString() should THROW exception with " + (arguments.length ? "argument " + node : "no argument"));
 }
 
 function shouldNOTThrowException(node)
 {
-    document.body.appendChild(document.createElement("br"));
-    var header = document.createElement("div");
-    header.textContent = ++count + ". Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument " + node;
-    document.body.appendChild(header);
-
     var xs = new XMLSerializer();
+    ++count;
     try {
         var str = xs.serializeToString(node);
-
-        var result = document.createElement("div");
-        result.className = "passed"
-        result.textContent = "PASS";
-        document.body.appendChild(result);
     } catch (exception) {
-        var err = "Exception thrown = [" + exception.name + ": " + exception.message + "]";
-        var content = document.createElement("div");
-        content.textContent = err;
-        document.body.appendChild(content);
-
-        var result = document.createElement("div");
-        result.className = "failed"
-        result.textContent = "FAIL";
-        document.body.appendChild(result);
+        assert_unreached(count + ". Verifying XMLSerializer.serializeToString() should NOT-THROW exception with argument " + node);
     }
 }
 
-function runTest()
-{
+test(() => {
     shouldThrowException();
     shouldThrowException(null);
     shouldThrowException(undefined);
@@ -96,11 +59,7 @@
     var xmlDoc = domParser.parseFromString("<root/>", "text/xml");
     shouldNOTThrowException(xmlDoc);
     shouldNOTThrowException(xmlDoc.lastChild);
-}
+}, "This tests XMLSerializer.serializeToString() throwing exception when node value is invalid and passing otherwise");
 </script>
-</head>
-<body onload="runTest();">
-This tests XMLSerializer.serializeToString() throwing exception when node value is invalid and passing otherwise.
-<h1 id="heading"/>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-xml-namespace-expected.txt b/third_party/blink/web_tests/dom/domparsing/xmlserializer-xml-namespace-expected.txt
deleted file mode 100644
index e1c183d..0000000
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-xml-namespace-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-<div xmlns="http://www.w3.org/1999/xhtml" id="target"> <div id="output"> </div><xml:foo xml:space="preserve"/><xml:bar xml:space="default"/></div>
diff --git a/third_party/blink/web_tests/dom/domparsing/xmlserializer-xml-namespace.html b/third_party/blink/web_tests/dom/domparsing/xmlserializer-xml-namespace.html
index 509b7ea..8586c4a 100644
--- a/third_party/blink/web_tests/dom/domparsing/xmlserializer-xml-namespace.html
+++ b/third_party/blink/web_tests/dom/domparsing/xmlserializer-xml-namespace.html
@@ -1,11 +1,12 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
+    <script src=../../resources/testharness.js></script>
+    <script src=../../resources/testharnessreport.js></script>
+</head>
+<body>
+    <div id="target"></div>
     <script type="text/javascript">
-    function runTest()
-    {
-        if (window.testRunner)
-            testRunner.dumpAsText();
-
+    test(() => {
         var target = document.getElementById("target");
 
         var xmlns = "http://www.w3.org/XML/1998/namespace";
@@ -23,13 +24,11 @@
         var serializer = new XMLSerializer();
         var xmlString = serializer.serializeToString(target);
 
-        var outputText = document.getElementById("output");
+        var outputText = document.createElement('div');
         outputText.textContent = xmlString;
-    }
+
+        assert_equals(outputText.textContent, "<div xmlns=\"http://www.w3.org/1999/xhtml\" id=\"target\"><xml:foo xml:space=\"preserve\"/><xml:bar xml:space=\"default\"/></div>");
+    }, 'XMLSerializer with xml namespace');
     </script>
-</head>
-    <body onload="runTest()">
-        <div id="target"/>
-        <div id="output"/>
-    </body>
+</body>
 </html>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index df57fe4b..e07cff4 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -1837,12 +1837,174 @@
      {}
     ]
    ],
+   "css/css-text-decor/text-decoration-line-001-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-001-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-002-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-002-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-003-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-003-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-004-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-004-manual.html",
+     {}
+    ]
+   ],
    "css/css-text-decor/text-decoration-line-014.xht": [
     [
      "/css/css-text-decor/text-decoration-line-014.xht",
      {}
     ]
    ],
+   "css/css-text-decor/text-decoration-line-040-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-040-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-040a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-040a-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-041-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-041-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-044-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-044-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-045-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-045-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-046a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-046a-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-048-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-048-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-048a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-048a-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-049-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-049-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-082-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-082-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-085-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-085-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-090-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-090-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-090a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-090a-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-091-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-091-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-091a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-091a-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-092-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-092-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-092a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-092a-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-095-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-095-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-095a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-095a-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-096-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-096-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-096a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-096a-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-097-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-097-manual.html",
+     {}
+    ]
+   ],
+   "css/css-text-decor/text-decoration-line-097a-manual.html": [
+    [
+     "/css/css-text-decor/text-decoration-line-097a-manual.html",
+     {}
+    ]
+   ],
    "css/css-text/text-transform/text-transform-copy-paste-001-manual.html": [
     [
      "/css/css-text/text-transform/text-transform-copy-paste-001-manual.html",
@@ -64115,6 +64277,30 @@
      {}
     ]
    ],
+   "css/css-text/line-breaking/line-breaking-013.html": [
+    [
+     "/css/css-text/line-breaking/line-breaking-013.html",
+     [
+      [
+       "/css/css-text/line-breaking/reference/line-breaking-013-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-text/line-breaking/line-breaking-014.html": [
+    [
+     "/css/css-text/line-breaking/line-breaking-014.html",
+     [
+      [
+       "/css/css-text/line-breaking/reference/line-breaking-013-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-text/line-breaking/line-breaking-ic-001.html": [
     [
      "/css/css-text/line-breaking/line-breaking-ic-001.html",
@@ -146700,6 +146886,11 @@
      {}
     ]
    ],
+   "css/css-text/line-breaking/reference/line-breaking-013-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-text/line-breaking/reference/line-breaking-ic-001-ref.html": [
     [
      {}
@@ -219990,6 +220181,12 @@
      {}
     ]
    ],
+   "css/css-contain/contain-size-multicol-as-flex-item.html": [
+    [
+     "/css/css-contain/contain-size-multicol-as-flex-item.html",
+     {}
+    ]
+   ],
    "css/css-contain/inheritance.html": [
     [
      "/css/css-contain/inheritance.html",
@@ -349800,6 +349997,10 @@
    "81465c02d6114aa4a27637b2e77b3d62161c0864",
    "reftest"
   ],
+  "css/css-contain/contain-size-multicol-as-flex-item.html": [
+   "19aa12262f9a7dc35f3682b1f7baa3c12949a906",
+   "testharness"
+  ],
   "css/css-contain/contain-size-replaced-001.html": [
    "b0dba02f1cd56f4fccc772cfb948dbabb1d600e2",
    "reftest"
@@ -373788,6 +373989,22 @@
    "fd5bc5da3a800a7f7e97211e8cb2438bfdc0c462",
    "reftest"
   ],
+  "css/css-text-decor/text-decoration-line-001-manual.html": [
+   "76a8f5401494b508b14fc5cc19f07108f53b12a1",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-002-manual.html": [
+   "0e41fd1fcfe713c4b63f7c3d8ed34ea6aee1fe71",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-003-manual.html": [
+   "2e94e8e946b553c02e9c09460723afdc93e741dc",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-004-manual.html": [
+   "5c6da02d1e0e3aa097120a2da527139e9cfdbe3b",
+   "manual"
+  ],
   "css/css-text-decor/text-decoration-line-010.xht": [
    "f4b16e32aa185d204c1793f9ca1b0eda502c60fd",
    "reftest"
@@ -373808,6 +374025,98 @@
    "7d5f0570b324b81761a1b5dfdc24e3bcc5b66869",
    "manual"
   ],
+  "css/css-text-decor/text-decoration-line-040-manual.html": [
+   "c93f4b99084a9c8f54dee6130493c3cc631b434a",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-040a-manual.html": [
+   "5020a1fd118ea0e8bd81994c4e38449b2b33826a",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-041-manual.html": [
+   "7c7c920e990688518acab7aeb054ef713d36f36e",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-044-manual.html": [
+   "2d9048d3d1d5e5a5d97ea3127ae08f38054f9b1e",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-045-manual.html": [
+   "b9d688387569958283f24c3811cd97d8210a48bb",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-046a-manual.html": [
+   "67f2b89ea23576cf51a8654859f5d9d47a8215fc",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-048-manual.html": [
+   "f3a6e137e2d7ae5bc9ab3e930392a7c347661cd5",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-048a-manual.html": [
+   "9bd48defaf03432a5e0acddd4d68aa81a7ea20c4",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-049-manual.html": [
+   "31f9aa762183f604bbdb5964b79052084a1c7f40",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-082-manual.html": [
+   "c4425f90729ca688bd38283a8e5dc1ea42806e49",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-085-manual.html": [
+   "88a5fc02a048dfc12aa981938379c50c59675570",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-090-manual.html": [
+   "f4484c0ee282bb8bdcfc5130a7275a3878fe2cbc",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-090a-manual.html": [
+   "e98688521ffcc4508befe9b9dc019339d6f9c08b",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-091-manual.html": [
+   "94405c0362bc231c2092401fbd066d291a63fed9",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-091a-manual.html": [
+   "e1ba036c257eb4bc4670c3856f86381146641cf0",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-092-manual.html": [
+   "4dffcf6a00efffdfdb042f28cb5994be029fbf00",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-092a-manual.html": [
+   "7f803a35f1603795a6709ee052cd9e544dfabd3d",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-095-manual.html": [
+   "62ca5d1a19e40dfa59fadf8353629686de0894d1",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-095a-manual.html": [
+   "f5bab19374f94fe60c07f7742222ec27180d5881",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-096-manual.html": [
+   "01eaf87875b47897164e7407c01aba59024f7e19",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-096a-manual.html": [
+   "233006c904ebc8568ca18547f48749315847727a",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-097-manual.html": [
+   "9502b2367aca0bae91d2cac7ace69a2dfcd8e96d",
+   "manual"
+  ],
+  "css/css-text-decor/text-decoration-line-097a-manual.html": [
+   "e037e15d73c445a72bd81e5fd4971c9e17eba456",
+   "manual"
+  ],
   "css/css-text-decor/text-decoration-line-recalc.html": [
    "321aea9f3d93f31685878a14bfc67f9c3e9cf63e",
    "reftest"
@@ -376416,6 +376725,14 @@
    "08f956c9166df4770cdd1d2d60aac9c8acd675d2",
    "reftest"
   ],
+  "css/css-text/line-breaking/line-breaking-013.html": [
+   "448a41d88f1ff099866f1597edec3ebf5128ecb6",
+   "reftest"
+  ],
+  "css/css-text/line-breaking/line-breaking-014.html": [
+   "ea9956362beecb2da890dbef24362a6e9a8be905",
+   "reftest"
+  ],
   "css/css-text/line-breaking/line-breaking-ic-001.html": [
    "6cfe6f86a452c19eae31a076710f02dfc8c4ec4e",
    "reftest"
@@ -376436,6 +376753,10 @@
    "463dc2287ea251750e0c542873011c9c8707dd30",
    "support"
   ],
+  "css/css-text/line-breaking/reference/line-breaking-013-ref.html": [
+   "816015adeab54895037530b8e4d410f81082931f",
+   "support"
+  ],
   "css/css-text/line-breaking/reference/line-breaking-ic-001-ref.html": [
    "421002818f6d5f9837c5de3967a1c3d7b441244f",
    "support"
@@ -407313,7 +407634,7 @@
    "testharness"
   ],
   "dom/events/Event-dispatch-on-disabled-elements.html": [
-   "72e63c4d1e97f1113dd7db357921fd1b0ab9372b",
+   "361006a7240496e9be747faca5056fe2a62a2cff",
    "testharness"
   ],
   "dom/events/Event-dispatch-order.html": [
@@ -437305,7 +437626,7 @@
    "support"
   ],
   "interfaces/wake-lock.idl": [
-   "202dc6b09f7e6aa56289b62bb810ed0fb2e289d0",
+   "4c11b695f49986e7e9852348f21fe9bd5e68d185",
    "support"
   ],
   "interfaces/wasm-js-api.idl": [
@@ -437361,7 +437682,7 @@
    "support"
   ],
   "interfaces/webrtc-stats.idl": [
-   "0992b341e1fe3cede699c09eeb5838bd1e7a3999",
+   "d9d20191ad851f4b6d85cd09e8a53b8697bc840e",
    "support"
   ],
   "interfaces/webrtc.idl": [
@@ -437377,7 +437698,7 @@
    "support"
   ],
   "interfaces/webxr.idl": [
-   "8a3264073e8268282c3db6a891b860d57bfcdb28",
+   "4c74fdac384299ed22a9b9244411c3755a8741db",
    "support"
   ],
   "interfaces/worklets.idl": [
@@ -475869,7 +476190,7 @@
    "support"
   ],
   "wake-lock/idlharness.https.any-expected.txt": [
-   "ca88133a56777e7b0c5b4b65741aae208cf0d904",
+   "3930e1230bde2cf7199b566c5963cbcb12f35bec",
    "support"
   ],
   "wake-lock/idlharness.https.any.js": [
@@ -475877,7 +476198,7 @@
    "testharness"
   ],
   "wake-lock/idlharness.https.any.worker-expected.txt": [
-   "78381b7e21fc484f22d9c055ab9341356f856601",
+   "a17dfea9f40571d8a150f1bf9dd2fa492691d469",
    "support"
   ],
   "wake-lock/wakelock-applicability-manual.https-expected.txt": [
@@ -485077,7 +485398,7 @@
    "support"
   ],
   "webxr/idlharness.https.window-expected.txt": [
-   "a00bc3610a5f316dd8cf94aa659d28b471ee09a1",
+   "93f6ab5a8da7b4a9740101195dae3fc8c158911a",
    "support"
   ],
   "webxr/idlharness.https.window.js": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-001-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-001-manual.html
new file mode 100644
index 0000000..76a8f5401
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-001-manual.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline</title>
+<meta name="assert" content="text-decoration-line:underline; there is a line at or under the alphabetic baseline">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline;
+}
+</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line at or under the alphabetic baseline.</p>
+<div id="htmlsrc">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-002-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-002-manual.html
new file mode 100644
index 0000000..0e41fd1f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-002-manual.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line overline</title>
+<meta name="assert" content="text-decoration-line:overline; there is an overline">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is an overline.</p>
+<div id="htmlsrc">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-003-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-003-manual.html
new file mode 100644
index 0000000..2e94e8e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-003-manual.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line line-through</title>
+<meta name="assert" content="text-decoration-line:line-through; there is a solid line through the centre of the characters">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:line-through;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a solid line through the centre of the characters.</p>
+<div id="htmlsrc">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-004-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-004-manual.html
new file mode 100644
index 0000000..5c6da02d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-004-manual.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline overline</title>
+<meta name="assert" content="text-decoration-line:underline overline; there is an overline and an underline">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is an overline and an underline.</p>
+<div id="htmlsrc">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-040-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-040-manual.html
new file mode 100644
index 0000000..c93f4b99
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-040-manual.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline tbrl hor scripts</title>
+<meta name="assert" content="text-decoration-line:underline; there is a line to the LEFT of the characters for horizontal scripts set vertically using writing-mode: vertical-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the LEFT of the characters for all lines.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p-->
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-040a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-040a-manual.html
new file mode 100644
index 0000000..5020a1f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-040a-manual.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline tbrl mixed</title>
+<meta name="assert" content="text-decoration-line:underline; there is an unbroken line to the LEFT of the characters for each lines">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 3.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div p {
+text-decoration-line:underline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is an unbroken line to the LEFT of the characters for all lines.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh">引发<span lang="en">quick brown fox</span>网络<span lang="ar">جهانی سازیم</span>的全<span lang="my">အပြည်ပြည်</span>部潜<span lang="th">ปิ้งอยู่ในถ้ำ</span>能引<span lang="bo">འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་</span>發網<span lang="hi">विश्वव्यापी बना रहें हैं絡</span>的全<span lang="ja">部潛能</span></p>
+</div>
+</div>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-041-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-041-manual.html
new file mode 100644
index 0000000..7c7c920e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-041-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline tbrl (zh)</title>
+<meta name="assert" content="text-decoration-line:underline; there is a line to the LEFT of the characters">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the LEFT of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能引發網絡的全部潛能</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-044-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-044-manual.html
new file mode 100644
index 0000000..2d9048d3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-044-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line overline tbrl hor scripts</title>
+<meta name="assert" content="text-decoration-line-line:overline; there is a line to the RIGHT of the characters for horizontal scripts set vertically using writing-mode: vertical-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the RIGHT of the characters for all lines.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-045-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-045-manual.html
new file mode 100644
index 0000000..b9d68838
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-045-manual.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line overline tbrl (zh)</title>
+<meta name="assert" content="text-decoration-line-line:overline; there is a line to the RIGHT of the characters">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the RIGHT of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+<div>
+<p lang="zh"><span>引发网络的全部潜能引發網絡的全部潛能</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-046a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-046a-manual.html
new file mode 100644
index 0000000..67f2b89
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-046a-manual.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line overline tbrl mixed</title>
+<meta name="assert" content="text-decoration-line-line:overline; there is an unbroken line to the RIGHT of the characters for all lines">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 3.5;
+	}
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div p {
+text-decoration-line:overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is an unbroken line to the RIGHT of the characters for each line.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh">引发<span lang="en">quick brown fox</span>网络<span lang="ar">جهانی سازیم</span>的全<span lang="my">အပြည်ပြည်</span>部潜<span lang="th">ปิ้งอยู่ในถ้ำ</span>能引<span lang="bo">འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་</span>發網<span lang="hi">विश्वव्यापी बना रहें हैं絡</span>的全<span lang="ja">部潛能</span></p>
+</div>
+</div>
+</body>
+</html>
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-048-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-048-manual.html
new file mode 100644
index 0000000..f3a6e13
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-048-manual.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line line-through vertical-rl</title>
+<meta name="assert" content="text-decoration-line:line-through; there is a solid vertical line through the centre of the characters">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:line-through;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a solid vertical line through the centre of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能引發網絡的全部潛能</span></p>
+<p lang="ja"><span>可能性を最大限に導き出すために</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-048a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-048a-manual.html
new file mode 100644
index 0000000..9bd48def
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-048a-manual.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line line-through vertical-rl hor scripts</title>
+<meta name="assert" content="text-decoration-line:line-through; there is a solid vertical line through the centre of the characters">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:line-through;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a solid vertical line through the centre of the characters.<br/><span class="hint">Skip the test if the text the text fails for the Chinese line, or is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+
+<div>
+<p lang="zh"><span>引发网络的全部潜能引發網絡的全部潛能</span></p>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-049-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-049-manual.html
new file mode 100644
index 0000000..31f9aa7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-049-manual.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line vertical-rl over+under</title>
+<meta name="assert" content="text-decoration-line:underline overline; there is a vertical line on both sides of the characters">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a vertical line on both sides of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-rl">
+<div>
+<p lang="zh"><span>引发网络的全部潜能引發網絡的全部潛能</span></p>
+<p lang="ja"><span>可能性を最大限に導き出すために</span></p>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-082-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-082-manual.html
new file mode 100644
index 0000000..c4425f9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-082-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline overline tblr</title>
+<meta name="assert" content="text-decoration-line:underline overline; there is a line on both sides of the characters">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line on both sides of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-lr">
+<div>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-085-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-085-manual.html
new file mode 100644
index 0000000..88a5fc02
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-085-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line line-through tblr</title>
+<meta name="assert" content="text-decoration-line:line-through; there is a solid vertical line through the centre of the characters.">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+:lang(mn) { font-family: "Mongolian Baiti", "Noto sans Mongolian", serif; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:line-through;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a solid vertical line through the centre of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:vertical-lr">
+<div>
+<p lang="mn"><span>ᠣᠯᠠᠨ ᠦᠨᠳᠦᠰᠦᠲᠡᠨ ᠦ ᠪᠣᠯᠭᠠᠬᠤ ᠦᠢᠯᠡ ᠠᠵᠢᠯᠯᠠᠭ᠎ᠠ</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-090-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-090-manual.html
new file mode 100644
index 0000000..f4484c0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-090-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline sideways-rl</title>
+<meta name="assert" content="text-decoration-line:underline; there is a line to the LEFT of the characters for horizontal scripts set vertically using writing-mode: sideways-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the LEFT of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-rl">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-090a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-090a-manual.html
new file mode 100644
index 0000000..e986885
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-090a-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline sideways-rl non-Latin</title>
+<meta name="assert" content="text-decoration-line:underline; there is a line to the LEFT of the characters for horizontal scripts set vertically using writing-mode: sideways-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the LEFT of the characters for all lines.<br/><span class="hint">Skip the test if it fails for the English text, or if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-rl">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p-->
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-091-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-091-manual.html
new file mode 100644
index 0000000..94405c03
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-091-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line overline sideways-rl</title>
+<meta name="assert" content="text-decoration-line:overline; there is a line to the RIGHT of the characters for horizontal scripts set vertically using writing-mode: sideways-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the RIGHT of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-rl">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-091a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-091a-manual.html
new file mode 100644
index 0000000..e1ba036
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-091a-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line overline sideways-rl non-Latin</title>
+<meta name="assert" content="text-decoration-line:overline; there is a line to the RIGHT of the characters for horizontal scripts set vertically using writing-mode: sideways-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the RIGHT of the characters for all lines.<br/><span class="hint">Skip the test if it fails for the English sentence, or if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-rl">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p-->
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-092-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-092-manual.html
new file mode 100644
index 0000000..4dffcf6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-092-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line line-through sideways-rl</title>
+<meta name="assert" content="text-decoration-line:line-through; there is a line through the CENTRE of the characters for horizontal scripts set vertically using writing-mode: sideways-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:line-through;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line through the CENTRE of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-rl">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-092a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-092a-manual.html
new file mode 100644
index 0000000..7f803a35
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-092a-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line line-through sideways-rl non-Latin</title>
+<meta name="assert" content="text-decoration-line:line-through; there is a line through the CENTRE of the characters for horizontal scripts set vertically using writing-mode: sideways-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:line-through;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line through the CENTRE of the characters for all lines.<br/><span class="hint">Skip the test if it fails for the English sentence, or if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-rl">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p-->
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-095-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-095-manual.html
new file mode 100644
index 0000000..62ca5d1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-095-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline sideways-lr</title>
+<meta name="assert" content="text-decoration-line:underline; there is a line to the LEFT of the characters for horizontal scripts set vertically using writing-mode: sideways-rl">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the RIGHT of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-lr">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-095a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-095a-manual.html
new file mode 100644
index 0000000..f5bab19
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-095a-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line underline sideways-lr non-Latin</title>
+<meta name="assert" content="text-decoration-line:underline; there is a line to the RIGHT of the characters for horizontal scripts set vertically using writing-mode: sideways-lr">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:underline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the RIGHT of the characters for all lines.<br/><span class="hint">Skip the test if it fails for the English sentence, or if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-lr">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p-->
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-096-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-096-manual.html
new file mode 100644
index 0000000..01eaf87
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-096-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line overline sideways-lr</title>
+<meta name="assert" content="text-decoration-line:overline; there is a line to the LEFT of the characters for horizontal scripts set vertically using writing-mode: sideways-lr">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the LEFT of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-lr">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-096a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-096a-manual.html
new file mode 100644
index 0000000..233006c9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-096a-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line overline sideways-lr non-Latin</title>
+<meta name="assert" content="text-decoration-line:overline; there is a line to the LEFT of the characters for horizontal scripts set vertically using writing-mode: sideways-lr">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:overline;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line to the LEFT of the characters for all lines.<br/><span class="hint">Skip the test if it fails for the English sentence, or if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-lr">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p-->
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-097-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-097-manual.html
new file mode 100644
index 0000000..9502b23
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-097-manual.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line line-through sideways-lr</title>
+<meta name="assert" content="text-decoration-line:line-through; there is a line through the CENTRE of the characters for horizontal scripts set vertically using writing-mode: sideways-lr">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:line-through;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line through the CENTRE of the characters.<br/><span class="hint">Skip the test if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-lr">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-097a-manual.html b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-097a-manual.html
new file mode 100644
index 0000000..e037e15
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-line-097a-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>text-decoration-line line-through sideways-lr non-Latin</title>
+<meta name="assert" content="text-decoration-line:line-through; there is a line through the CENTRE of the characters for horizontal scripts set vertically using writing-mode: sideways-lr">
+<link rel="author" title="Richard Ishida" href="mailto:ishida@w3.org">
+<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#line-decoration">
+<!-- cosmetic styling -->
+<style>
+#htmlsrc { margin: 2em; }
+#htmlsrc p {
+	font-size: 28px;
+	border-radius: 5px;
+	line-height: 1.5;
+	}
+.hint { color: brown; font-family: sans-serif; font-size: 90%; }
+.hint:before { content: '❗ '; }
+</style>
+<!-- the test -->
+<style>
+div span {
+text-decoration-line:line-through;
+}</style>
+</head>
+<body>
+<p class="instructions">Test passes if there is a line through the CENTRE of the characters for all lines.<br/><span class="hint">Skip the test if it fails for the English sentence, or if the text is not vertical.</span></p>
+<div id="htmlsrc" style="writing-mode:sideways-lr">
+
+<div>
+<p lang="en"><span>The quick brown fox jumps over the lazy dog.</span></p>
+<p lang="ar"><span>وب جهانی را به‌درستی جهانی سازیم!</span></p>
+<p lang="my"><span>အပြည်ပြည်ဆိုင်ရာလှုပ်ရှားမှု၊</span></p>
+<p lang="th"><span>กูกินกุ้งปิ้งอยู่ในถ้ำ กูกินกุ้งปิ้งอยู่ในถ้ำ</span></p>
+<p lang="bo"><span>འཛམ་གླིང་ཡོངས་འབྲེལ་འདི་ ངོ་མ་འབད་རང་ འཛམ་གླིང་ཡོངས་ལུ་ཁྱབ་ཚུགསཔ་བཟོ་བ།</span></p>
+<p lang="hi"><span>वर्ल्ड वाईड वेब को सचमुच विश्वव्यापी बना रहें हैं !</span></p-->
+</div>  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-013.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-013.html
new file mode 100644
index 0000000..448a41d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-013.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>CSS Text — line breaking of emoji sequences that should form single clusters</title>
+<meta name=assert content="A UA must use the extended grapheme cluster (not legacy grapheme cluster), as defined in [UAX29], as the basis for its typographic character unit.">
+<link rel=help href="https://www.w3.org/TR/css-text-3/#characters">
+<link rel=match href="reference/line-breaking-013-ref.html">
+<link rel=author title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<style>
+div {
+    line-height: 1em;
+    font-size: 30px;
+}
+.container {
+    position: relative;
+    margin: .5em;
+}
+.ref {
+    background-color: white;
+    padding: .25em;
+}
+.test {
+    color: transparent;
+    background-color: red;
+    position: absolute;
+    width: .5em;
+    left: 0;
+    top: 0;
+    padding: .25em;
+    z-index: -1;
+}
+</style>
+<body>
+    <p>Each emoji should appear on a single line with no red background.</p>
+    <div class=container>
+        <div class=ref>&#x1F468;&#x200D;&#x1F4BB;</div><!-- man technologist -->
+        <div class=test>&#x1F468;&#x200D;&#x1F4BB;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F469;&#x200D;&#x1F467;&#x200D;&#x1F466;</div><!-- family with mother, son and daughter -->
+        <div class=test>&#x1F469;&#x200D;&#x1F467;&#x200D;&#x1F466;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F939;&#x200D;&#x2640;&#xFE0F;</div><!-- woman juggling -->
+        <div class=test>&#x1F939;&#x200D;&#x2640;&#xFE0F;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x26F9;&#x1F3FF;&#x200D;&#x2640;&#xFE0F;</div><!-- woman basketball player (fitzpatrick type 6) -->
+        <div class=test>&#x26F9;&#x1F3FF;&#x200D;&#x2640;&#xFE0F;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F3F3;&#xFE0F;&#x200D;&#x1F308;</div><!-- rainbow flag -->
+        <div class=test>&#x1F3F3;&#xFE0F;&#x200D;&#x1F308;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F3F4;&#xE0067;&#xE0062;&#xE0077;&#xE006C;&#xE0073;&#xE007F;</div><!-- flag of Wales -->
+        <div class=test>&#x1F3F4;&#xE0067;&#xE0062;&#xE0077;&#xE006C;&#xE0073;&#xE007F;</div>
+    </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-014.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-014.html
new file mode 100644
index 0000000..ea99563
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/line-breaking-014.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>CSS Text — line breaking (with word-break:break-all) of emoji sequences that should form single clusters</title>
+<meta name=assert content="A UA must use the extended grapheme cluster (not legacy grapheme cluster), as defined in [UAX29], as the basis for its typographic character unit.">
+<link rel=help href="https://www.w3.org/TR/css-text-3/#characters">
+<link rel=match href="reference/line-breaking-013-ref.html">
+<link rel=author title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<style>
+div {
+    line-height: 1em;
+    font-size: 30px;
+}
+.container {
+    position: relative;
+    margin: .5em;
+}
+.ref {
+    background-color: white;
+    padding: .25em;
+}
+.test {
+    word-break: break-all;
+    color: transparent;
+    background-color: red;
+    position: absolute;
+    width: .5em;
+    left: 0;
+    top: 0;
+    padding: .25em;
+    z-index: -1;
+}
+</style>
+<body>
+    <p>Each emoji should appear on a single line with no red background.</p>
+    <div class=container>
+        <div class=ref>&#x1F468;&#x200D;&#x1F4BB;</div><!-- man technologist -->
+        <div class=test>&#x1F468;&#x200D;&#x1F4BB;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F469;&#x200D;&#x1F467;&#x200D;&#x1F466;</div><!-- family with mother, son and daughter -->
+        <div class=test>&#x1F469;&#x200D;&#x1F467;&#x200D;&#x1F466;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F939;&#x200D;&#x2640;&#xFE0F;</div><!-- woman juggling -->
+        <div class=test>&#x1F939;&#x200D;&#x2640;&#xFE0F;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x26F9;&#x1F3FF;&#x200D;&#x2640;&#xFE0F;</div><!-- woman basketball player (fitzpatrick type 6) -->
+        <div class=test>&#x26F9;&#x1F3FF;&#x200D;&#x2640;&#xFE0F;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F3F3;&#xFE0F;&#x200D;&#x1F308;</div><!-- rainbow flag -->
+        <div class=test>&#x1F3F3;&#xFE0F;&#x200D;&#x1F308;</div>
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F3F4;&#xE0067;&#xE0062;&#xE0077;&#xE006C;&#xE0073;&#xE007F;</div><!-- flag of Wales -->
+        <div class=test>&#x1F3F4;&#xE0067;&#xE0062;&#xE0077;&#xE006C;&#xE0073;&#xE007F;</div>
+    </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-013-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-013-ref.html
new file mode 100644
index 0000000..816015a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/line-breaking/reference/line-breaking-013-ref.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>CSS Text — reference file for emoji sequence line-breaking test</title>
+<link rel=author title="Jonathan Kew" href="mailto:jkew@mozilla.com">
+<style>
+div {
+    line-height: 1em;
+    font-size: 30px;
+}
+.container {
+    position: relative;
+    margin: .5em;
+}
+.ref {
+    background-color: white;
+    padding: .25em;
+}
+</style>
+<body>
+    <p>Each emoji should appear on a single line with no red background.</p>
+    <div class=container>
+        <div class=ref>&#x1F468;&#x200D;&#x1F4BB;</div><!-- man technologist -->
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F469;&#x200D;&#x1F467;&#x200D;&#x1F466;</div><!-- family with mother, son and daughter -->
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F939;&#x200D;&#x2640;&#xFE0F;</div><!-- woman juggling -->
+    </div>
+    <div class=container>
+        <div class=ref>&#x26F9;&#x1F3FF;&#x200D;&#x2640;&#xFE0F;</div><!-- woman basketball player (fitzpatrick type 6) -->
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F3F3;&#xFE0F;&#x200D;&#x1F308;</div><!-- rainbow flag -->
+    </div>
+    <div class=container>
+        <div class=ref>&#x1F3F4;&#xE0067;&#xE0062;&#xE0077;&#xE006C;&#xE0073;&#xE007F;</div><!-- flag of Wales -->
+    </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/dom/events/Event-dispatch-on-disabled-elements.html b/third_party/blink/web_tests/external/wpt/dom/events/Event-dispatch-on-disabled-elements.html
index 72e63c4..361006a 100644
--- a/third_party/blink/web_tests/external/wpt/dom/events/Event-dispatch-on-disabled-elements.html
+++ b/third_party/blink/web_tests/external/wpt/dom/events/Event-dispatch-on-disabled-elements.html
@@ -165,19 +165,30 @@
     const elem = document.createElement(localName);
     document.body.appendChild(elem);
     elem.disabled = true;
-    const eventPromises = [
-      "animationstart",
-      "animationiteration",
-      "animationend",
-    ].map(eventType => {
-      return new Promise(r => {
-        elem.addEventListener(eventType, r, { once: true });
+    const animationStartPromise = new Promise(r => {
+      elem.addEventListener("animationstart", () => {
+        // Seek to the second iteration to trigger the animationiteration event
+        elem.style.animationDelay = "-100s"
+        r();
       });
     });
-    elem.style.animation = "fade .1s 2";
+    const animationIterationPromise = new Promise(r => {
+      elem.addEventListener("animationiteration", ()=>{
+        elem.style.animationDelay = "-200s"
+        r();
+      });
+    });
+    const animationEndPromise = new Promise(r => {
+      elem.addEventListener("animationend", r);
+    });
+    elem.style.animation = "fade 100s 2";
     elem.classList.add("animate");
     // All the events fire...
-    await Promise.all(eventPromises);
+    await Promise.all([
+      animationStartPromise,
+      animationIterationPromise,
+      animationEndPromise,
+    ]);
     elem.remove();
   }
 }, "CSS Animation animationstart, animationiteration, animationend fire on disabled form elements");
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl b/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl
index 202dc6b0..4c11b69 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/wake-lock.idl
@@ -11,13 +11,19 @@
 
 [Constructor(WakeLockType type), SecureContext, Exposed=(DedicatedWorker,Window)]
 interface WakeLock : EventTarget {
-  [Exposed=Window] static Promise<PermissionState> requestPermission(WakeLockType type);
   readonly attribute WakeLockType type;
   readonly attribute boolean active;
   attribute EventHandler onactivechange;
   Promise<void> request(optional WakeLockRequestOptions options);
+  static sequence<WakeLock> query(optional WakeLockQueryFilter filter);
+  [Exposed=Window] static Promise<PermissionState> requestPermission(WakeLockType type);
 };
 
 dictionary WakeLockRequestOptions {
   AbortSignal? signal;
 };
+
+dictionary WakeLockQueryFilter {
+  WakeLockType? type;
+  boolean? active;
+};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webrtc-stats.idl b/third_party/blink/web_tests/external/wpt/interfaces/webrtc-stats.idl
index 0992b341..d9d2019 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webrtc-stats.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webrtc-stats.idl
@@ -102,7 +102,10 @@
              DOMString senderId;
              DOMString remoteId;
              DOMHighResTimeStamp lastPacketSentTimestamp;
+             unsigned long long retransmittedPacketsSent;
+             unsigned long long retransmittedBytesSent;
              double targetBitrate;
+             unsigned long long totalEncodedBytesTarget;
              unsigned long framesEncoded;
              unsigned long long qpSum;
              double totalEncodeTime;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl b/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
index 8a32640..4c74fdac 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
@@ -188,6 +188,13 @@
   readonly attribute XRTargetRayMode targetRayMode;
   readonly attribute XRSpace targetRaySpace;
   readonly attribute XRSpace? gripSpace;
+  readonly attribute Gamepad? gamepad;
+};
+
+enum GamepadMappingType {
+  "",            // Defined in the Gamepad API
+  "standard",    // Defined in the Gamepad API
+  "xr-standard",
 };
 
 [SecureContext, Exposed=Window] interface XRLayer {};
@@ -254,11 +261,13 @@
 interface XRInputSourceEvent : Event {
   readonly attribute XRFrame frame;
   readonly attribute XRInputSource inputSource;
+  readonly attribute long? buttonIndex;
 };
 
 dictionary XRInputSourceEventInit : EventInit {
   required XRFrame frame;
   required XRInputSource inputSource;
+  long? buttonIndex = null;
 };
 
 [SecureContext, Exposed=Window, Constructor(DOMString type, XRReferenceSpaceEventInit eventInitDict)]
diff --git a/third_party/blink/web_tests/external/wpt/url/resources/urltestdata.json b/third_party/blink/web_tests/external/wpt/url/resources/urltestdata.json
index 26b8ea2..bf4e2a7 100644
--- a/third_party/blink/web_tests/external/wpt/url/resources/urltestdata.json
+++ b/third_party/blink/web_tests/external/wpt/url/resources/urltestdata.json
@@ -4633,6 +4633,22 @@
     "search": "",
     "hash": ""
   },
+  "# unknown scheme with non-URL characters in the path",
+  {
+    "input": "wow:\uFFFF",
+    "base": "about:blank",
+    "href": "wow:%EF%BF%BF",
+    "origin": "null",
+    "protocol": "wow:",
+    "username": "",
+    "password": "",
+    "host": "",
+    "hostname": "",
+    "port": "",
+    "pathname": "%EF%BF%BF",
+    "search": "",
+    "hash": ""
+  },
   "# Hosts and percent-encoding",
   {
     "input": "ftp://example.com%80/",
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt
index ca88133a..3930e12 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any-expected.txt
@@ -6,19 +6,22 @@
 PASS WakeLock interface: existence and properties of interface prototype object
 PASS WakeLock interface: existence and properties of interface prototype object's "constructor" property
 PASS WakeLock interface: existence and properties of interface prototype object's @@unscopables property
-FAIL WakeLock interface: operation requestPermission(WakeLockType) assert_own_property: interface object missing static operation expected property "requestPermission" missing
 PASS WakeLock interface: attribute type
 PASS WakeLock interface: attribute active
 PASS WakeLock interface: attribute onactivechange
 FAIL WakeLock interface: operation request(WakeLockRequestOptions) assert_own_property: interface prototype object missing non-static operation expected property "request" missing
+FAIL WakeLock interface: operation query(WakeLockQueryFilter) assert_own_property: interface object missing static operation expected property "query" missing
+FAIL WakeLock interface: operation requestPermission(WakeLockType) assert_own_property: interface object missing static operation expected property "requestPermission" missing
 FAIL WakeLock must be primary interface of new WakeLock("screen") assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
 FAIL Stringification of new WakeLock("screen") assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
-FAIL WakeLock interface: new WakeLock("screen") must inherit property "requestPermission(WakeLockType)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
-FAIL WakeLock interface: calling requestPermission(WakeLockType) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
 FAIL WakeLock interface: new WakeLock("screen") must inherit property "type" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
 FAIL WakeLock interface: new WakeLock("screen") must inherit property "active" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
 FAIL WakeLock interface: new WakeLock("screen") must inherit property "onactivechange" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
 FAIL WakeLock interface: new WakeLock("screen") must inherit property "request(WakeLockRequestOptions)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
 FAIL WakeLock interface: calling request(WakeLockRequestOptions) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
+FAIL WakeLock interface: new WakeLock("screen") must inherit property "query(WakeLockQueryFilter)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
+FAIL WakeLock interface: calling query(WakeLockQueryFilter) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
+FAIL WakeLock interface: new WakeLock("screen") must inherit property "requestPermission(WakeLockType)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
+FAIL WakeLock interface: calling requestPermission(WakeLockType) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Illegal constructor"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt
index 78381b7..a17dfea9 100644
--- a/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/wake-lock/idlharness.https.any.worker-expected.txt
@@ -6,18 +6,21 @@
 FAIL WakeLock interface: existence and properties of interface prototype object assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing
 FAIL WakeLock interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing
 FAIL WakeLock interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing
-FAIL WakeLock interface: member requestPermission Cannot use 'in' operator to search for 'requestPermission' in undefined
 FAIL WakeLock interface: attribute type assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing
 FAIL WakeLock interface: attribute active assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing
 FAIL WakeLock interface: attribute onactivechange assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing
 FAIL WakeLock interface: operation request(WakeLockRequestOptions) assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing
+FAIL WakeLock interface: operation query(WakeLockQueryFilter) assert_own_property: self does not have own property "WakeLock" expected property "WakeLock" missing
+FAIL WakeLock interface: member requestPermission Cannot use 'in' operator to search for 'requestPermission' in undefined
 FAIL WakeLock must be primary interface of new WakeLock("screen") assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
 FAIL Stringification of new WakeLock("screen") assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
-FAIL WakeLock interface: new WakeLock("screen") must not have property "requestPermission" assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
 FAIL WakeLock interface: new WakeLock("screen") must inherit property "type" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
 FAIL WakeLock interface: new WakeLock("screen") must inherit property "active" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
 FAIL WakeLock interface: new WakeLock("screen") must inherit property "onactivechange" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
 FAIL WakeLock interface: new WakeLock("screen") must inherit property "request(WakeLockRequestOptions)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
 FAIL WakeLock interface: calling request(WakeLockRequestOptions) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
+FAIL WakeLock interface: new WakeLock("screen") must inherit property "query(WakeLockQueryFilter)" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
+FAIL WakeLock interface: calling query(WakeLockQueryFilter) on new WakeLock("screen") with too few arguments must throw TypeError assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
+FAIL WakeLock interface: new WakeLock("screen") must not have property "requestPermission" assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: WakeLock is not defined"
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
deleted file mode 100644
index 62c3abf..0000000
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded-expected.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-This is a testharness.js-based test.
-PASS Creating first data channel should fire negotiationneeded event
-PASS calling createDataChannel twice should fire negotiationneeded event once
-PASS addTransceiver() should fire negotiationneeded event
-FAIL Calling addTransceiver() twice should fire negotiationneeded event once assert_unreached: Pending promise should never be resolved. Instead it is fulfilled with: [object Object] Reached unreachable code
-FAIL Calling both addTransceiver() and createDataChannel() should fire negotiationneeded event once assert_unreached: Pending promise should never be resolved. Instead it is fulfilled with: [object Object] Reached unreachable code
-PASS negotiationneeded event should not fire if signaling state is not stable
-FAIL negotiationneeded event should fire only after signaling state go back to stable assert_false: negotiationneeded should not fire until the next iteration of the event loop after returning to stable expected false got true
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded.html
index f7bf8bd3..336b100 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-onnegotiationneeded.html
@@ -199,10 +199,133 @@
     pc.onnegotiationneeded = e => fired = true;
     pc.createDataChannel('test');
     const answer = await generateAnswer(offer);
-    await pc.setRemoteDescription(answer);
+    pc.setRemoteDescription(answer);
     assert_false(fired, "negotiationneeded should not fire until the next iteration of the event loop after returning to stable");
     await new Promise(resolve => pc.onnegotiationneeded = resolve);
-  }, 'negotiationneeded event should fire only after signaling state go back to stable');
+  }, 'negotiationneeded event should fire only after signaling state go back to stable after setRemoteDescription');
+
+  promise_test(async t => {
+    const callee = new RTCPeerConnection();
+    t.add_cleanup(() => callee.close());
+
+    const caller = new RTCPeerConnection();
+    t.add_cleanup(() => caller.close());
+
+    callee.addTransceiver('audio');
+
+    const offer = await caller.createOffer();
+    let fired = false;
+    callee.onnegotiationneeded = e => fired = true;
+    await callee.setRemoteDescription(offer);
+    callee.createDataChannel('test');
+
+    const answer = await callee.createAnswer(offer);
+    callee.setLocalDescription(answer);
+    assert_false(fired, "negotiationneeded should not fire until the next iteration of the event loop after returning to stable");
+
+    await new Promise(resolve => callee.onnegotiationneeded = resolve);
+  }, 'negotiationneeded event should fire only after signaling state go back to stable after setLocalDescription');
+
+  /*
+    5.1.  RTCPeerConnection Interface Extensions
+
+      addTrack
+        10. Update the negotiation-needed flag for connection.
+  */
+  promise_test(async t => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+
+    const stream = await getNoiseStream({ audio: true });
+    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+    const [track] = stream.getTracks();
+    pc.addTrack(track, stream);
+
+    await new Promise(resolve => pc.onnegotiationneeded = resolve);
+  }, 'addTrack should cause negotiationneeded to fire');
+
+  /*
+    5.1.  RTCPeerConnection Interface Extensions
+
+      removeTrack
+        12. Update the negotiation-needed flag for connection.
+  */
+  async_test(async t => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+
+    const stream = await getNoiseStream({ audio: true });
+    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+    const [track] = stream.getTracks();
+    const sender = pc.addTrack(track, stream);
+    pc.onnegotiationneeded = t.step_func(async () => {
+      pc.onnegotiationneeded = t.step_func(async () => {
+        assert_unreached('onnegotiationneeded misfired');
+      });
+      const offer = await pc.createOffer();
+      await pc.setLocalDescription(offer);
+
+      const answer = await generateAnswer(offer);
+      await pc.setRemoteDescription(answer);
+
+      pc.removeTrack(sender);
+      await new Promise(resolve => pc.onnegotiationneeded = resolve)
+      t.done();
+    });
+  }, 'removeTrack should cause negotiationneeded to fire on the caller');
+
+  /*
+    5.1.  RTCPeerConnection Interface Extensions
+
+      removeTrack
+        12. Update the negotiation-needed flag for connection.
+  */
+  async_test(async t => {
+    const caller = new RTCPeerConnection();
+    t.add_cleanup(() => caller.close());
+    caller.addTransceiver('audio', {direction:'recvonly'});
+    const offer = await caller.createOffer();
+
+    const callee = new RTCPeerConnection();
+    t.add_cleanup(() => callee.close());
+
+    const stream = await getNoiseStream({ audio: true });
+    t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
+    const [track] = stream.getTracks();
+    const sender = callee.addTrack(track, stream);
+
+    callee.onnegotiationneeded = t.step_func(async () => {
+      callee.onnegotiationneeded = t.step_func(async () => {
+        assert_unreached('onnegotiationneeded misfired');
+      });
+      await callee.setRemoteDescription(offer);
+      const answer = await callee.createAnswer(offer);
+      callee.setLocalDescription(answer);
+
+      callee.removeTrack(sender);
+      await new Promise(resolve => callee.onnegotiationneeded = resolve)
+      t.done();
+    });
+  }, 'removeTrack should cause negotiationneeded to fire on the callee');
+
+  /*
+    5.4.  RTCRtpTransceiver Interface
+
+      setDirection
+        7.  Update the negotiation-needed flag for connection.
+  */
+  promise_test(async t => {
+    const pc = new RTCPeerConnection();
+    t.add_cleanup(() => pc.close());
+
+    const transceiver = pc.addTransceiver('audio', {direction:'sendrecv'});
+    const offer = await pc.createOffer();
+    await pc.setLocalDescription(offer);
+    const answer = await generateAnswer(offer);
+    await pc.setRemoteDescription(answer);
+    transceiver.direction = 'recvonly';
+    await new Promise(resolve => pc.onnegotiationneeded = resolve);
+  }, 'Updating the direction of the transceiver should cause negotiationneeded to fire');
 
   /*
     TODO
@@ -243,19 +366,8 @@
       When the RTCPeerConnection() constructor is invoked
         7.  Let connection have a [[needNegotiation]] internal slot, initialized to false.
 
-    5.1.  RTCPeerConnection Interface Extensions
-
-      addTrack
-        10. Update the negotiation-needed flag for connection.
-
-      removeTrack
-        12. Update the negotiation-needed flag for connection.
-
     5.4.  RTCRtpTransceiver Interface
 
-      setDirection
-        7.  Update the negotiation-needed flag for connection.
-
       stop
         11. Update the negotiation-needed flag for connection.
 
diff --git a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
index a00bc36..93f6ab5a 100644
--- a/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webxr/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 216 tests; 203 PASS, 13 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 218 tests; 203 PASS, 15 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Partial interface Navigator: original interface defined
 PASS Partial dictionary WebGLContextAttributes: original dictionary defined
@@ -159,6 +159,7 @@
 PASS XRInputSource interface: attribute targetRayMode
 PASS XRInputSource interface: attribute targetRaySpace
 PASS XRInputSource interface: attribute gripSpace
+FAIL XRInputSource interface: attribute gamepad assert_true: The prototype object must have a property "gamepad" expected true got false
 PASS XRLayer interface: existence and properties of interface object
 PASS XRLayer interface object length
 PASS XRLayer interface object name
@@ -205,6 +206,7 @@
 PASS XRInputSourceEvent interface: existence and properties of interface prototype object's @@unscopables property
 PASS XRInputSourceEvent interface: attribute frame
 PASS XRInputSourceEvent interface: attribute inputSource
+FAIL XRInputSourceEvent interface: attribute buttonIndex assert_true: The prototype object must have a property "buttonIndex" expected true got false
 FAIL XRReferenceSpaceEvent interface: existence and properties of interface object assert_own_property: self does not have own property "XRReferenceSpaceEvent" expected property "XRReferenceSpaceEvent" missing
 FAIL XRReferenceSpaceEvent interface object length assert_own_property: self does not have own property "XRReferenceSpaceEvent" expected property "XRReferenceSpaceEvent" missing
 FAIL XRReferenceSpaceEvent interface object name assert_own_property: self does not have own property "XRReferenceSpaceEvent" expected property "XRReferenceSpaceEvent" missing
diff --git a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-AddRemoveStream.html b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-AddRemoveStream.html
index b830577..acc0209 100644
--- a/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-AddRemoveStream.html
+++ b/third_party/blink/web_tests/fast/peerconnection/RTCPeerConnection-AddRemoveStream.html
@@ -17,24 +17,22 @@
         assert_false(stream1.id === stream2.id);
 
         pc = new RTCPeerConnection();
+        testRTC.add_cleanup(() => pc.close());
 
         pc.onnegotiationneeded = (event) => {
             assert_array_equals(pc.getLocalStreams(), [stream1]);
 
-            pc.onnegotiationneeded = () => {
+            pc.onnegotiationneeded = testRTC.step_func(() => {
                 assert_unreached('onErroneousNegotiationNeeded was called.');
-            };
+            });
 
             pc.addStream(stream1);
             assert_equals(pc.getLocalStreams().length, 1);
             pc.removeStream(stream2);
             assert_equals(pc.getLocalStreams().length, 1);
 
-            pc.onnegotiationneeded = (event) => {
-                assert_equals(pc.getLocalStreams().length, 0);
-                testRTC.done();
-            };
             pc.removeStream(stream1);
+            setTimeout(() => testRTC.done());
         };
 
         pc.addStream(stream1);
diff --git a/third_party/blink/web_tests/paint/dark-mode/native-theme-off/text-input-elements.html b/third_party/blink/web_tests/paint/dark-mode/native-theme-off/text-input-elements.html
new file mode 100644
index 0000000..846b0912f
--- /dev/null
+++ b/third_party/blink/web_tests/paint/dark-mode/native-theme-off/text-input-elements.html
@@ -0,0 +1 @@
+<input style="border-width: thick"><textarea style="border-width: thick"></textarea>
diff --git a/third_party/blink/web_tests/paint/dark-mode/native-theme-on/text-input-elements.html b/third_party/blink/web_tests/paint/dark-mode/native-theme-on/text-input-elements.html
new file mode 100644
index 0000000..f2e3d4c
--- /dev/null
+++ b/third_party/blink/web_tests/paint/dark-mode/native-theme-on/text-input-elements.html
@@ -0,0 +1 @@
+<input><textarea></textarea>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-expected.txt
index 667b500..a9c7a84 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 334 PASS, 182 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 334 PASS, 183 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -360,6 +360,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 FAIL Parsing: <ftp://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%80/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <ftp://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%A0/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <https://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%80/" but got "https://example.com%EF%BF%BD/"
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-expected.txt
index 6a59534..2eff1d990 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 299 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 300 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Parsing origin: <sc::a@example.net> against <about:blank>
 PASS Parsing origin: <wow:%NBD> against <about:blank>
 PASS Parsing origin: <wow:%1G> against <about:blank>
+PASS Parsing origin: <wow:￿> against <about:blank>
 PASS Parsing origin: <ftp://%e2%98%83> against <about:blank>
 PASS Parsing origin: <https://%e2%98%83> against <about:blank>
 PASS Parsing origin: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-xhtml-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-xhtml-expected.txt
index 6a59534..2eff1d990 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-origin-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 299 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 300 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Parsing origin: <sc::a@example.net> against <about:blank>
 PASS Parsing origin: <wow:%NBD> against <about:blank>
 PASS Parsing origin: <wow:%1G> against <about:blank>
+PASS Parsing origin: <wow:￿> against <about:blank>
 PASS Parsing origin: <ftp://%e2%98%83> against <about:blank>
 PASS Parsing origin: <https://%e2%98%83> against <about:blank>
 PASS Parsing origin: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-xhtml-expected.txt
index 667b500..a9c7a84 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 334 PASS, 182 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 334 PASS, 183 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -360,6 +360,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 FAIL Parsing: <ftp://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%80/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <ftp://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%A0/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <https://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%80/" but got "https://example.com%EF%BF%BD/"
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor-expected.txt
index 4450c6f..1938487b 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-constructor-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 400 PASS, 116 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 400 PASS, 117 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -400,6 +400,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 PASS Parsing: <ftp://example.com%80/> against <about:blank>
 PASS Parsing: <ftp://example.com%A0/> against <about:blank>
 PASS Parsing: <https://example.com%80/> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-origin-expected.txt b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-origin-expected.txt
index 7880e90..72594ff 100644
--- a/third_party/blink/web_tests/platform/linux/external/wpt/url/url-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/external/wpt/url/url-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 296 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 297 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Origin parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Origin parsing: <sc::a@example.net> against <about:blank>
 PASS Origin parsing: <wow:%NBD> against <about:blank>
 PASS Origin parsing: <wow:%1G> against <about:blank>
+PASS Origin parsing: <wow:￿> against <about:blank>
 PASS Origin parsing: <ftp://%e2%98%83> against <about:blank>
 PASS Origin parsing: <https://%e2%98%83> against <about:blank>
 PASS Origin parsing: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/linux/paint/dark-mode/native-theme-off/text-input-elements-expected.png b/third_party/blink/web_tests/platform/linux/paint/dark-mode/native-theme-off/text-input-elements-expected.png
new file mode 100644
index 0000000..9adc149
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/paint/dark-mode/native-theme-off/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/paint/dark-mode/native-theme-on/text-input-elements-expected.png b/third_party/blink/web_tests/platform/linux/paint/dark-mode/native-theme-on/text-input-elements-expected.png
new file mode 100644
index 0000000..8315438
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/paint/dark-mode/native-theme-on/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png
new file mode 100644
index 0000000..0e3e22f0
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png b/third_party/blink/web_tests/platform/linux/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png
new file mode 100644
index 0000000..310a6f2
--- /dev/null
+++ b/third_party/blink/web_tests/platform/linux/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-expected.txt
index 667b500..a9c7a84 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 334 PASS, 182 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 334 PASS, 183 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -360,6 +360,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 FAIL Parsing: <ftp://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%80/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <ftp://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%A0/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <https://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%80/" but got "https://example.com%EF%BF%BD/"
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-expected.txt
index 6a59534..2eff1d990 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 299 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 300 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Parsing origin: <sc::a@example.net> against <about:blank>
 PASS Parsing origin: <wow:%NBD> against <about:blank>
 PASS Parsing origin: <wow:%1G> against <about:blank>
+PASS Parsing origin: <wow:￿> against <about:blank>
 PASS Parsing origin: <ftp://%e2%98%83> against <about:blank>
 PASS Parsing origin: <https://%e2%98%83> against <about:blank>
 PASS Parsing origin: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-xhtml-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-xhtml-expected.txt
index 6a59534..2eff1d990 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-origin-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 299 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 300 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Parsing origin: <sc::a@example.net> against <about:blank>
 PASS Parsing origin: <wow:%NBD> against <about:blank>
 PASS Parsing origin: <wow:%1G> against <about:blank>
+PASS Parsing origin: <wow:￿> against <about:blank>
 PASS Parsing origin: <ftp://%e2%98%83> against <about:blank>
 PASS Parsing origin: <https://%e2%98%83> against <about:blank>
 PASS Parsing origin: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
index 667b500..a9c7a84 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 334 PASS, 182 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 334 PASS, 183 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -360,6 +360,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 FAIL Parsing: <ftp://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%80/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <ftp://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%A0/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <https://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%80/" but got "https://example.com%EF%BF%BD/"
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor-expected.txt
index 4450c6f..1938487b 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-constructor-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 400 PASS, 116 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 400 PASS, 117 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -400,6 +400,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 PASS Parsing: <ftp://example.com%80/> against <about:blank>
 PASS Parsing: <ftp://example.com%A0/> against <about:blank>
 PASS Parsing: <https://example.com%80/> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-origin-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-origin-expected.txt
index 7880e90..72594ff 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/url/url-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/url/url-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 296 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 297 PASS, 7 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Origin parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Origin parsing: <sc::a@example.net> against <about:blank>
 PASS Origin parsing: <wow:%NBD> against <about:blank>
 PASS Origin parsing: <wow:%1G> against <about:blank>
+PASS Origin parsing: <wow:￿> against <about:blank>
 PASS Origin parsing: <ftp://%e2%98%83> against <about:blank>
 PASS Origin parsing: <https://%e2%98%83> against <about:blank>
 PASS Origin parsing: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/mac/paint/dark-mode/native-theme-off/text-input-elements-expected.png b/third_party/blink/web_tests/platform/mac/paint/dark-mode/native-theme-off/text-input-elements-expected.png
new file mode 100644
index 0000000..0d5bd5a
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/paint/dark-mode/native-theme-off/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/dark-mode/native-theme-on/text-input-elements-expected.png b/third_party/blink/web_tests/platform/mac/paint/dark-mode/native-theme-on/text-input-elements-expected.png
new file mode 100644
index 0000000..b71efac
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/paint/dark-mode/native-theme-on/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png
new file mode 100644
index 0000000..8dd35947
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png
new file mode 100644
index 0000000..80b2276
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
index 57dca08..ac76307 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 330 PASS, 186 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 330 PASS, 187 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -360,6 +360,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 FAIL Parsing: <ftp://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%80/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <ftp://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%A0/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <https://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%80/" but got "https://example.com%EF%BF%BD/"
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-expected.txt
index f56cdf4..1955d9f 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 297 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 298 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Parsing origin: <sc::a@example.net> against <about:blank>
 PASS Parsing origin: <wow:%NBD> against <about:blank>
 PASS Parsing origin: <wow:%1G> against <about:blank>
+PASS Parsing origin: <wow:￿> against <about:blank>
 PASS Parsing origin: <ftp://%e2%98%83> against <about:blank>
 PASS Parsing origin: <https://%e2%98%83> against <about:blank>
 PASS Parsing origin: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-xhtml-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-xhtml-expected.txt
index f56cdf4..1955d9f 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-origin-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 297 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 298 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing origin: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Parsing origin: <sc::a@example.net> against <about:blank>
 PASS Parsing origin: <wow:%NBD> against <about:blank>
 PASS Parsing origin: <wow:%1G> against <about:blank>
+PASS Parsing origin: <wow:￿> against <about:blank>
 PASS Parsing origin: <ftp://%e2%98%83> against <about:blank>
 PASS Parsing origin: <https://%e2%98%83> against <about:blank>
 PASS Parsing origin: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
index 57dca08..ac76307 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/a-element-xhtml-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 330 PASS, 186 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 330 PASS, 187 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -360,6 +360,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 FAIL Parsing: <ftp://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%80/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <ftp://example.com%A0/> against <about:blank> assert_equals: failure should set href to input expected "ftp://example.com%A0/" but got "ftp://example.com%EF%BF%BD/"
 FAIL Parsing: <https://example.com%80/> against <about:blank> assert_equals: failure should set href to input expected "https://example.com%80/" but got "https://example.com%EF%BF%BD/"
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor-expected.txt
index 1d9467c..2aa72cb 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/url-constructor-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 516 tests; 394 PASS, 122 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 517 tests; 394 PASS, 123 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -400,6 +400,7 @@
 PASS Parsing: <sc::a@example.net> against <about:blank>
 PASS Parsing: <wow:%NBD> against <about:blank>
 PASS Parsing: <wow:%1G> against <about:blank>
+FAIL Parsing: <wow:￿> against <about:blank> assert_equals: href expected "wow:%EF%BF%BF" but got "wow:%EF%BF%BD"
 PASS Parsing: <ftp://example.com%80/> against <about:blank>
 PASS Parsing: <ftp://example.com%A0/> against <about:blank>
 PASS Parsing: <https://example.com%80/> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/url/url-origin-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/url/url-origin-expected.txt
index e7b1e0122..616310c9 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/url/url-origin-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/url/url-origin-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 303 tests; 294 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 304 tests; 295 PASS, 9 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS Loading data…
 PASS Origin parsing: <http://example	.
 org> against <http://example.org/foo/bar>
@@ -262,6 +262,7 @@
 PASS Origin parsing: <sc::a@example.net> against <about:blank>
 PASS Origin parsing: <wow:%NBD> against <about:blank>
 PASS Origin parsing: <wow:%1G> against <about:blank>
+PASS Origin parsing: <wow:￿> against <about:blank>
 PASS Origin parsing: <ftp://%e2%98%83> against <about:blank>
 PASS Origin parsing: <https://%e2%98%83> against <about:blank>
 PASS Origin parsing: <http://127.0.0.1:10100/relative_import.html> against <about:blank>
diff --git a/third_party/blink/web_tests/platform/win/paint/dark-mode/native-theme-off/text-input-elements-expected.png b/third_party/blink/web_tests/platform/win/paint/dark-mode/native-theme-off/text-input-elements-expected.png
new file mode 100644
index 0000000..67a425c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/paint/dark-mode/native-theme-off/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/dark-mode/native-theme-on/text-input-elements-expected.png b/third_party/blink/web_tests/platform/win/paint/dark-mode/native-theme-on/text-input-elements-expected.png
new file mode 100644
index 0000000..c52b4fd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/paint/dark-mode/native-theme-on/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png
new file mode 100644
index 0000000..5b4aadf
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-mode/paint/dark-mode/native-theme-off/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png b/third_party/blink/web_tests/platform/win/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png
new file mode 100644
index 0000000..8257781
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/virtual/dark-mode/paint/dark-mode/native-theme-on/text-input-elements-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/native-theme-off/README.txt b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/native-theme-off/README.txt
new file mode 100644
index 0000000..cb49f2e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/native-theme-off/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in LayoutTests/paint/dark-mode
+# with --blink-settings="darkMode=3"
+# See the virtual_test_suites() method in tools/blinkpy/web_tests/port/base.py.
diff --git a/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/native-theme-on/README.txt b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/native-theme-on/README.txt
new file mode 100644
index 0000000..cb49f2e
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/dark-mode/paint/dark-mode/native-theme-on/README.txt
@@ -0,0 +1,3 @@
+# This suite runs the tests in LayoutTests/paint/dark-mode
+# with --blink-settings="darkMode=3"
+# See the virtual_test_suites() method in tools/blinkpy/web_tests/port/base.py.
diff --git a/third_party/cacheinvalidation/src/google/cacheinvalidation/BUILD.gn b/third_party/cacheinvalidation/src/google/cacheinvalidation/BUILD.gn
index 3bbb844e..fc92fb5 100644
--- a/third_party/cacheinvalidation/src/google/cacheinvalidation/BUILD.gn
+++ b/third_party/cacheinvalidation/src/google/cacheinvalidation/BUILD.gn
@@ -23,5 +23,6 @@
     ]
   }
 
-  proto_out_dir = "google/cacheinvalidation"
+  proto_out_dir = "$target_gen_dir/google/cacheinvalidation"
+  public_include_dirs = [ target_gen_dir ]
 }
diff --git a/third_party/libaddressinput/chromium/chrome_address_validator_unittest.cc b/third_party/libaddressinput/chromium/chrome_address_validator_unittest.cc
index 4116f5d..8afbe82 100644
--- a/third_party/libaddressinput/chromium/chrome_address_validator_unittest.cc
+++ b/third_party/libaddressinput/chromium/chrome_address_validator_unittest.cc
@@ -50,6 +50,7 @@
 using ::i18n::addressinput::MISSING_REQUIRED_FIELD;
 using ::i18n::addressinput::UNEXPECTED_FIELD;
 using ::i18n::addressinput::UNKNOWN_VALUE;
+using ::i18n::addressinput::UNSUPPORTED_FIELD;
 using ::i18n::addressinput::USES_P_O_BOX;
 
 // This class should always succeed in getting the rules.
@@ -248,55 +249,51 @@
   FieldProblemMap problems;
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_TRUE(problems.empty());
 
+  FieldProblemMap expected;
+  expected.emplace(LOCALITY, UNSUPPORTED_FIELD);
+  expected.emplace(DEPENDENT_LOCALITY, UNSUPPORTED_FIELD);
+  EXPECT_EQ(expected, problems);
   problems.clear();
 
   // An extended, valid Californian zip code.
   address.postal_code = "90210-1234";
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_TRUE(problems.empty());
-
+  EXPECT_EQ(expected, problems);
   problems.clear();
 
   // New York zip code (which is invalid for California).
   address.postal_code = "12345";
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_EQ(1U, problems.size());
-  EXPECT_EQ(problems.begin()->first, POSTAL_CODE);
-  EXPECT_EQ(problems.begin()->second, MISMATCHING_VALUE);
-
+  expected.emplace(POSTAL_CODE, MISMATCHING_VALUE);
+  EXPECT_EQ(expected, problems);
   problems.clear();
 
   // A zip code with a "90" in the middle.
   address.postal_code = "12903";
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_EQ(1U, problems.size());
-  EXPECT_EQ(problems.begin()->first, POSTAL_CODE);
-  EXPECT_EQ(problems.begin()->second, MISMATCHING_VALUE);
-
+  EXPECT_EQ(expected, problems);
   problems.clear();
 
   // Invalid zip code (too many digits).
   address.postal_code = "902911";
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_EQ(1U, problems.size());
-  EXPECT_EQ(problems.begin()->first, POSTAL_CODE);
-  EXPECT_EQ(problems.begin()->second, INVALID_FORMAT);
-
+  expected.clear();
+  expected.emplace(LOCALITY, UNSUPPORTED_FIELD);
+  expected.emplace(DEPENDENT_LOCALITY, UNSUPPORTED_FIELD);
+  expected.emplace(POSTAL_CODE, INVALID_FORMAT);
+  EXPECT_EQ(expected, problems);
   problems.clear();
 
   // Invalid zip code (too few digits).
   address.postal_code = "9029";
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_EQ(1U, problems.size());
-  EXPECT_EQ(problems.begin()->first, POSTAL_CODE);
-  EXPECT_EQ(problems.begin()->second, INVALID_FORMAT);
+  EXPECT_EQ(expected, problems);
 }
 
 TEST_F(AddressValidatorTest, BasicValidation) {
@@ -313,35 +310,39 @@
   FieldProblemMap problems;
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_TRUE(problems.empty());
+
+  FieldProblemMap expected;
+  expected.emplace(LOCALITY, UNSUPPORTED_FIELD);
+  expected.emplace(DEPENDENT_LOCALITY, UNSUPPORTED_FIELD);
+  EXPECT_EQ(expected, problems);
 
   // The display name works as well as the key.
   address.administrative_area = "Texas";
   problems.clear();
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_TRUE(problems.empty());
+  EXPECT_EQ(expected, problems);
 
   // Ignore capitalization.
   address.administrative_area = "tx";
   problems.clear();
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_TRUE(problems.empty());
+  EXPECT_EQ(expected, problems);
 
   // Ignore capitalization.
   address.administrative_area = "teXas";
   problems.clear();
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_TRUE(problems.empty());
+  EXPECT_EQ(expected, problems);
 
   // Ignore diacriticals.
   address.administrative_area = base::WideToUTF8(L"T\u00E9xas");
   problems.clear();
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
-  EXPECT_TRUE(problems.empty());
+  EXPECT_EQ(expected, problems);
 }
 
 TEST_F(AddressValidatorTest, BasicValidationFailure) {
@@ -359,9 +360,13 @@
   EXPECT_EQ(AddressValidator::SUCCESS,
             validator_->ValidateAddress(address, NULL, &problems));
 
-  ASSERT_EQ(1U, problems.size());
-  EXPECT_EQ(UNKNOWN_VALUE, problems.begin()->second);
-  EXPECT_EQ(ADMIN_AREA, problems.begin()->first);
+  ASSERT_EQ(3U, problems.size());
+
+  FieldProblemMap expected;
+  expected.emplace(ADMIN_AREA, UNKNOWN_VALUE);
+  expected.emplace(LOCALITY, UNSUPPORTED_FIELD);
+  expected.emplace(DEPENDENT_LOCALITY, UNSUPPORTED_FIELD);
+  EXPECT_EQ(expected, problems);
 }
 
 TEST_F(AddressValidatorTest, NoNullSuggestionsCrash) {
@@ -912,7 +917,8 @@
 
  private:
   // LoadRulesListener implementation.
-  void OnAddressValidationRulesLoaded(const std::string&, bool success) override {
+  void OnAddressValidationRulesLoaded(const std::string&,
+                                      bool success) override {
     load_rules_success_ = success;
   }
 
diff --git a/third_party/protobuf/proto_library.gni b/third_party/protobuf/proto_library.gni
index 5c4ffa01..9798fd68 100644
--- a/third_party/protobuf/proto_library.gni
+++ b/third_party/protobuf/proto_library.gni
@@ -16,8 +16,8 @@
 #
 #   proto_out_dir (optional)
 #       Specifies the path suffix that output files are generated under.
-#       This path will be appended to |root_gen_dir|, but for python stubs
-#       it will be appended to |root_build_dir|/pyproto.
+#       If a relative path is provided, it be appended to |root_gen_dir|.
+#       For python stubs it will be appended to |root_build_dir|/pyproto.
 #
 #   generate_python (optional, default true)
 #       Generate Python protobuf stubs.
@@ -98,6 +98,9 @@
 #       A list of config labels that will be removed from the configs apllying
 #       to the source set.
 #
+#   public_include_dirs (optional)
+#       Add a public config with given include_dirs.
+#
 # Example:
 #  proto_library("mylib") {
 #    sources = [
@@ -107,6 +110,11 @@
 
 import("//build/config/sanitizers/sanitizers.gni")
 
+declare_args() {
+  # TODO(agrieve): Remove arg after all offenders have been fixed.
+  allow_proto_library_outside_of_target_gen_dir = true
+}
+
 template("proto_library") {
   assert(defined(invoker.sources), "Need sources for proto_library")
   proto_sources = invoker.sources
@@ -188,22 +196,65 @@
 
   if (defined(invoker.proto_out_dir)) {
     proto_out_dir = invoker.proto_out_dir
-  } else {
-    # Absolute path to the directory of current BUILD.gn file excluding "//".
-    proto_out_dir = rebase_path(".", "//")
-    if (proto_in_dir != ".") {
-      proto_out_dir += "/$proto_in_dir"
-    }
+
+    # TODO(agrieve): Remove special case once perfetto is updated.
+    # Note: Other perfetto paths already pass this check because
+    # get_path_info normalizes "a//b" -> "a/b".
+    proto_out_dir_is_abs =
+        proto_out_dir != "//third_party/perfetto/" &&
+        get_path_info(proto_out_dir, "abspath") == proto_out_dir
   }
 
   # We need both absolute path to use in GN statements and a relative one
   # to pass to external script.
   if (generate_cc || generate_with_plugin) {
-    cc_out_dir = "$root_gen_dir/" + proto_out_dir
+    if (defined(proto_out_dir)) {
+      if (!proto_out_dir_is_abs) {
+        proto_out_dir = "$root_gen_dir/$proto_out_dir"
+      }
+
+      if (!allow_proto_library_outside_of_target_gen_dir) {
+        # Use sources filter to test the prefix of proto_out_dir.
+        set_sources_assignment_filter([
+                                        target_gen_dir,
+                                        "$target_gen_dir/*",
+                                      ])
+        sources = [
+          proto_out_dir,
+        ]
+        set_sources_assignment_filter([])
+        if (sources != []) {
+          # TODO(agrieve): Change this to an assert.
+          print(
+              "proto_out_dir must be subdirectory of \$target_gen_dir (https://crbug.com/944928)")
+          print("    target_gen_dir=$target_gen_dir")
+          print("    proto_out_dir=$proto_out_dir")
+          sources = []
+        }
+      }
+
+      cc_out_dir = proto_out_dir
+    } else {
+      cc_out_dir = target_gen_dir
+      if (rebase_path(".", "//") != ".") {
+        cc_out_dir += "/$proto_in_dir"
+      }
+    }
     rel_cc_out_dir = rebase_path(cc_out_dir, root_build_dir)
   }
   if (generate_python) {
-    py_out_dir = "$root_out_dir/pyproto/" + proto_out_dir
+    if (defined(proto_out_dir) && proto_out_dir_is_abs) {
+      py_out_dir = proto_out_dir
+    } else {
+      if (!defined(proto_out_dir)) {
+        # Absolute path to the directory of current BUILD.gn file excluding "//".
+        proto_out_dir = rebase_path(".", "//")
+        if (proto_in_dir != ".") {
+          proto_out_dir += "/$proto_in_dir"
+        }
+      }
+      py_out_dir = "$root_out_dir/pyproto/" + proto_out_dir
+    }
     rel_py_out_dir = rebase_path(py_out_dir, root_build_dir)
   }
 
@@ -232,7 +283,7 @@
     }
   }
 
-  action_name = "${target_name}_gen"
+  action_name = "${target_name}__gen"
   source_set_name = target_name
 
   # Generate protobuf stubs.
@@ -344,15 +395,21 @@
   # use relative paths starting at |cc_out_dir|.
   # However there is no necessity to add an additional directory, if all protos
   # are located in the same directory which is in the search path by default.
-  config_name = "${target_name}_config"
-  config(config_name) {
-    include_dirs = []
-    if (has_nested_dirs) {
-      include_dirs += [ cc_out_dir ]
-    }
-    if (defined(invoker.import_dirs)) {
-      foreach(path, invoker.import_dirs) {
-        include_dirs += [ "$root_gen_dir/" + rebase_path(path, "//") ]
+  if (has_nested_dirs || defined(invoker.import_dirs) ||
+      defined(invoker.public_include_dirs)) {
+    config_name = "${target_name}__config"
+    config(config_name) {
+      include_dirs = []
+      if (has_nested_dirs) {
+        include_dirs += [ cc_out_dir ]
+      }
+      if (defined(invoker.import_dirs)) {
+        foreach(path, invoker.import_dirs) {
+          include_dirs += [ "$root_gen_dir/" + rebase_path(path, "//") ]
+        }
+      }
+      if (defined(invoker.public_include_dirs)) {
+        include_dirs += invoker.public_include_dirs
       }
     }
   }
@@ -387,7 +444,7 @@
 
     if (generate_cc || generate_with_plugin) {
       # Not necessary if all protos are located in the same directory.
-      if (has_nested_dirs || defined(invoker.import_dirs)) {
+      if (defined(config_name)) {
         # It's not enough to set |include_dirs| for target since public imports
         # expose corresponding includes to header files as well.
         public_configs += [ ":$config_name" ]
diff --git a/tools/clang/scripts/build_clang_tools_extra.py b/tools/clang/scripts/build_clang_tools_extra.py
new file mode 100755
index 0000000..12db0ad3
--- /dev/null
+++ b/tools/clang/scripts/build_clang_tools_extra.py
@@ -0,0 +1,91 @@
+#!/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.
+
+"""A script for fetching LLVM monorepo and building clang-tools-extra binaries.
+
+Example: build clangd and clangd-indexer
+
+   tools/clang/scripts/build_clang_tools_extra.py --fetch out/Release clangd \
+       clangd-indexer
+"""
+
+import argparse
+import errno
+import os
+import subprocess
+import sys
+
+
+def GetCheckoutDir(out_dir):
+  """Returns absolute path to the checked-out llvm repo."""
+  return os.path.join(out_dir, 'tools', 'clang', 'third_party', 'llvm')
+
+
+def GetBuildDir(out_dir):
+  return os.path.join(GetCheckoutDir(out_dir), 'build')
+
+
+def CreateDirIfNotExists(dir):
+  if not os.path.exists(dir):
+    os.makedirs(dir)
+
+
+def FetchLLVM(checkout_dir):
+  """Clone llvm repo into |out_dir| or update if it already exists."""
+  CreateDirIfNotExists(os.path.dirname(checkout_dir))
+
+  try:
+    # First, try to clone the repo.
+    args = [
+        'git',
+        'clone',
+        'https://github.com/llvm/llvm-project.git',
+        checkout_dir,
+    ]
+    subprocess.check_call(args, shell=sys.platform == 'win32')
+  except subprocess.CalledProcessError:
+    # Otherwise, try to update it.
+    print('-- Attempting to update existing repo')
+    args = ['git', 'pull', '--rebase', 'origin', 'master']
+    subprocess.check_call(args, cwd=checkout_dir)
+
+
+def BuildTargets(build_dir, targets):
+  """Build targets from llvm repo at |build_dir|."""
+  CreateDirIfNotExists(build_dir)
+
+  # From that dir, run cmake
+  cmake_args = [
+      'cmake',
+      '-GNinja',
+      '-DLLVM_ENABLE_PROJECTS=clang;clang-tools-extra',
+      '-DCMAKE_BUILD_TYPE=Release',
+      '-DLLVM_ENABLE_ASSERTIONS=On',
+      '../llvm',
+  ]
+  subprocess.check_call(cmake_args, cwd=build_dir)
+
+  ninja_commands = ['ninja'] + targets
+  subprocess.check_call(ninja_commands, cwd=build_dir)
+
+
+def main():
+  parser = argparse.ArgumentParser(description='Build clang_tools_extra.')
+  parser.add_argument(
+      '--fetch', action='store_true', help='fetch LLVM source')
+  parser.add_argument('OUT_DIR', help='where we put the LLVM source repository')
+  parser.add_argument('TARGETS', nargs='+', help='targets being built')
+  args = parser.parse_args()
+
+  if args.fetch:
+    print 'Fetching LLVM source'
+    FetchLLVM(GetCheckoutDir(args.OUT_DIR))
+
+  print 'Building targets: %s' % ', '.join(args.TARGETS)
+  BuildTargets(GetBuildDir(args.OUT_DIR), args.TARGETS)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/clang/scripts/clang_tidy_tool.py b/tools/clang/scripts/clang_tidy_tool.py
index fae2f66..7bc16e2 100755
--- a/tools/clang/scripts/clang_tidy_tool.py
+++ b/tools/clang/scripts/clang_tidy_tool.py
@@ -28,14 +28,7 @@
 import subprocess
 import sys
 
-
-def GetCheckoutDir(out_dir):
-  """Returns absolute path to the checked-out llvm repo."""
-  return os.path.join(out_dir, 'tools', 'clang', 'third_party', 'llvm')
-
-
-def GetBuildDir(out_dir):
-  return os.path.join(GetCheckoutDir(out_dir), 'build')
+import build_clang_tools_extra
 
 
 def GetBinaryPath(build_dir, binary):
@@ -43,57 +36,6 @@
     binary += '.exe'
   return os.path.join(build_dir, 'bin', binary)
 
-def FetchClang(checkout_dir):
-  """Clone llvm repo into |out_dir| or update if it already exists."""
-  try:
-    # Create parent directories of the checkout directory
-    os.makedirs(os.path.dirname(checkout_dir))
-  except OSError:
-    pass
-
-  try:
-    # First, try to clone the repo.
-    args = [
-        'git',
-        'clone',
-        'https://github.com/llvm/llvm-project.git',
-        checkout_dir,
-    ]
-    subprocess.check_call(args, shell=sys.platform == 'win32')
-  except subprocess.CalledProcessError:
-    # Otherwise, try to update it.
-    print('-- Attempting to update existing repo')
-    args = ['git', 'pull', '--rebase', 'origin', 'master']
-    subprocess.check_call(args, cwd=checkout_dir)
-
-
-def BuildClang(build_dir):
-  """Build clang from llvm repo at |build_dir|."""
-  # Make <checkout>/build directory
-  try:
-    os.mkdir(build_dir)
-  except OSError as e:
-    # Ignore errno 17 'File Exists'
-    if e.errno != 17:
-      raise e
-
-  # From that dir, run cmake
-  cmake_args = [
-      'cmake',
-      '-GNinja',
-      '-DLLVM_ENABLE_PROJECTS=clang;clang-tools-extra',
-      '-DCMAKE_BUILD_TYPE=Release',
-      '../llvm',
-  ]
-  subprocess.check_call(cmake_args, cwd=build_dir)
-
-  ninja_args = [
-      'ninja',
-      'clang-tidy',
-      'clang-apply-replacements',
-  ]
-  subprocess.check_call(ninja_args, cwd=build_dir)
-
 
 def BuildNinjaTarget(out_dir, ninja_target):
   args = ['autoninja', '-C', out_dir, ninja_target]
@@ -228,9 +170,9 @@
   # If the user hasn't provided a clang checkout and build dir, checkout and
   # build clang-tidy where update.py would.
   if not args.clang_src_dir:
-    args.clang_src_dir = GetCheckoutDir(args.OUT_DIR)
+    args.clang_src_dir = build_clang_tools_extra.GetCheckoutDir(args.OUT_DIR)
   if not args.clang_build_dir:
-    args.clang_build_dir = GetBuildDir(args.OUT_DIR)
+    args.clang_build_dir = build_clang_tools_extra.GetBuildDir(args.OUT_DIR)
   elif (args.clang_build_dir and not
         os.path.isfile(GetBinaryPath(args.clang_build_dir, 'clang-tidy'))):
     sys.exit('clang-tidy binary doesn\'t exist at ' +
@@ -238,11 +180,14 @@
 
 
   if args.fetch:
-    steps.append(('Fetching clang sources', lambda:
-                  FetchClang(args.clang_src_dir)))
+    steps.append(('Fetching LLVM sources', lambda:
+                  build_clang_tools_extra.FetchLLVM(args.clang_src_dir)))
 
   if args.build:
-    steps.append(('Building clang', lambda: BuildClang(args.clang_build_dir)))
+    steps.append(('Building clang-tidy',
+                  lambda: build_clang_tools_extra.BuildTargets(
+                      args.clang_build_dir,
+                      ['clang-tidy', 'clang-apply-replacements'])))
 
   steps += [
       ('Building ninja target: %s' % args.NINJA_TARGET,
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 10b061f..5e5f4bf 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -35,7 +35,7 @@
 # Do NOT CHANGE this if you don't know what you're doing -- see
 # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
-CLANG_REVISION = '356356'
+CLANG_REVISION = '357692'
 
 use_head_revision = bool(os.environ.get('LLVM_FORCE_HEAD_REVISION', '0')
                          in ('1', 'YES'))
@@ -43,7 +43,7 @@
   CLANG_REVISION = 'HEAD'
 
 # This is incremented when pushing a new build of Clang at the same revision.
-CLANG_SUB_REVISION=3
+CLANG_SUB_REVISION=1
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6de4664a..83e353f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -33238,6 +33238,7 @@
   <int value="-181279574" label="NativeFilesystemAPI:enabled"/>
   <int value="-181093956" label="ScrollAnchoring:enabled"/>
   <int value="-180481252" label="CompositorThreadedScrollbarScrolling:enabled"/>
+  <int value="-175666252" label="Portals:disabled"/>
   <int value="-174706795"
       label="WebPaymentsPerMethodCanMakePaymentQuota:enabled"/>
   <int value="-174564579"
@@ -33368,6 +33369,7 @@
   <int value="7533886" label="disable-offer-store-unmasked-wallet-cards"/>
   <int value="10458238" label="disable-print-preview-simplify"/>
   <int value="11698808" label="enable-dom-distiller-button-animation"/>
+  <int value="15614295" label="Portals:enabled"/>
   <int value="19629326" label="OmniboxExperimentalKeywordMode:enabled"/>
   <int value="19815558" label="EnableSettingsShortcutSearch:disabled"/>
   <int value="23556595" label="MarkHttpAs:enabled"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index be720c7..55bea1c 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -112269,10 +112269,24 @@
 </histogram>
 
 <histogram name="Signin.Reconciler.Duration">
+  <obsolete>
+    Deprecated in favor of Signin.Reconciler.Duration.UpTo3mins.
+  </obsolete>
   <owner>rogerta@chromium.org</owner>
+  <owner>msarda@chromium.org</owner>
   <summary>Records the execution time of the account reconciler.</summary>
 </histogram>
 
+<histogram base="true" name="Signin.Reconciler.Duration.UpTo3mins" units="ms"
+    expires_after="2021-04-30">
+  <owner>droger@chromium.org</owner>
+  <owner>msarda@chromium.org</owner>
+  <summary>
+    Records the execution time of the account reconciler using 100 buckets, up
+    to 3 minutes.
+  </summary>
+</histogram>
+
 <histogram name="Signin.Reconciler.ExternalCcResultTime.Completed">
   <owner>msarda@chromium.org</owner>
   <owner>droger@chromium.org</owner>
@@ -150259,11 +150273,20 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="Signin.Reconciler.Duration" separator=".">
+  <obsolete>
+    Deprecated in favor of Signin.Reconciler.Duration.UpTo3mins.
+  </obsolete>
   <suffix name="Failure" label="Failed execution of reconciler"/>
   <suffix name="Success" label="Successful execution of reconciler"/>
   <affected-histogram name="Signin.Reconciler.Duration"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="Signin.Reconciler.Duration.UpTo3mins" separator=".">
+  <suffix name="Failure" label="Failed execution of reconciler"/>
+  <suffix name="Success" label="Successful execution of reconciler"/>
+  <affected-histogram name="Signin.Reconciler.Duration.UpTo3mins"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="SigninAccountStatus" separator=".">
   <suffix name="NewAccount" label="Using a new account, in the sign-in promo.">
     <obsolete>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 998ccfb70..7bf87f405 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -2405,6 +2405,7 @@
     <aggregation>
       <history>
         <index fields="profile.country"/>
+        <index fields="profile.country,profile.system_ram"/>
         <statistics>
           <quantiles type="std-percentiles"/>
         </statistics>
@@ -2443,6 +2444,7 @@
     <aggregation>
       <history>
         <index fields="profile.country"/>
+        <index fields="profile.country,profile.system_ram"/>
         <statistics>
           <quantiles type="std-percentiles"/>
         </statistics>
diff --git a/tools/traffic_annotation/auditor/BUILD.gn b/tools/traffic_annotation/auditor/BUILD.gn
index 5f01c32..7931a890 100644
--- a/tools/traffic_annotation/auditor/BUILD.gn
+++ b/tools/traffic_annotation/auditor/BUILD.gn
@@ -9,8 +9,6 @@
 assert(is_win || is_linux)
 
 proto_library("chrome_settings_full_runtime") {
-  proto_out_dir = "/tools/traffic_annotation"
-
   cc_include = "components/policy/proto/policy_proto_export.h"
 
   sources = [
diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn
index a8b692a..8ce6a2e 100644
--- a/ui/accessibility/BUILD.gn
+++ b/ui/accessibility/BUILD.gn
@@ -132,15 +132,12 @@
 
   defines = [ "ACCESSIBILITY_IMPLEMENTATION" ]
 
-  deps = [
-    "//third_party/cld_3/src/src:cld_3",
-  ]
-
   public_deps = [
     ":ax_constants_mojo",
     ":ax_enums_mojo",
     "//base",
     "//base:i18n",
+    "//third_party/cld_3/src/src:cld_3",
     "//ui/base",
     "//ui/display",
     "//ui/gfx",
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index 06cac04..82eec09 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -3238,8 +3238,53 @@
 }
 
 void AXPlatformNodeAuraLinux::UpdateHypertext() {
+  AXHypertext old_hypertext = hypertext_;
+  base::OffsetAdjuster::Adjustments old_adjustments = GetHypertextAdjustments();
+
   UpdateComputedHypertext();
   text_unicode_adjustments_ = base::nullopt;
+
+  if ((!GetData().HasState(ax::mojom::State::kEditable) ||
+       GetData().GetRestriction() == ax::mojom::Restriction::kReadOnly) &&
+      !IsInLiveRegion()) {
+    return;
+  }
+
+  size_t shared_prefix, old_len, new_len;
+  ComputeHypertextRemovedAndInserted(old_hypertext, &shared_prefix, &old_len,
+                                     &new_len);
+
+  DCHECK(atk_object_);
+  DCHECK(ATK_IS_TEXT(atk_object_));
+
+  if (old_len > 0) {
+    base::string16 removed_substring =
+        old_hypertext.hypertext.substr(shared_prefix, old_len);
+
+    size_t shared_unicode_prefix = shared_prefix;
+    base::OffsetAdjuster::AdjustOffset(old_adjustments, &shared_unicode_prefix);
+    size_t shared_unicode_suffix = shared_prefix + old_len;
+    base::OffsetAdjuster::AdjustOffset(old_adjustments, &shared_unicode_suffix);
+
+    g_signal_emit_by_name(
+        atk_object_, "text-remove",
+        shared_unicode_prefix,                  // position of removal
+        shared_unicode_suffix - shared_prefix,  // length of removal
+        base::UTF16ToUTF8(removed_substring).c_str());
+  }
+
+  if (new_len > 0) {
+    base::string16 inserted_substring =
+        hypertext_.hypertext.substr(shared_prefix, new_len);
+    size_t shared_unicode_prefix = UTF16ToUnicodeOffsetInText(shared_prefix);
+    size_t shared_unicode_suffix =
+        UTF16ToUnicodeOffsetInText(shared_prefix + new_len);
+    g_signal_emit_by_name(
+        atk_object_, "text-insert",
+        shared_unicode_prefix,                          // position of insertion
+        shared_unicode_suffix - shared_unicode_prefix,  // length of insertion
+        base::UTF16ToUTF8(inserted_substring).c_str());
+  }
 }
 
 const AXHypertext& AXPlatformNodeAuraLinux::GetHypertext() {
@@ -3558,4 +3603,9 @@
                            selection_end);
 }
 
+bool AXPlatformNodeAuraLinux::IsInLiveRegion() {
+  return GetData().HasStringAttribute(
+      ax::mojom::StringAttribute::kContainerLiveStatus);
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h
index c447b482..d1dd931 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.h
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.h
@@ -160,6 +160,7 @@
   void AddRelationToSet(AtkRelationSet*,
                         AtkRelationType,
                         AXPlatformNode* target);
+  bool IsInLiveRegion();
 
   // The AtkStateType for a checkable node can vary depending on the role.
   AtkStateType GetAtkStateTypeForCheckableNode();
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index 11bdd64..14a7849 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -1366,4 +1366,122 @@
   }
 }
 
+bool AXPlatformNodeBase::IsSameHypertextCharacter(
+    const AXHypertext& old_hypertext,
+    size_t old_char_index,
+    size_t new_char_index) {
+  if (old_char_index >= old_hypertext.hypertext.size() ||
+      new_char_index >= hypertext_.hypertext.size()) {
+    return false;
+  }
+
+  // For anything other than the "embedded character", we just compare the
+  // characters directly.
+  base::char16 old_ch = old_hypertext.hypertext[old_char_index];
+  base::char16 new_ch = hypertext_.hypertext[new_char_index];
+  if (old_ch != new_ch)
+    return false;
+  if (new_ch != kEmbeddedCharacter)
+    return true;
+
+  // If it's an embedded character, they're only identical if the child id
+  // the hyperlink points to is the same.
+  const std::map<int32_t, int32_t>& old_offset_to_index =
+      old_hypertext.hyperlink_offset_to_index;
+  const std::vector<int32_t>& old_hyperlinks = old_hypertext.hyperlinks;
+  int32_t old_hyperlinkscount = static_cast<int32_t>(old_hyperlinks.size());
+  auto iter = old_offset_to_index.find(static_cast<int32_t>(old_char_index));
+  int old_index = (iter != old_offset_to_index.end()) ? iter->second : -1;
+  int old_child_id = (old_index >= 0 && old_index < old_hyperlinkscount)
+                         ? old_hyperlinks[old_index]
+                         : -1;
+
+  const std::map<int32_t, int32_t>& new_offset_to_index =
+      hypertext_.hyperlink_offset_to_index;
+  const std::vector<int32_t>& new_hyperlinks = hypertext_.hyperlinks;
+  int32_t new_hyperlinkscount = static_cast<int32_t>(new_hyperlinks.size());
+  iter = new_offset_to_index.find(static_cast<int32_t>(new_char_index));
+  int new_index = (iter != new_offset_to_index.end()) ? iter->second : -1;
+  int new_child_id = (new_index >= 0 && new_index < new_hyperlinkscount)
+                         ? new_hyperlinks[new_index]
+                         : -1;
+
+  return old_child_id == new_child_id;
+}
+
+// Return true if the index represents a text character.
+bool AXPlatformNodeBase::IsText(const base::string16& text,
+                                size_t index,
+                                bool is_indexed_from_end) {
+  size_t text_len = text.size();
+  if (index == text_len)
+    return false;
+  auto ch = text[is_indexed_from_end ? text_len - index - 1 : index];
+  return ch != kEmbeddedCharacter;
+}
+
+void AXPlatformNodeBase::ComputeHypertextRemovedAndInserted(
+    const AXHypertext& old_hypertext,
+    size_t* start,
+    size_t* old_len,
+    size_t* new_len) {
+  *start = 0;
+  *old_len = 0;
+  *new_len = 0;
+
+  // Do not compute for static text objects, otherwise redundant text change
+  // announcements will occur in live regions, as the parent hypertext also
+  // changes.
+  if (GetData().role == ax::mojom::Role::kStaticText)
+    return;
+
+  const base::string16& old_text = old_hypertext.hypertext;
+  const base::string16& new_text = hypertext_.hypertext;
+
+  // TODO(accessibility) Plumb through which part of text changed so we don't
+  // have to guess what changed based on character differences. This can be
+  // wrong in some cases as follows:
+  // -- EDITABLE --
+  // If editable: when part of the text node changes, assume only that part
+  // changed, and not the entire thing. For example, if "car" changes to
+  // "cat", assume only 1 letter changed. This code compares common characters
+  // to guess what has changed.
+  // -- NOT EDITABLE --
+  // When part of the text changes, assume the entire node's text changed. For
+  // example, if "car" changes to "cat" then assume all 3 letters changed.
+  // Note, it is possible (though rare) that CharacterData methods are used to
+  // remove, insert, replace or append a substring.
+  bool allow_partial_text_node_changes =
+      GetData().HasState(ax::mojom::State::kEditable);
+  size_t prefix_index = 0;
+  size_t common_prefix = 0;
+  while (prefix_index < old_text.size() && prefix_index < new_text.size() &&
+         IsSameHypertextCharacter(old_hypertext, prefix_index, prefix_index)) {
+    ++prefix_index;
+    if (allow_partial_text_node_changes ||
+        (!IsText(old_text, prefix_index) && !IsText(new_text, prefix_index))) {
+      common_prefix = prefix_index;
+    }
+  }
+
+  size_t suffix_index = 0;
+  size_t common_suffix = 0;
+  while (common_prefix + suffix_index < old_text.size() &&
+         common_prefix + suffix_index < new_text.size() &&
+         IsSameHypertextCharacter(old_hypertext,
+                                  old_text.size() - suffix_index - 1,
+                                  new_text.size() - suffix_index - 1)) {
+    ++suffix_index;
+    if (allow_partial_text_node_changes ||
+        (!IsText(old_text, suffix_index, true) &&
+         !IsText(new_text, suffix_index, true))) {
+      common_suffix = suffix_index;
+    }
+  }
+
+  *start = common_prefix;
+  *old_len = old_text.size() - common_prefix - common_suffix;
+  *new_len = new_text.size() - common_prefix - common_suffix;
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_base.h b/ui/accessibility/platform/ax_platform_node_base.h
index 4e84509..5cd44c74 100644
--- a/ui/accessibility/platform/ax_platform_node_base.h
+++ b/ui/accessibility/platform/ax_platform_node_base.h
@@ -340,12 +340,24 @@
   int GetHypertextOffsetFromEndpoint(AXPlatformNodeBase* endpoint_object,
                                      int endpoint_offset);
 
+  bool IsSameHypertextCharacter(const AXHypertext& old_hypertext,
+                                size_t old_char_index,
+                                size_t new_char_index);
+  void ComputeHypertextRemovedAndInserted(const AXHypertext& old_hypertext,
+                                          size_t* start,
+                                          size_t* old_len,
+                                          size_t* new_len);
   int32_t GetPosInSet() const;
   int32_t GetSetSize() const;
 
   AXHypertext hypertext_;
 
  private:
+  // Return true if the index represents a text character.
+  bool IsText(const base::string16& text,
+              size_t index,
+              bool is_indexed_from_end = false);
+
   DISALLOW_COPY_AND_ASSIGN(AXPlatformNodeBase);
 };
 
diff --git a/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
index b9e358f1..49b93164 100644
--- a/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textprovider_win_unittest.cc
@@ -324,6 +324,7 @@
   EXPECT_EQ(0, wcscmp(text_content, L"some"));
   text_content.Reset();
   selections.Reset();
+  text_range_provider.Release();
 
   // Verify that start and end are appropriately swapped when sel_anchor_offset
   // is greater than sel_focus_offset
@@ -348,6 +349,7 @@
   EXPECT_EQ(0, wcscmp(text_content, L"some"));
   text_content.Reset();
   selections.Reset();
+  text_range_provider.Release();
 
   // Verify that text ranges at an insertion point returns a degenerate (empty)
   // text range via textbox with sel_anchor_offset equal to sel_focus_offset
@@ -384,6 +386,7 @@
   EXPECT_EQ(0U, SysStringLen(text_content));
   text_content.Reset();
   selections.Reset();
+  text_edit_range_provider.Release();
 
   // Now delete the tree (which will delete the associated elements) and verify
   // that UIA_E_ELEMENTNOTAVAILABLE is returned when calling GetSelection on
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 0bbf5c3c..2008808 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/win/scoped_variant.h"
+#include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/platform/ax_platform_node_delegate.h"
 
 #define UIA_VALIDATE_TEXTRANGEPROVIDER_CALL()                        \
@@ -320,7 +321,17 @@
 
 STDMETHODIMP AXPlatformNodeTextRangeProviderWin::Select() {
   WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_TEXTRANGE_SELECT);
-  return E_NOTIMPL;
+  UIA_VALIDATE_TEXTRANGEPROVIDER_CALL();
+
+  AXNodeRange range(start_->Clone(), end_->Clone());
+  AXActionData action_data;
+  action_data.anchor_node_id = range.anchor()->anchor_id();
+  action_data.anchor_offset = range.anchor()->text_offset();
+  action_data.focus_node_id = range.focus()->anchor_id();
+  action_data.focus_offset = range.focus()->text_offset();
+  action_data.action = ax::mojom::Action::kSetSelection;
+  owner()->GetDelegate()->AccessibilityPerformAction(action_data);
+  return S_OK;
 }
 
 STDMETHODIMP AXPlatformNodeTextRangeProviderWin::AddToSelection() {
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
index fe9bfaf..9592c8a3 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win_unittest.cc
@@ -442,6 +442,8 @@
     EXPECT_UIA_ELEMENTNOTAVAILABLE(text_range_provider->MoveEndpointByRange(
         TextPatternRangeEndpoint_Start, text_range_provider.Get(),
         TextPatternRangeEndpoint_Start));
+
+    EXPECT_UIA_ELEMENTNOTAVAILABLE(text_range_provider->Select());
   }
 
   // Test for when this provider is valid, but the other provider is not an
@@ -1287,4 +1289,190 @@
                               expected_mixed_variant);
 }
 
+TEST_F(AXPlatformNodeTextRangeProviderTest, TestITextRangeProviderSelect) {
+  ui::AXNodeData text_data;
+  text_data.id = 2;
+  text_data.role = ax::mojom::Role::kStaticText;
+  text_data.SetName("some text");
+
+  ui::AXNodeData more_text_data;
+  more_text_data.id = 3;
+  more_text_data.role = ax::mojom::Role::kStaticText;
+  more_text_data.SetName("more text2");
+
+  ui::AXNodeData root_data;
+  root_data.id = 1;
+  root_data.role = ax::mojom::Role::kRootWebArea;
+  root_data.child_ids = {2, 3};
+
+  ui::AXTreeUpdate update;
+  ui::AXTreeData tree_data;
+  tree_data.tree_id = ui::AXTreeID::CreateNewAXTreeID();
+  update.tree_data = tree_data;
+  update.has_tree_data = true;
+  update.root_id = root_data.id;
+  update.nodes = {root_data, text_data, more_text_data};
+
+  Init(update);
+
+  AXNode* root_node = GetRootNode();
+  AXNodePosition::SetTreeForTesting(tree_.get());
+  AXNode* text_node = root_node->children()[0];
+  AXNode* more_text_node = root_node->children()[1];
+
+  // Text range for the document, which contains text "some textmore text2".
+  ComPtr<IRawElementProviderSimple> root_node_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(root_node);
+  ComPtr<ITextProvider> document_provider;
+  ComPtr<ITextRangeProvider> document_text_range_provider;
+  ComPtr<AXPlatformNodeTextRangeProviderWin> document_text_range;
+  EXPECT_HRESULT_SUCCEEDED(
+      root_node_raw->GetPatternProvider(UIA_TextPatternId, &document_provider));
+  EXPECT_HRESULT_SUCCEEDED(
+      document_provider->get_DocumentRange(&document_text_range_provider));
+  document_text_range_provider->QueryInterface(
+      IID_PPV_ARGS(&document_text_range));
+
+  // Text range related to "some text".
+  ComPtr<IRawElementProviderSimple> text_node_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(text_node);
+  ComPtr<ITextProvider> text_provider;
+  ComPtr<ITextRangeProvider> text_range_provider;
+  ComPtr<AXPlatformNodeTextRangeProviderWin> text_range;
+  EXPECT_HRESULT_SUCCEEDED(
+      text_node_raw->GetPatternProvider(UIA_TextPatternId, &text_provider));
+  EXPECT_HRESULT_SUCCEEDED(
+      text_provider->get_DocumentRange(&text_range_provider));
+  text_range_provider->QueryInterface(IID_PPV_ARGS(&text_range));
+
+  // Text range related to "more text2".
+  ComPtr<IRawElementProviderSimple> more_text_node_raw =
+      QueryInterfaceFromNode<IRawElementProviderSimple>(more_text_node);
+  ComPtr<ITextProvider> more_text_provider;
+  ComPtr<ITextRangeProvider> more_text_range_provider;
+  ComPtr<AXPlatformNodeTextRangeProviderWin> more_text_range;
+  EXPECT_HRESULT_SUCCEEDED(more_text_node_raw->GetPatternProvider(
+      UIA_TextPatternId, &more_text_provider));
+  EXPECT_HRESULT_SUCCEEDED(
+      more_text_provider->get_DocumentRange(&more_text_range_provider));
+  more_text_range_provider->QueryInterface(IID_PPV_ARGS(&more_text_range));
+
+  AXPlatformNodeDelegate* delegate =
+      GetOwner(document_text_range.Get())->GetDelegate();
+
+  CComPtr<ITextRangeProvider> selected_text_range_provider;
+  base::win::ScopedSafearray selection;
+  base::win::ScopedBstr selected_text_content;
+  long index = 0;
+  long ubound;
+  long lbound;
+
+  // Text range "some text" performs select.
+  {
+    text_range_provider->Select();
+
+    // Verify selection.
+    EXPECT_EQ(2, delegate->GetTreeData().sel_anchor_object_id);
+    EXPECT_EQ(2, delegate->GetTreeData().sel_focus_object_id);
+    EXPECT_EQ(0, delegate->GetTreeData().sel_anchor_offset);
+    EXPECT_EQ(9, delegate->GetTreeData().sel_focus_offset);
+
+    // Verify the content of the selection.
+    document_provider->GetSelection(selection.Receive());
+    ASSERT_NE(nullptr, selection.Get());
+
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selection.Get(), 1, &ubound));
+    EXPECT_EQ(0, ubound);
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selection.Get(), 1, &lbound));
+    EXPECT_EQ(0, lbound);
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetElement(
+        selection.Get(), &index, &selected_text_range_provider));
+
+    EXPECT_HRESULT_SUCCEEDED(selected_text_range_provider->GetText(
+        -1, selected_text_content.Receive()));
+    EXPECT_EQ(0, wcscmp(selected_text_content, L"some text"));
+    selection.Reset();
+    selected_text_content.Reset();
+    selected_text_range_provider.Release();
+  }
+
+  // Text range "more text2" performs select.
+  {
+    more_text_range_provider->Select();
+
+    // Verify selection
+    EXPECT_EQ(3, delegate->GetTreeData().sel_anchor_object_id);
+    EXPECT_EQ(3, delegate->GetTreeData().sel_focus_object_id);
+    EXPECT_EQ(0, delegate->GetTreeData().sel_anchor_offset);
+    EXPECT_EQ(10, delegate->GetTreeData().sel_focus_offset);
+
+    // Verify the content of the selection.
+    document_provider->GetSelection(selection.Receive());
+    ASSERT_NE(nullptr, selection.Get());
+
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selection.Get(), 1, &ubound));
+    EXPECT_EQ(0, ubound);
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selection.Get(), 1, &lbound));
+    EXPECT_EQ(0, lbound);
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetElement(
+        selection.Get(), &index, &selected_text_range_provider));
+
+    EXPECT_HRESULT_SUCCEEDED(selected_text_range_provider->GetText(
+        -1, selected_text_content.Receive()));
+    EXPECT_EQ(0, wcscmp(selected_text_content, L"more text2"));
+    selection.Reset();
+    selected_text_content.Reset();
+    selected_text_range_provider.Release();
+  }
+
+  // Document text range "some textmore text2" performs select.
+  {
+    document_text_range_provider->Select();
+
+    // Verify selection.
+    EXPECT_EQ(2, delegate->GetTreeData().sel_anchor_object_id);
+    EXPECT_EQ(3, delegate->GetTreeData().sel_focus_object_id);
+    EXPECT_EQ(0, delegate->GetTreeData().sel_anchor_offset);
+    EXPECT_EQ(10, delegate->GetTreeData().sel_focus_offset);
+
+    // When selection spans multiple nodes ITextProvider::GetSelection is not
+    // supported. But the text range is still selected.
+    document_provider->GetSelection(selection.Receive());
+    ASSERT_EQ(nullptr, selection.Get());
+  }
+
+  // A degenerate text range performs select.
+  {
+    // Move the endpoint of text range so it becomes degenerate, then select.
+    text_range_provider->MoveEndpointByRange(TextPatternRangeEndpoint_Start,
+                                             text_range_provider.Get(),
+                                             TextPatternRangeEndpoint_End);
+    text_range_provider->Select();
+
+    // Verify selection.
+    EXPECT_EQ(2, delegate->GetTreeData().sel_anchor_object_id);
+    EXPECT_EQ(2, delegate->GetTreeData().sel_focus_object_id);
+    EXPECT_EQ(9, delegate->GetTreeData().sel_anchor_offset);
+    EXPECT_EQ(9, delegate->GetTreeData().sel_focus_offset);
+
+    // Verify the content of the selection.
+    document_provider->GetSelection(selection.Receive());
+    ASSERT_NE(nullptr, selection.Get());
+
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetUBound(selection.Get(), 1, &ubound));
+    EXPECT_EQ(0, ubound);
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetLBound(selection.Get(), 1, &lbound));
+    EXPECT_EQ(0, lbound);
+    EXPECT_HRESULT_SUCCEEDED(SafeArrayGetElement(
+        selection.Get(), &index, &selected_text_range_provider));
+
+    EXPECT_HRESULT_SUCCEEDED(selected_text_range_provider->GetText(
+        -1, selected_text_content.Receive()));
+    EXPECT_EQ(0, wcscmp(selected_text_content, L""));
+    selection.Reset();
+    selected_text_content.Reset();
+    selected_text_range_provider.Release();
+  }
+}
+
 }  // namespace ui
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index fb94111..cb10e1f 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -6471,118 +6471,11 @@
   return false;
 }
 
-bool AXPlatformNodeWin::IsSameHypertextCharacter(size_t old_char_index,
-                                                 size_t new_char_index) {
-  if (old_char_index >= old_hypertext_.hypertext.size() ||
-      new_char_index >= hypertext_.hypertext.size()) {
-    return false;
-  }
-
-  // For anything other than the "embedded character", we just compare the
-  // characters directly.
-  base::char16 old_ch = old_hypertext_.hypertext[old_char_index];
-  base::char16 new_ch = hypertext_.hypertext[new_char_index];
-  if (old_ch != new_ch)
-    return false;
-  if (new_ch != kEmbeddedCharacter)
-    return true;
-
-  // If it's an embedded character, they're only identical if the child id
-  // the hyperlink points to is the same.
-  std::map<int32_t, int32_t>& old_offset_to_index =
-      old_hypertext_.hyperlink_offset_to_index;
-  std::vector<int32_t>& old_hyperlinks = old_hypertext_.hyperlinks;
-  int32_t old_hyperlinkscount = static_cast<int32_t>(old_hyperlinks.size());
-  std::map<int32_t, int32_t>::iterator iter;
-  iter = old_offset_to_index.find((int32_t)old_char_index);
-  int old_index = (iter != old_offset_to_index.end()) ? iter->second : -1;
-  int old_child_id = (old_index >= 0 && old_index < old_hyperlinkscount)
-                         ? old_hyperlinks[old_index]
-                         : -1;
-
-  std::map<int32_t, int32_t>& new_offset_to_index =
-      hypertext_.hyperlink_offset_to_index;
-  std::vector<int32_t>& new_hyperlinks = hypertext_.hyperlinks;
-  int32_t new_hyperlinkscount = static_cast<int32_t>(new_hyperlinks.size());
-  iter = new_offset_to_index.find((int32_t)new_char_index);
-  int new_index = (iter != new_offset_to_index.end()) ? iter->second : -1;
-  int new_child_id = (new_index >= 0 && new_index < new_hyperlinkscount)
-                         ? new_hyperlinks[new_index]
-                         : -1;
-
-  return old_child_id == new_child_id;
-}
-
-// Return true if the index represents a text character.
-bool AXPlatformNodeWin::IsText(const base::string16& text,
-                               size_t index,
-                               bool is_indexed_from_end) {
-  size_t text_len = text.size();
-  if (index == text_len)
-    return false;
-  auto ch = text[is_indexed_from_end ? text_len - index - 1 : index];
-  return ch != kEmbeddedCharacter;
-}
-
 void AXPlatformNodeWin::ComputeHypertextRemovedAndInserted(size_t* start,
                                                            size_t* old_len,
                                                            size_t* new_len) {
-  *start = 0;
-  *old_len = 0;
-  *new_len = 0;
-
-  // Do not compute for static text objects, otherwise redundant text change
-  // announcements will occur in live regions, as the parent hypertext also
-  // changes.
-  if (GetData().role == ax::mojom::Role::kStaticText)
-    return;
-
-  const base::string16& old_text = old_hypertext_.hypertext;
-  const base::string16& new_text = hypertext_.hypertext;
-
-  // TODO(accessibility) Plumb through which part of text changed so we don't
-  // have to guess what changed based on character differences. This can be
-  // wrong in some cases as follows:
-  // -- EDITABLE --
-  // If editable: when part of the text node changes, assume only that part
-  // changed, and not the entire thing. For example, if "car" changes to
-  // "cat", assume only 1 letter changed. This code compares common characters
-  // to guess what has changed.
-  // -- NOT EDITABLE --
-  // When part of the text changes, assume the entire node's text changed. For
-  // example, if "car" changes to "cat" then assume all 3 letters changed.
-  // Note, it is possible (though rare) that CharacterData methods are used to
-  // remove, insert, replace or append a substring.
-  bool allow_partial_text_node_changes =
-      GetData().HasState(ax::mojom::State::kEditable);
-  size_t prefix_index = 0;
-  size_t common_prefix = 0;
-  while (prefix_index < old_text.size() && prefix_index < new_text.size() &&
-         IsSameHypertextCharacter(prefix_index, prefix_index)) {
-    ++prefix_index;
-    if (allow_partial_text_node_changes ||
-        (!IsText(old_text, prefix_index) && !IsText(new_text, prefix_index))) {
-      common_prefix = prefix_index;
-    }
-  }
-
-  size_t suffix_index = 0;
-  size_t common_suffix = 0;
-  while (common_prefix + suffix_index < old_text.size() &&
-         common_prefix + suffix_index < new_text.size() &&
-         IsSameHypertextCharacter(old_text.size() - suffix_index - 1,
-                                  new_text.size() - suffix_index - 1)) {
-    ++suffix_index;
-    if (allow_partial_text_node_changes ||
-        (!IsText(old_text, suffix_index, true) &&
-         !IsText(new_text, suffix_index, true))) {
-      common_suffix = suffix_index;
-    }
-  }
-
-  *start = common_prefix;
-  *old_len = old_text.size() - common_prefix - common_suffix;
-  *new_len = new_text.size() - common_prefix - common_suffix;
+  AXPlatformNodeBase::ComputeHypertextRemovedAndInserted(old_hypertext_, start,
+                                                         old_len, new_len);
 }
 
 double AXPlatformNodeWin::GetHorizontalScrollPercent() {
diff --git a/ui/accessibility/platform/ax_platform_node_win.h b/ui/accessibility/platform/ax_platform_node_win.h
index 190f838..2eb9a14 100644
--- a/ui/accessibility/platform/ax_platform_node_win.h
+++ b/ui/accessibility/platform/ax_platform_node_win.h
@@ -1048,8 +1048,6 @@
   // Also, in IA2, text that includes embedded objects is called hypertext.
   // Returns true if the current object is an IA2 hyperlink.
   bool IsHyperlink();
-
-  bool IsSameHypertextCharacter(size_t old_char_index, size_t new_char_index);
   void ComputeHypertextRemovedAndInserted(size_t* start,
                                           size_t* old_len,
                                           size_t* new_len);
@@ -1165,11 +1163,6 @@
                     LONG start_offset,
                     TextBoundaryDirection direction);
 
-  // Return true if the index represents a text character.
-  bool IsText(const base::string16& text,
-              size_t index,
-              bool is_indexed_from_end = false);
-
   // Many MSAA methods take a var_id parameter indicating that the operation
   // should be performed on a particular child ID, rather than this object.
   // This method tries to figure out the target object from |var_id| and
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index b29eb27..3e10fef 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -278,6 +278,22 @@
   node_->SetData(new_data);
 }
 
+void TestAXNodeWrapper::ReplaceTreeDataTextSelection(int32_t anchor_node_id,
+                                                     int32_t anchor_offset,
+                                                     int32_t focus_node_id,
+                                                     int32_t focus_offset) {
+  if (!tree_)
+    return;
+
+  AXTreeData new_tree_data = GetTreeData();
+  new_tree_data.sel_anchor_object_id = anchor_node_id;
+  new_tree_data.sel_anchor_offset = anchor_offset;
+  new_tree_data.sel_focus_object_id = focus_node_id;
+  new_tree_data.sel_focus_offset = focus_offset;
+
+  tree_->UpdateData(new_tree_data);
+}
+
 bool TestAXNodeWrapper::IsTable() const {
   return node_->IsTable();
 }
@@ -434,14 +450,17 @@
       }
       return true;
 
-    case ax::mojom::Action::kSetSelection:
+    case ax::mojom::Action::kSetSelection: {
       ReplaceIntAttribute(data.anchor_node_id,
                           ax::mojom::IntAttribute::kTextSelStart,
                           data.anchor_offset);
-      ReplaceIntAttribute(data.anchor_node_id,
+      ReplaceIntAttribute(data.focus_node_id,
                           ax::mojom::IntAttribute::kTextSelEnd,
                           data.focus_offset);
+      ReplaceTreeDataTextSelection(data.anchor_node_id, data.anchor_offset,
+                                   data.focus_node_id, data.focus_offset);
       return true;
+    }
 
     case ax::mojom::Action::kFocus:
       g_focused_node_in_tree[tree_] = node_;
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.h b/ui/accessibility/platform/test_ax_node_wrapper.h
index 1ce4add8..7c2ccfc 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.h
+++ b/ui/accessibility/platform/test_ax_node_wrapper.h
@@ -119,6 +119,10 @@
   void ReplaceBoolAttribute(ax::mojom::BoolAttribute attribute, bool value);
   void ReplaceStringAttribute(ax::mojom::StringAttribute attribute,
                               std::string value);
+  void ReplaceTreeDataTextSelection(int32_t anchor_node_id,
+                                    int32_t anchor_offset,
+                                    int32_t focus_node_id,
+                                    int32_t focus_offset);
 
   TestAXNodeWrapper* HitTestSyncInternal(int x, int y);
 
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index fdd132b..b9a4f57 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -1822,6 +1822,12 @@
                             drag_drop_controller_.get());
 }
 
+void WindowTreeClient::ConnectToImeEngine(
+    ime::mojom::ImeEngineRequest engine_request,
+    ime::mojom::ImeEngineClientPtr client) {
+  tree_->ConnectToImeEngine(std::move(engine_request), std::move(client));
+}
+
 void WindowTreeClient::OnTransientChildWindowAdded(Window* parent,
                                                    Window* transient_child) {
   // TransientWindowClient is a singleton and we allow multiple
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index c9b3d24..04a0a3d 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -515,6 +515,8 @@
   std::unique_ptr<WindowPortMus> CreateWindowPortForTopLevel(
       const std::map<std::string, std::vector<uint8_t>>* properties) override;
   void OnWindowTreeHostCreated(WindowTreeHostMus* window_tree_host) override;
+  void ConnectToImeEngine(ime::mojom::ImeEngineRequest engine_request,
+                          ime::mojom::ImeEngineClientPtr client) override;
 
   // client::TransientWindowClientObserver:
   void OnTransientChildWindowAdded(Window* parent,
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc
index a06c87c..1c694ec 100644
--- a/ui/aura/mus/window_tree_host_mus.cc
+++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -350,6 +350,13 @@
   WindowPortMus::Get(window())->SetImeVisibility(visible, std::move(state));
 }
 
+bool WindowTreeHostMus::ConnectToImeEngine(
+    ime::mojom::ImeEngineRequest engine_request,
+    ime::mojom::ImeEngineClientPtr client) {
+  delegate_->ConnectToImeEngine(std::move(engine_request), std::move(client));
+  return true;
+}
+
 void WindowTreeHostMus::SetBoundsInPixels(
     const gfx::Rect& bounds,
     const viz::LocalSurfaceIdAllocation& local_surface_id_allocation) {
diff --git a/ui/aura/mus/window_tree_host_mus.h b/ui/aura/mus/window_tree_host_mus.h
index 45cdbf0..a8afe3b 100644
--- a/ui/aura/mus/window_tree_host_mus.h
+++ b/ui/aura/mus/window_tree_host_mus.h
@@ -16,6 +16,7 @@
 #include "ui/aura/aura_export.h"
 #include "ui/aura/mus/input_method_mus_delegate.h"
 #include "ui/aura/window_tree_host_platform.h"
+#include "ui/base/ime/mojo/ime.mojom.h"
 #include "ui/base/mojo/ui_base_types.mojom.h"
 
 namespace display {
@@ -118,6 +119,8 @@
   void SetTextInputState(ui::mojom::TextInputStatePtr state) override;
   void SetImeVisibility(bool visible,
                         ui::mojom::TextInputStatePtr state) override;
+  bool ConnectToImeEngine(ime::mojom::ImeEngineRequest engine_request,
+                          ime::mojom::ImeEngineClientPtr client) override;
 
  protected:
   // This is in the protected section as SetBounds() is preferred.
diff --git a/ui/aura/mus/window_tree_host_mus_delegate.h b/ui/aura/mus/window_tree_host_mus_delegate.h
index 76f759a5..d45ed48 100644
--- a/ui/aura/mus/window_tree_host_mus_delegate.h
+++ b/ui/aura/mus/window_tree_host_mus_delegate.h
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "ui/aura/aura_export.h"
+#include "ui/base/ime/mojo/ime.mojom.h"
 
 namespace gfx {
 class Rect;
@@ -75,6 +76,11 @@
   // created.
   virtual void OnWindowTreeHostCreated(WindowTreeHostMus* window_tree_host) = 0;
 
+  // Called when a client requests to connect to the active
+  // ime::mojom::ImeEngine.
+  virtual void ConnectToImeEngine(ime::mojom::ImeEngineRequest engine_request,
+                                  ime::mojom::ImeEngineClientPtr client) = 0;
+
  protected:
   virtual ~WindowTreeHostMusDelegate() {}
 };
diff --git a/ui/base/ime/BUILD.gn b/ui/base/ime/BUILD.gn
index 5323a67..8826b212 100644
--- a/ui/base/ime/BUILD.gn
+++ b/ui/base/ime/BUILD.gn
@@ -13,15 +13,47 @@
   ]
 }
 
-jumbo_component("ime") {
-  output_name = "ui_base_ime"
+jumbo_component("ime_types") {
+  output_name = "ui_base_ime_types"
   sources = [
     "candidate_window.cc",
     "candidate_window.h",
-    "character_composer.cc",
-    "character_composer.h",
     "composition_text.cc",
     "composition_text.h",
+    "ime_text_span.cc",
+    "ime_text_span.h",
+    "infolist_entry.cc",
+    "infolist_entry.h",
+  ]
+
+  defines = [ "IS_UI_BASE_IME_TYPES_IMPL" ]
+
+  deps = [
+    "//base",
+    "//ui/gfx/range",
+  ]
+
+  public_deps = [
+    ":text_input_types",
+    "//skia",
+  ]
+
+  if (is_chromeos || use_ozone) {
+    sources += [
+      "character_composer.cc",
+      "character_composer.h",
+    ]
+    deps += [
+      "//ui/events:dom_keycode_converter",
+      "//ui/events:events",
+      "//ui/events:events_base",
+    ]
+  }
+}
+
+jumbo_component("ime") {
+  output_name = "ui_base_ime"
+  sources = [
     "constants.cc",
     "constants.h",
     "ime_bridge.cc",
@@ -29,10 +61,6 @@
     "ime_candidate_window_handler_interface.h",
     "ime_engine_handler_interface.h",
     "ime_input_context_handler_interface.h",
-    "ime_text_span.cc",
-    "ime_text_span.h",
-    "infolist_entry.cc",
-    "infolist_entry.h",
     "input_method.h",
     "input_method_base.cc",
     "input_method_base.h",
@@ -57,11 +85,13 @@
   defines = [ "IS_UI_BASE_IME_IMPL" ]
 
   public_deps = [
+    ":ime_types",
     ":text_input_types",
     "//base",
     "//base:i18n",
     "//skia",
     "//third_party/icu",
+    "//ui/base/ime/mojo",
     "//ui/events",
     "//ui/events:dom_keycode_converter",
     "//ui/events:events",
diff --git a/ui/base/ime/candidate_window.h b/ui/base/ime/candidate_window.h
index 57e40d04..82c3fac 100644
--- a/ui/base/ime/candidate_window.h
+++ b/ui/base/ime/candidate_window.h
@@ -18,14 +18,14 @@
 namespace ui {
 
 // CandidateWindow represents the structure of candidates generated from IME.
-class COMPONENT_EXPORT(UI_BASE_IME) CandidateWindow {
+class COMPONENT_EXPORT(UI_BASE_IME_TYPES) CandidateWindow {
  public:
   enum Orientation {
     HORIZONTAL = 0,
     VERTICAL = 1,
   };
 
-  struct COMPONENT_EXPORT(UI_BASE_IME) CandidateWindowProperty {
+  struct COMPONENT_EXPORT(UI_BASE_IME_TYPES) CandidateWindowProperty {
     CandidateWindowProperty();
     virtual ~CandidateWindowProperty();
     int page_size;
@@ -41,7 +41,7 @@
   };
 
   // Represents a candidate entry.
-  struct COMPONENT_EXPORT(UI_BASE_IME) Entry {
+  struct COMPONENT_EXPORT(UI_BASE_IME_TYPES) Entry {
     Entry();
     Entry(const Entry& other);
     virtual ~Entry();
diff --git a/ui/base/ime/character_composer.h b/ui/base/ime/character_composer.h
index 42baf12..57642c4 100644
--- a/ui/base/ime/character_composer.h
+++ b/ui/base/ime/character_composer.h
@@ -20,7 +20,7 @@
 
 // A class to recognize compose and dead key sequence.
 // Outputs composed character.
-class COMPONENT_EXPORT(UI_BASE_IME) CharacterComposer {
+class COMPONENT_EXPORT(UI_BASE_IME_TYPES) CharacterComposer {
  public:
   using ComposeBuffer = std::vector<DomKey>;
 
diff --git a/ui/base/ime/chromeos/BUILD.gn b/ui/base/ime/chromeos/BUILD.gn
index aee4bc0..0c31364 100644
--- a/ui/base/ime/chromeos/BUILD.gn
+++ b/ui/base/ime/chromeos/BUILD.gn
@@ -57,6 +57,7 @@
     "//services/ws/public/cpp/input_devices",
     "//third_party/icu",
     "//ui/base",
+    "//ui/base/ime/mojo",
     "//ui/chromeos/strings",
   ]
 }
diff --git a/ui/base/ime/chromeos/input_method_chromeos.cc b/ui/base/ime/chromeos/input_method_chromeos.cc
index bc94b4eb..587b0890 100644
--- a/ui/base/ime/chromeos/input_method_chromeos.cc
+++ b/ui/base/ime/chromeos/input_method_chromeos.cc
@@ -19,12 +19,15 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/third_party/icu/icu_utf.h"
 #include "chromeos/system/devicemode.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
 #include "ui/base/ime/chromeos/ime_keyboard.h"
 #include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/composition_text.h"
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ime/ime_engine_handler_interface.h"
 #include "ui/base/ime/input_method_delegate.h"
+#include "ui/base/ime/mojo/ime.mojom.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/events/event.h"
 #include "ui/gfx/geometry/rect.h"
@@ -57,6 +60,68 @@
 
 }  // namespace
 
+// The helper to make the InputMethodChromeOS as a ime::mojom::ImeEngineClient.
+// It forwards the ime::mojom::ImeEngineClient method calls toi methods of
+// ui::IMEInputContextHandlerInterface methods.
+// Due to the method naming conflict, InputMethodChromeOS cannot directly
+// inherit from ime::mojom::ImeEngineClient.
+class InputMethodChromeOS::MojoHelper : public ime::mojom::ImeEngineClient {
+ public:
+  explicit MojoHelper(InputMethodChromeOS* im) : im_(im), binding_(this) {}
+  ~MojoHelper() override = default;
+
+  ime::mojom::ImeEngineProxy* ime_engine() { return ime_engine_.get(); }
+
+  bool connected() const { return connected_; }
+
+  void Connect() {
+    ime::mojom::ImeEngineClientPtr client_ptr;
+    binding_.Bind(mojo::MakeRequest(&client_ptr));
+    connected_ = im_->delegate()->ConnectToImeEngine(
+        mojo::MakeRequest(&ime_engine_), std::move(client_ptr));
+  }
+
+  void Reset() {
+    binding_.Close();
+    ime_engine_.reset();
+    connected_ = false;
+  }
+
+ private:
+  // ime::mojom::ImeEngineClient:
+  void CommitText(const std::string& text) override { im_->CommitText(text); }
+  void UpdateCompositionText(const ui::CompositionText& composition_text,
+                             uint32_t cursor_pos,
+                             bool visible) override {
+    im_->UpdateCompositionText(composition_text, cursor_pos, visible);
+  }
+  void DeleteSurroundingText(int32_t offset, uint32_t length) override {
+    im_->DeleteSurroundingText(offset, length);
+  }
+  void SendKeyEvent(std::unique_ptr<ui::Event> key_event) override {
+    im_->SendKeyEvent(key_event->AsKeyEvent());
+  }
+  void Reconnect() override {
+    // Don't reconnect when the |ime_engine_| has been reset, which means the
+    // InputMethodChromeOS is not focused.
+    if (ime_engine_) {
+      Reset();
+      Connect();
+    }
+  }
+
+  InputMethodChromeOS* im_;
+  // Whether the mojo connection is enabled.
+  // If true, |InputMethodChromeOS| works with ime::mojom::ImeEngine.
+  // If false, |InputMethodChromeOS| works with ui::IMEEngineHandlerInterface.
+  bool connected_ = false;
+
+  mojo::Binding<ime::mojom::ImeEngineClient> binding_;
+  ime::mojom::ImeEnginePtr ime_engine_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoHelper);
+};
+
 // InputMethodChromeOS implementation -----------------------------------------
 InputMethodChromeOS::InputMethodChromeOS(
     internal::InputMethodDelegate* delegate)
@@ -64,6 +129,7 @@
       composing_text_(false),
       composition_changed_(false),
       handling_key_event_(false),
+      mojo_helper_(std::make_unique<MojoHelper>(this)),
       weak_ptr_factory_(this) {
   ui::IMEBridge::Get()->SetInputContextHandler(this);
 
@@ -150,7 +216,8 @@
   // normal input field (not a password field).
   // Note: We need to send the key event to ibus even if the |context_| is not
   // enabled, so that ibus can have a chance to enable the |context_|.
-  if (!IsNonPasswordInputFieldFocused() || !GetEngine()) {
+  const bool has_engine = GetEngine() || mojo_helper_->connected();
+  if (!IsNonPasswordInputFieldFocused() || !has_engine) {
     if (event->type() == ET_KEY_PRESSED) {
       if (ExecuteCharacterComposer(*event)) {
         // Treating as PostIME event if character composer handles key event and
@@ -165,13 +232,17 @@
   }
 
   handling_key_event_ = true;
+  ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = base::BindOnce(
+      &InputMethodChromeOS::KeyEventDoneCallback,
+      weak_ptr_factory_.GetWeakPtr(),
+      // Pass the ownership of the new copied event.
+      base::Owned(new ui::KeyEvent(*event)), std::move(result_callback));
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->ProcessKeyEvent(ui::Event::Clone(*event),
+                                                std::move(callback));
+    return ui::EventDispatchDetails();
+  }
   if (GetEngine()->IsInterestedInKeyEvent()) {
-    ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback =
-        base::BindOnce(&InputMethodChromeOS::KeyEventDoneCallback,
-                       weak_ptr_factory_.GetWeakPtr(),
-                       // Pass the ownership of the new copied event.
-                       base::Owned(new ui::KeyEvent(*event)),
-                       std::move(result_callback));
     GetEngine()->ProcessKeyEvent(*event, std::move(callback));
     return ui::EventDispatchDetails();
   }
@@ -227,16 +298,23 @@
 
   UpdateContextFocusState();
 
-  ui::IMEEngineHandlerInterface* engine = GetEngine();
-  if (engine) {
-    // When focused input client is not changed, a text input type change should
-    // cause blur/focus events to engine.
-    // The focus in to or out from password field should also notify engine.
-    engine->FocusOut();
-    ui::IMEEngineHandlerInterface::InputContext context(
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->FinishInput();
+    mojo_helper_->ime_engine()->StartInput(ime::mojom::EditorInfo::New(
         GetTextInputType(), GetTextInputMode(), GetTextInputFlags(),
-        GetClientFocusReason(), GetClientShouldDoLearning());
-    engine->FocusIn(context);
+        GetClientFocusReason(), GetClientShouldDoLearning()));
+  } else {
+    ui::IMEEngineHandlerInterface* engine = GetEngine();
+    if (engine) {
+      ui::IMEEngineHandlerInterface::InputContext context(
+          GetTextInputType(), GetTextInputMode(), GetTextInputFlags(),
+          GetClientFocusReason(), GetClientShouldDoLearning());
+      // When focused input client is not changed, a text input type change
+      // should cause blur/focus events to engine. The focus in to or out from
+      // password field should also notify engine.
+      engine->FocusOut();
+      engine->FocusIn(context);
+    }
   }
 
   OnCaretBoundsChanged(client);
@@ -257,8 +335,12 @@
   DCHECK(client == GetTextInputClient());
   DCHECK(!IsTextInputTypeNone());
 
-  if (GetEngine())
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->UpdateCompositionBounds(
+        GetCompositionBounds(client));
+  } else if (GetEngine()) {
     GetEngine()->SetCompositionBounds(GetCompositionBounds(client));
+  }
 
   chromeos::IMECandidateWindowHandlerInterface* candidate_window =
       ui::IMEBridge::Get()->GetCandidateWindowHandler();
@@ -305,12 +387,17 @@
   // Here SetSurroundingText accepts relative position of |surrounding_text|, so
   // we have to convert |selection_range| from node coordinates to
   // |surrounding_text| coordinates.
-  if (!GetEngine())
-    return;
-  GetEngine()->SetSurroundingText(base::UTF16ToUTF8(surrounding_text),
-                                  selection_range.start() - text_range.start(),
-                                  selection_range.end() - text_range.start(),
-                                  text_range.start());
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->UpdateSurroundingInfo(
+        base::UTF16ToUTF8(surrounding_text),
+        selection_range.start() - text_range.start(),
+        selection_range.end() - text_range.start(), text_range.start());
+  } else if (GetEngine()) {
+    GetEngine()->SetSurroundingText(
+        base::UTF16ToUTF8(surrounding_text),
+        selection_range.start() - text_range.start(),
+        selection_range.end() - text_range.start(), text_range.start());
+  }
 }
 
 void InputMethodChromeOS::CancelComposition(const TextInputClient* client) {
@@ -334,13 +421,26 @@
   return InputMethodBase::GetInputMethodKeyboardController();
 }
 
+void InputMethodChromeOS::OnFocus() {
+  InputMethodBase::OnFocus();
+  mojo_helper_->Connect();
+}
+
+void InputMethodChromeOS::OnBlur() {
+  InputMethodBase::OnBlur();
+  mojo_helper_->Reset();
+}
+
 void InputMethodChromeOS::OnWillChangeFocusedClient(
     TextInputClient* focused_before,
     TextInputClient* focused) {
   ConfirmCompositionText();
 
-  if (GetEngine())
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->FinishInput();
+  } else if (GetEngine()) {
     GetEngine()->FocusOut();
+  }
 }
 
 void InputMethodChromeOS::OnDidChangeFocusedClient(
@@ -351,7 +451,11 @@
   // focus and after it acquires focus again are the same.
   UpdateContextFocusState();
 
-  if (GetEngine()) {
+  if (mojo_helper_->connected()) {
+    mojo_helper_->ime_engine()->StartInput(ime::mojom::EditorInfo::New(
+        GetTextInputType(), GetTextInputMode(), GetTextInputFlags(),
+        GetClientFocusReason(), GetClientShouldDoLearning()));
+  } else if (GetEngine()) {
     ui::IMEEngineHandlerInterface::InputContext context(
         GetTextInputType(), GetTextInputMode(), GetTextInputFlags(),
         GetClientFocusReason(), GetClientShouldDoLearning());
@@ -382,7 +486,9 @@
   // Note: some input method engines may not support reset method, such as
   // ibus-anthy. But as we control all input method engines by ourselves, we can
   // make sure that all of the engines we are using support it correctly.
-  if (GetEngine())
+  if (mojo_helper_->connected())
+    mojo_helper_->ime_engine()->CancelInput();
+  else if (GetEngine())
     GetEngine()->Reset();
 
   character_composer_.Reset();
diff --git a/ui/base/ime/chromeos/input_method_chromeos.h b/ui/base/ime/chromeos/input_method_chromeos.h
index e7d968ca..41d809e 100644
--- a/ui/base/ime/chromeos/input_method_chromeos.h
+++ b/ui/base/ime/chromeos/input_method_chromeos.h
@@ -24,6 +24,8 @@
 
 namespace ui {
 
+class TestableInputMethodChromeOS;
+
 // A ui::InputMethod implementation for ChromeOS.
 class COMPONENT_EXPORT(UI_BASE_IME_CHROMEOS) InputMethodChromeOS
     : public InputMethodBase,
@@ -48,6 +50,8 @@
   InputMethodKeyboardController* GetInputMethodKeyboardController() override;
 
   // Overridden from InputMethodBase:
+  void OnFocus() override;
+  void OnBlur() override;
   void OnWillChangeFocusedClient(TextInputClient* focused_before,
                                  TextInputClient* focused) override;
   void OnDidChangeFocusedClient(TextInputClient* focused_before,
@@ -71,7 +75,9 @@
   void ResetContext();
 
  private:
+  class MojoHelper;
   class PendingKeyEvent;
+  friend TestableInputMethodChromeOS;
 
   ui::EventDispatchDetails DispatchKeyEventInternal(ui::KeyEvent* event,
                                                     AckCallback ack_callback);
@@ -180,6 +186,8 @@
   // This is used in CommitText/UpdateCompositionText/etc.
   bool handling_key_event_;
 
+  std::unique_ptr<MojoHelper> mojo_helper_;
+
   // Used for making callbacks.
   base::WeakPtrFactory<InputMethodChromeOS> weak_ptr_factory_;
 
diff --git a/ui/base/ime/chromeos/input_method_chromeos_unittest.cc b/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
index 07ad8b0..f456a48 100644
--- a/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
+++ b/ui/base/ime/chromeos/input_method_chromeos_unittest.cc
@@ -15,6 +15,8 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
@@ -25,6 +27,7 @@
 #include "ui/base/ime/ime_bridge.h"
 #include "ui/base/ime/ime_engine_handler_interface.h"
 #include "ui/base/ime/input_method_delegate.h"
+#include "ui/base/ime/mojo/ime.mojom.h"
 #include "ui/base/ime/text_input_client.h"
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
@@ -88,6 +91,10 @@
     }
     return details;
   }
+  void CommitText(const std::string& text) override {
+    InputMethodChromeOS::CommitText(text);
+    text_committed_ = text;
+  }
 
   void ResetCallCount() {
     process_key_event_post_ime_call_count_ = 0;
@@ -101,6 +108,8 @@
     return process_key_event_post_ime_call_count_;
   }
 
+  const std::string& text_committed() const { return text_committed_; }
+
   // Change access rights for testing.
   using InputMethodChromeOS::ExtractCompositionText;
   using InputMethodChromeOS::ResetContext;
@@ -108,6 +117,7 @@
  private:
   ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
   int process_key_event_post_ime_call_count_;
+  std::string text_committed_;
 };
 
 class SetSurroundingTextVerifier {
@@ -224,6 +234,53 @@
   DISALLOW_COPY_AND_ASSIGN(CachingInputMethodDelegate);
 };
 
+class MojoInputMethodDelegate : public ui::internal::InputMethodDelegate,
+                                public ime::mojom::ImeEngine {
+ public:
+  MojoInputMethodDelegate() : engine_binding_(this) {}
+  ~MojoInputMethodDelegate() override = default;
+
+  ime::mojom::ImeEngineClientProxy* engine_client() const {
+    return engine_client_.get();
+  }
+
+  void FlushForTesting() { engine_client_.FlushForTesting(); }
+
+ private:
+  // Overridden from ui::internal::InputMethodDelegate:
+  ui::EventDispatchDetails DispatchKeyEventPostIME(
+      ui::KeyEvent* event,
+      DispatchKeyEventPostIMECallback callback) override {
+    event->StopPropagation();
+    RunDispatchKeyEventPostIMECallback(event, std::move(callback));
+    return ui::EventDispatchDetails();
+  }
+  bool ConnectToImeEngine(ime::mojom::ImeEngineRequest engine_request,
+                          ime::mojom::ImeEngineClientPtr client) override {
+    engine_binding_.Bind(std::move(engine_request));
+    engine_client_ = std::move(client);
+    return true;
+  }
+
+  // ime::mojom::ImeEngine:
+  void StartInput(ime::mojom::EditorInfoPtr info) override {}
+  void FinishInput() override {}
+  void CancelInput() override {}
+  void ProcessKeyEvent(std::unique_ptr<ui::Event> key_event,
+                       ProcessKeyEventCallback callback) override {}
+  void UpdateSurroundingInfo(const std::string& text,
+                             int32_t cursor,
+                             int32_t anchor,
+                             int32_t offset) override {}
+  void UpdateCompositionBounds(const std::vector<gfx::Rect>& bounds) override {}
+
+  mojo::Binding<ime::mojom::ImeEngine> engine_binding_;
+
+  ime::mojom::ImeEngineClientPtr engine_client_;
+
+  DISALLOW_COPY_AND_ASSIGN(MojoInputMethodDelegate);
+};
+
 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
                                 public testing::Test,
                                 public DummyTextInputClient {
@@ -388,6 +445,8 @@
 
   TestInputMethodManager* input_method_manager_;
 
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
   DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
 };
 
@@ -911,6 +970,15 @@
   IMEBridge::Get()->SetCurrentEngineHandler(nullptr);
 }
 
+TEST_F(InputMethodChromeOSTest, MojoInteractions) {
+  MojoInputMethodDelegate delegate;
+  TestableInputMethodChromeOS im(&delegate);
+  im.OnFocus();
+  delegate.engine_client()->CommitText("test");
+  delegate.FlushForTesting();
+  EXPECT_EQ("test", im.text_committed());
+}
+
 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
  public:
   InputMethodChromeOSKeyEventTest() {}
diff --git a/ui/base/ime/composition_text.h b/ui/base/ime/composition_text.h
index ba7b76f..cddf445e 100644
--- a/ui/base/ime/composition_text.h
+++ b/ui/base/ime/composition_text.h
@@ -15,7 +15,7 @@
 namespace ui {
 
 // A struct represents the status of an ongoing composition text.
-struct COMPONENT_EXPORT(UI_BASE_IME) CompositionText {
+struct COMPONENT_EXPORT(UI_BASE_IME_TYPES) CompositionText {
   CompositionText();
   CompositionText(const CompositionText& other);
   ~CompositionText();
diff --git a/ui/base/ime/ime_text_span.h b/ui/base/ime/ime_text_span.h
index 4125a3b..4e449567 100644
--- a/ui/base/ime/ime_text_span.h
+++ b/ui/base/ime/ime_text_span.h
@@ -18,7 +18,7 @@
 // Intentionally keep sync with blink::WebImeTextSpan defined in:
 // third_party/WebKit/public/web/WebImeTextSpan.h
 
-struct COMPONENT_EXPORT(UI_BASE_IME) ImeTextSpan {
+struct COMPONENT_EXPORT(UI_BASE_IME_TYPES) ImeTextSpan {
   enum class Type {
     // Creates a composition marker.
     kComposition,
diff --git a/ui/base/ime/infolist_entry.h b/ui/base/ime/infolist_entry.h
index 107db39..54d6320 100644
--- a/ui/base/ime/infolist_entry.h
+++ b/ui/base/ime/infolist_entry.h
@@ -11,7 +11,7 @@
 namespace ui {
 
 // The data model of infolist window.
-struct COMPONENT_EXPORT(UI_BASE_IME) InfolistEntry {
+struct COMPONENT_EXPORT(UI_BASE_IME_TYPES) InfolistEntry {
   base::string16 title;
   base::string16 body;
   bool highlighted;
diff --git a/ui/base/ime/input_method_delegate.cc b/ui/base/ime/input_method_delegate.cc
index dc950847..92c25586 100644
--- a/ui/base/ime/input_method_delegate.cc
+++ b/ui/base/ime/input_method_delegate.cc
@@ -5,11 +5,18 @@
 #include "ui/base/ime/input_method_delegate.h"
 
 #include "base/callback.h"
+#include "ui/base/ime/mojo/ime.mojom.h"
 #include "ui/events/event.h"
 
 namespace ui {
 namespace internal {
 
+bool InputMethodDelegate::ConnectToImeEngine(
+    ::ime::mojom::ImeEngineRequest engine_request,
+    ::ime::mojom::ImeEngineClientPtr client) {
+  return false;
+}
+
 // static
 void InputMethodDelegate::RunDispatchKeyEventPostIMECallback(
     KeyEvent* key_event,
diff --git a/ui/base/ime/input_method_delegate.h b/ui/base/ime/input_method_delegate.h
index a0bcccd2..324982e 100644
--- a/ui/base/ime/input_method_delegate.h
+++ b/ui/base/ime/input_method_delegate.h
@@ -7,6 +7,17 @@
 
 #include "base/callback_forward.h"
 #include "base/component_export.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+
+namespace ime {
+namespace mojom {
+
+class ImeEngine;
+class ImeEngineClient;
+
+}  // namespace mojom
+}  // namespace ime
 
 namespace ui {
 
@@ -34,6 +45,10 @@
       KeyEvent* key_event,
       DispatchKeyEventPostIMECallback callback) = 0;
 
+  virtual bool ConnectToImeEngine(
+      mojo::InterfaceRequest<::ime::mojom::ImeEngine> engine_request,
+      mojo::InterfacePtr<::ime::mojom::ImeEngineClient> client);
+
  protected:
   static void RunDispatchKeyEventPostIMECallback(
       KeyEvent* key_event,
diff --git a/ui/base/ime/mojo/ime.mojom b/ui/base/ime/mojo/ime.mojom
index 993922c4..d28cbf8d 100644
--- a/ui/base/ime/mojo/ime.mojom
+++ b/ui/base/ime/mojo/ime.mojom
@@ -60,6 +60,25 @@
 //    (e.g. by IME's on-screen keyboard)
 //  - later after the IME responds the |ProcessKeyEvent| with the result;
 interface ImeEngineClient {
+  // Called when the IME wants to insert the |text| to the input field.
+  CommitText(string text);
+
+  // Called when the IME wants to generate/update the composition text to the
+  // input field.
+  UpdateCompositionText(ui.mojom.CompositionText composition_text,
+                        uint32 cursor_pos,
+                        bool visible);
+
+  // Called when the IME wants to remove a piece of text in the input field.
+  DeleteSurroundingText(int32 offset, uint32 length);
+
+  // Called when the IME wants to silumate a physical key event to the app.
+  // Usually this is for on-screen keyboard support (e.g. simulate Enter key).
+  SendKeyEvent(ui.mojom.Event key_event);
+
+  // Called when the ImeEngine is deactivated and this client should reconnect
+  // for the new active ImeEngine.
+  Reconnect();
 };
 
 // Implemented by the IME.
diff --git a/ui/base/ime/mojo/ime_types.typemap b/ui/base/ime/mojo/ime_types.typemap
index 4e800fc6..acdc8fff 100644
--- a/ui/base/ime/mojo/ime_types.typemap
+++ b/ui/base/ime/mojo/ime_types.typemap
@@ -15,7 +15,7 @@
   "//ui/base/ime/mojo/ime_types_struct_traits.cc",
 ]
 public_deps = [
-  "//ui/base/ime",
+  "//ui/base/ime:ime_types",
 ]
 deps = [
   "//ui/gfx/range",
diff --git a/ui/keyboard/BUILD.gn b/ui/keyboard/BUILD.gn
index 107da45..1cbb657 100644
--- a/ui/keyboard/BUILD.gn
+++ b/ui/keyboard/BUILD.gn
@@ -171,6 +171,7 @@
     "//base",
     "//base/test:test_support",
     "//components/ukm:test_support",
+    "//mojo/core/embedder",
     "//services/service_manager/public/cpp",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/ui/keyboard/test/run_all_unittests.cc b/ui/keyboard/test/run_all_unittests.cc
index 9d0da8c..2b3a1706 100644
--- a/ui/keyboard/test/run_all_unittests.cc
+++ b/ui/keyboard/test/run_all_unittests.cc
@@ -8,6 +8,7 @@
 #include "base/path_service.h"
 #include "base/test/launcher/unit_test_launcher.h"
 #include "base/test/test_suite.h"
+#include "mojo/core/embedder/embedder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/aura/env.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -49,6 +50,7 @@
 int main(int argc, char** argv) {
   KeyboardTestSuite test_suite(argc, argv);
 
+  mojo::core::Init();
   return base::LaunchUnitTests(
       argc, argv,
       base::BindOnce(&KeyboardTestSuite::Run, base::Unretained(&test_suite)));
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index 7385a97..761a10c 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -286,7 +286,7 @@
     const int hit;
   } kTestCases[] = {
 #if defined(OS_WIN)
-    {0, is_aero_glass_enabled ? HTTRANSPARENT : HTCAPTION},
+    {0, is_aero_glass_enabled ? HTTRANSPARENT : HTNOWHERE},
 #else
     {0, HTTRANSPARENT},
 #endif
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index a6a60190..3828c024 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -181,6 +181,19 @@
   if (close_->visible() && close_->GetMirroredBounds().Contains(point))
     return HTCLOSE;
 
+  // Allow dialogs to show the system menu and be dragged.
+  if (GetWidget()->widget_delegate()->AsDialogDelegate() &&
+      !GetWidget()->widget_delegate()->AsBubbleDialogDelegate()) {
+    gfx::Rect bounds(GetContentsBounds());
+    bounds.Inset(title_margins_);
+    gfx::Rect sys_rect(0, 0, bounds.x(), bounds.y());
+    sys_rect.set_origin(gfx::Point(GetMirroredXForRect(sys_rect), 0));
+    if (sys_rect.Contains(point))
+      return HTSYSMENU;
+    if (point.y() < title()->bounds().bottom())
+      return HTCAPTION;
+  }
+
   // Convert to RRectF to accurately represent the rounded corners of the
   // dialog and allow events to pass through the shadows.
   gfx::RRectF round_contents_bounds(gfx::RectF(GetContentsBounds()),
@@ -191,16 +204,6 @@
   if (!round_contents_bounds.Contains(rectf_point))
     return HTTRANSPARENT;
 
-  if (HasTitle() && point.y() < title()->bounds().bottom()) {
-    auto* dialog_delegate = GetWidget()->widget_delegate()->AsDialogDelegate();
-    // Allow the dialog to be dragged if it is not modal. This can happen if the
-    // dialog has no parent browser window.
-    if (dialog_delegate &&
-        dialog_delegate->GetModalType() == ui::MODAL_TYPE_NONE) {
-      return HTCAPTION;
-    }
-  }
-
   return GetWidget()->client_view()->NonClientHitTest(point);
 }
 
diff --git a/ui/views/controls/link.cc b/ui/views/controls/link.cc
index befcf5d..25e54af5 100644
--- a/ui/views/controls/link.cc
+++ b/ui/views/controls/link.cc
@@ -166,11 +166,6 @@
   node_data->role = ax::mojom::Role::kLink;
 }
 
-void Link::OnEnabledChanged() {
-  RecalculateFont();
-  View::OnEnabledChanged();  // Jump over Label.
-}
-
 void Link::OnFocus() {
   Label::OnFocus();
   RecalculateFont();
@@ -223,6 +218,9 @@
   underline_ = GetDefaultFocusStyle() != FocusStyle::UNDERLINE;
   RecalculateFont();
 
+  enabled_changed_subscription_ = AddEnabledChangedCallback(
+      base::BindRepeating(&Link::RecalculateFont, base::Unretained(this)));
+
   // Label::Init() calls SetText(), but if that's being called from Label(), our
   // SetText() override will not be reached (because the constructed class is
   // only a Label at the moment, not yet a Link).  So explicitly configure focus
diff --git a/ui/views/controls/link.h b/ui/views/controls/link.h
index 27c73ed..0764dfa 100644
--- a/ui/views/controls/link.h
+++ b/ui/views/controls/link.h
@@ -67,7 +67,6 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
   bool SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-  void OnEnabledChanged() override;
   void OnFocus() override;
   void OnBlur() override;
   void SetFontList(const gfx::FontList& font_list) override;
@@ -105,6 +104,8 @@
   SkColor requested_enabled_color_;
   bool requested_enabled_color_set_;
 
+  PropertyChangedSubscription enabled_changed_subscription_;
+
   DISALLOW_COPY_AND_ASSIGN(Link);
 };
 
diff --git a/ui/views/view.cc b/ui/views/view.cc
index eebe9cf..1b2f567 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -457,20 +457,25 @@
   return visible_ && parent_ ? parent_->IsDrawn() : false;
 }
 
-void View::SetEnabled(bool enabled) {
-  if (enabled != enabled_) {
-    enabled_ = enabled;
-    AdvanceFocusIfNecessary();
+void View::SetEnabled(bool is_enabled) {
+  if (enabled_ == is_enabled)
+    return;
 
-    OnEnabledChanged();
+  enabled_ = is_enabled;
+  AdvanceFocusIfNecessary();
+  OnPropertyChanged(&enabled_, kPropertyEffectsPaint);
 
-    for (ViewObserver& observer : observers_)
-      observer.OnViewEnabledChanged(this);
-  }
+  // TODO(kylixrd): Remove this once all overridden instances are refactored
+  // to use property change callback.
+  OnEnabledChanged();
+}
+
+PropertyChangedSubscription View::AddEnabledChangedCallback(
+    PropertyChangedCallback callback) {
+  return AddPropertyChangedCallback(&enabled_, std::move(callback));
 }
 
 void View::OnEnabledChanged() {
-  SchedulePaint();
 }
 
 View::Views View::GetChildrenInZOrder() {
@@ -1224,7 +1229,7 @@
 
 bool View::CanHandleAccelerators() const {
   const Widget* widget = GetWidget();
-  if (!enabled() || !IsDrawn() || !widget || !widget->IsVisible())
+  if (!GetEnabled() || !IsDrawn() || !widget || !widget->IsVisible())
     return false;
 #if defined(USE_AURA) && !defined(OS_CHROMEOS)
   // Non-ChromeOS aura windows have an associated FocusManagerEventHandler which
@@ -1278,11 +1283,11 @@
 }
 
 bool View::IsFocusable() const {
-  return focus_behavior_ == FocusBehavior::ALWAYS && enabled_ && IsDrawn();
+  return focus_behavior_ == FocusBehavior::ALWAYS && GetEnabled() && IsDrawn();
 }
 
 bool View::IsAccessibilityFocusable() const {
-  return focus_behavior_ != FocusBehavior::NEVER && enabled_ && IsDrawn();
+  return focus_behavior_ != FocusBehavior::NEVER && GetEnabled() && IsDrawn();
 }
 
 FocusManager* View::GetFocusManager() {
@@ -1849,6 +1854,40 @@
   return PaintInfo::ScaleType::kScaleWithEdgeSnapping;
 }
 
+void View::HandlePropertyChangeEffects(PropertyEffects effects) {
+  if (effects & kPropertyEffectsLayout)
+    InvalidateLayout();
+  if (effects & kPropertyEffectsPaint)
+    SchedulePaint();
+}
+
+PropertyChangedSubscription View::AddPropertyChangedCallback(
+    PropertyKey property,
+    PropertyChangedCallback callback) {
+  auto entry = property_changed_vectors_.find(property);
+  if (entry == property_changed_vectors_.end()) {
+    entry = property_changed_vectors_
+                .emplace(property, std::make_unique<PropertyChangedCallbacks>())
+                .first;
+  }
+  PropertyChangedCallbacks* property_changed_callbacks = entry->second.get();
+
+  return property_changed_callbacks->Add(std::move(callback));
+}
+
+void View::OnPropertyChanged(PropertyKey property,
+                             PropertyEffects property_effects) {
+  if (property_effects != kPropertyEffectsNone)
+    HandlePropertyChangeEffects(property_effects);
+
+  auto entry = property_changed_vectors_.find(property);
+  if (entry == property_changed_vectors_.end())
+    return;
+
+  PropertyChangedCallbacks* property_changed_callbacks = entry->second.get();
+  property_changed_callbacks->Notify();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // View, private:
 
@@ -2433,10 +2472,10 @@
       event.IsRightMouseButton() ? context_menu_controller_ : nullptr;
   View::DragInfo* drag_info = GetDragInfo();
 
-  const bool enabled = enabled_;
+  const bool was_enabled = GetEnabled();
   const bool result = OnMousePressed(event);
 
-  if (!enabled)
+  if (!was_enabled)
     return result;
 
   if (event.IsOnlyRightMouseButton() && context_menu_controller &&
diff --git a/ui/views/view.h b/ui/views/view.h
index 125ac49..eccdf5a6 100644
--- a/ui/views/view.h
+++ b/ui/views/view.h
@@ -15,6 +15,9 @@
 #include <utility>
 #include <vector>
 
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/callback_list.h"
 #include "base/compiler_specific.h"
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
@@ -118,6 +121,27 @@
   View* move_view;
 };
 
+// Used to identify the CallbackList<> within the PropertyChangedVectors map.
+using PropertyKey = const void*;
+
+using PropertyChangedCallbacks = base::CallbackList<void()>;
+using PropertyChangedCallback = PropertyChangedCallbacks::CallbackType;
+using PropertyChangedSubscription =
+    std::unique_ptr<PropertyChangedCallbacks::Subscription>;
+
+// The elements in PropertyEffects represent bits which define what effect(s) a
+// changed Property has on the containing class. Additional elements should
+// use the next most significant bit.
+enum PropertyEffects {
+  kPropertyEffectsNone = 0,
+  // Any changes to the property should cause the container to invalidate the
+  // current layout state.
+  kPropertyEffectsLayout = 0x00000001,
+  // Changes to the property should cause the container to schedule a painting
+  // update.
+  kPropertyEffectsPaint = 0x00000002,
+};
+
 /////////////////////////////////////////////////////////////////////////////
 //
 // View class
@@ -142,6 +166,69 @@
 //   Unless otherwise documented, views is not thread safe and should only be
 //   accessed from the main thread.
 //
+//   Properties ------------------
+//
+//   Properties which are intended to be dynamically visible through metadata to
+//   other subsystems, such as dev-tools must adhere to a naming convention,
+//   usage and implementation patterns.
+//
+//   Properties start with their base name, such as "Frobble" (note the
+//   capitalization). The method to set the property must be called SetXXXX and
+//   the method to retrieve the value is called GetXXXX. For the aforementioned
+//   Frobble property, this would be SetFrobble and GetFrobble.
+//
+//   void SetFrobble(bool is_frobble);
+//   bool GetFrobble() const;
+//
+//   In the SetXXXX method, after the value storage location has been updated,
+//   OnPropertyChanged() must be called using the address of the storage
+//   location as a key. Additionally, any combination of PropertyEffects are
+//   also passed in. This will ensure that any desired side effects are properly
+//   invoked.
+//
+//   void View::SetFrobble(bool is_frobble) {
+//     if (is_frobble == frobble_)
+//       return;
+//     frobble_ = is_frobble;
+//     OnPropertyChanged(&frobble_, kPropertyEffectsPaint);
+//   }
+//
+//   Each property should also have a way to "listen" to changes by registering
+//   a callback.
+//
+//   PropertyChangedSubscription AddFrobbleChangedCallback(
+//       PropertyChangedCallback callback) WARN_UNUSED_RETURN;
+//
+//   Each callback uses the the existing base::Bind mechanisms which allow for
+//   various kinds of callbacks; object methods, normal functions and lambdas.
+//
+//   Example:
+//
+//   class FrobbleView : public View {
+//    ...
+//    private:
+//     void OnFrobbleChanged();
+//     PropertyChangeSubscription frobble_changed_subscription_;
+//   }
+//
+//   ...
+//     frobble_changed_subscription_ = AddFrobbleChangedCallback(
+//         base::BindRepeating(&FrobbleView::OnFrobbleChanged,
+//         base::Unretained(this)));
+//
+//   Example:
+//
+//   void MyView::ValidateFrobbleChanged() {
+//     bool frobble_changed = false;
+//     PropertyChangedSubscription subscription =
+//       frobble_view_->AddFrobbleChangedCallback(
+//           base::BindRepeating([](bool* frobble_changed_ptr) {
+//             *frobble_changed_ptr = true;
+//           }, &frobble_changed));
+//     frobble_view_->SetFrobble(!frobble_view_->GetFrobble());
+//     LOG() << frobble_changed ? "Frobble changed" : "Frobble NOT changed!";
+//   }
+//
 /////////////////////////////////////////////////////////////////////////////
 class VIEWS_EXPORT View : public ui::LayerDelegate,
                           public ui::LayerOwner,
@@ -440,12 +527,23 @@
   // Returns true if this view is drawn on screen.
   virtual bool IsDrawn() const;
 
+  // The |Enabled| property. See comment above for instructions on declaring and
+  // implementing a property.
+  //
   // Set whether this view is enabled. A disabled view does not receive keyboard
   // or mouse inputs. If |enabled| differs from the current value, SchedulePaint
   // is invoked. Also, clears focus if the focused view is disabled.
-  void SetEnabled(bool enabled);
-
+  void SetEnabled(bool is_enabled);
   // Returns whether the view is enabled.
+  bool GetEnabled() const { return enabled_; }
+
+  // Adds a callback subscription associated with the above |Enabled| property.
+  // The callback will be invoked whenever the property changes.
+  PropertyChangedSubscription AddEnabledChangedCallback(
+      PropertyChangedCallback callback) WARN_UNUSED_RESULT;
+
+  // NOTE: Deprecated. Please use GetEnabled() which is the getter for the
+  // |Enabled| property.
   bool enabled() const { return enabled_; }
 
   // Returns the child views ordered in reverse z-order. That is, views later in
@@ -1424,6 +1522,14 @@
   // hierarchy).
   virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) {}
 
+  // Property Support ----------------------------------------------------------
+
+  PropertyChangedSubscription AddPropertyChangedCallback(
+      PropertyKey property,
+      PropertyChangedCallback callback);
+  void OnPropertyChanged(PropertyKey property,
+                         PropertyEffects property_effects);
+
  private:
   friend class internal::PreEventDispatchHandler;
   friend class internal::PostEventDispatchHandler;
@@ -1438,6 +1544,9 @@
   FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithMovedViewUsesCacheInRTL);
   FRIEND_TEST_ALL_PREFIXES(ViewTest, PaintWithUnknownInvalidation);
 
+  using PropertyChangedVectors =
+      std::map<PropertyKey, std::unique_ptr<PropertyChangedCallbacks>>;
+
   // Painting  -----------------------------------------------------------------
 
   // Invoked before and after the bounds change to schedule painting the old and
@@ -1692,6 +1801,12 @@
               const gfx::Point& press_pt,
               ui::DragDropTypes::DragEventSource source);
 
+  // Property support ----------------------------------------------------------
+
+  // Called from OnPropertyChanged with the given set of property effects. This
+  // function is NOT called if effects == kPropertyEffectsNone.
+  void HandlePropertyChangeEffects(PropertyEffects effects);
+
   //////////////////////////////////////////////////////////////////////////////
 
   // Creation and lifetime -----------------------------------------------------
@@ -1852,10 +1967,13 @@
   // Manages the accessibility interface for this View.
   std::unique_ptr<ViewAccessibility> view_accessibility_;
 
-  // Observers -------------------------------------------------------------
+  // Observers -----------------------------------------------------------------
 
   base::ObserverList<ViewObserver>::Unchecked observers_;
 
+  // Property Changed Callbacks ------------------------------------------------
+  PropertyChangedVectors property_changed_vectors_;
+
   DISALLOW_COPY_AND_ASSIGN(View);
 };
 
diff --git a/ui/views/view_observer.h b/ui/views/view_observer.h
index bcd0a61..39695be 100644
--- a/ui/views/view_observer.h
+++ b/ui/views/view_observer.h
@@ -26,9 +26,6 @@
   // View::IsDrawn() for details on how visibility and drawn differ.
   virtual void OnViewVisibilityChanged(View* observed_view) {}
 
-  // Called when View::SetEnabled() is called with a new value.
-  virtual void OnViewEnabledChanged(View* observed_view) {}
-
   // Called from View::PreferredSizeChanged().
   virtual void OnViewPreferredSizeChanged(View* observed_view) {}
 
diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc
index 46e3f804..174b0ac 100644
--- a/ui/views/view_unittest.cc
+++ b/ui/views/view_unittest.cc
@@ -9,6 +9,7 @@
 #include <map>
 #include <memory>
 
+#include "base/bind.h"
 #include "base/command_line.h"
 #include "base/i18n/rtl.h"
 #include "base/macros.h"
@@ -4979,6 +4980,17 @@
   EXPECT_EQ(layers_after_attached[1], child_view1->layer());
 }
 
+TEST_F(ViewTest, TestEnabledChangedCallback) {
+  View test_view;
+  bool enabled_changed = false;
+  auto subscription = test_view.AddEnabledChangedCallback(base::BindRepeating(
+      [](bool* enabled_changed) { *enabled_changed = true; },
+      &enabled_changed));
+  test_view.SetEnabled(false);
+  EXPECT_TRUE(enabled_changed);
+  EXPECT_FALSE(test_view.enabled());
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Observer tests.
 ////////////////////////////////////////////////////////////////////////////////
@@ -5005,10 +5017,6 @@
     view_visibility_changed_ = view;
   }
 
-  void OnViewEnabledChanged(View* view) override {
-    view_enabled_changed_ = view;
-  }
-
   void OnViewBoundsChanged(View* view) override { view_bounds_changed_ = view; }
 
   void OnChildViewReordered(View* parent, View* view) override {
@@ -5023,7 +5031,6 @@
     child_view_removed_ = nullptr;
     child_view_removed_parent_ = nullptr;
     view_visibility_changed_ = nullptr;
-    view_enabled_changed_ = nullptr;
     view_bounds_changed_ = nullptr;
     view_reordered_ = nullptr;
   }
@@ -5047,7 +5054,6 @@
   const View* view_visibility_changed() const {
     return view_visibility_changed_;
   }
-  const View* view_enabled_changed() const { return view_enabled_changed_; }
   const View* view_bounds_changed() const { return view_bounds_changed_; }
   const View* view_reordered() const { return view_reordered_; }
 
@@ -5060,7 +5066,6 @@
   View* child_view_removed_ = nullptr;
   View* child_view_removed_parent_ = nullptr;
   View* view_visibility_changed_ = nullptr;
-  View* view_enabled_changed_ = nullptr;
   View* view_bounds_changed_ = nullptr;
   View* view_reordered_ = nullptr;
 
@@ -5105,13 +5110,6 @@
   EXPECT_FALSE(view->visible());
 }
 
-TEST_F(ViewObserverTest, ViewEnabledChanged) {
-  std::unique_ptr<View> view = NewView();
-  view->SetEnabled(false);
-  EXPECT_EQ(view.get(), view_enabled_changed());
-  EXPECT_FALSE(view->enabled());
-}
-
 TEST_F(ViewObserverTest, ViewBoundsChanged) {
   std::unique_ptr<View> view = NewView();
   gfx::Rect bounds(2, 2, 2, 2);
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc
index 8aa6955..b8e92a1 100644
--- a/ui/views/window/dialog_delegate_unittest.cc
+++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -98,13 +98,6 @@
 
   views::Textfield* input() { return input_; }
 
-  ui::ModalType GetModalType() const override {
-    return modal_type_none_ ? ui::MODAL_TYPE_NONE : ui::MODAL_TYPE_WINDOW;
-  }
-  void set_modal_type_none(bool modal_type_none) {
-    modal_type_none_ = modal_type_none;
-  }
-
  private:
   views::Textfield* input_;
   bool canceled_ = false;
@@ -116,7 +109,6 @@
   bool show_close_button_ = true;
   bool should_handle_escape_ = false;
   int dialog_buttons_ = ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
-  bool modal_type_none_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(TestDialog);
 };
@@ -224,22 +216,22 @@
   const NonClientView* view = dialog()->GetWidget()->non_client_view();
   BubbleFrameView* frame = static_cast<BubbleFrameView*>(view->frame_view());
 
-  constexpr struct {
+  struct {
     const int point;
     const int hit;
-  } kCases[] = {
-      {0, HTTRANSPARENT},
-      {10, HTNOWHERE},
+  } cases[] = {
+      {0, HTSYSMENU},
+      {10, HTSYSMENU},
       {20, HTNOWHERE},
       {50, HTCLIENT /* Space is reserved for the close button. */},
       {60, HTCLIENT},
       {1000, HTNOWHERE},
   };
 
-  for (const auto test_case : kCases) {
-    gfx::Point point(test_case.point, test_case.point);
-    EXPECT_EQ(test_case.hit, frame->NonClientHitTest(point))
-        << " at point " << test_case.point;
+  for (size_t i = 0; i < base::size(cases); ++i) {
+    gfx::Point point(cases[i].point, cases[i].point);
+    EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point))
+        << " case " << i << " at point " << cases[i].point;
   }
 }
 
@@ -251,18 +243,18 @@
   const NonClientView* view = dialog()->GetWidget()->non_client_view();
   BubbleFrameView* frame = static_cast<BubbleFrameView*>(view->frame_view());
 
-  constexpr struct {
+  struct {
     const int point;
     const int hit;
-  } kCases[] = {
-      {0, HTTRANSPARENT}, {10, HTCLIENT}, {20, HTCLIENT},
-      {50, HTCLIENT},     {60, HTCLIENT}, {1000, HTNOWHERE},
+  } cases[] = {
+      {0, HTSYSMENU}, {10, HTSYSMENU}, {20, HTCLIENT},
+      {50, HTCLIENT}, {60, HTCLIENT},  {1000, HTNOWHERE},
   };
 
-  for (const auto test_case : kCases) {
-    gfx::Point point(test_case.point, test_case.point);
-    EXPECT_EQ(test_case.hit, frame->NonClientHitTest(point))
-        << " at point " << test_case.point;
+  for (size_t i = 0; i < base::size(cases); ++i) {
+    gfx::Point point(cases[i].point, cases[i].point);
+    EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point))
+        << " case " << i << " at point " << cases[i].point;
   }
 }
 
@@ -274,43 +266,18 @@
   dialog()->GetWidget()->LayoutRootViewIfNecessary();
   BubbleFrameView* frame = static_cast<BubbleFrameView*>(view->frame_view());
 
-  constexpr struct {
+  struct {
     const int point;
     const int hit;
-  } kCases[] = {
-      {0, HTTRANSPARENT}, {10, HTNOWHERE}, {20, HTNOWHERE},
-      {50, HTCLIENT},     {60, HTCLIENT},  {1000, HTNOWHERE},
+  } cases[] = {
+      {0, HTSYSMENU}, {10, HTSYSMENU}, {20, HTCAPTION},
+      {50, HTCLIENT}, {60, HTCLIENT},  {1000, HTNOWHERE},
   };
 
-  for (const auto test_case : kCases) {
-    gfx::Point point(test_case.point, test_case.point);
-    EXPECT_EQ(test_case.hit, frame->NonClientHitTest(point))
-        << " at point " << test_case.point;
-  }
-}
-
-TEST_F(DialogTest, HitTest_ModalTypeNone_WithTitle) {
-  // Ensure that BubbleFrameView hit-tests as expected when the title is shown
-  // and the modal type is none.
-  const NonClientView* view = dialog()->GetWidget()->non_client_view();
-  dialog()->set_title(base::ASCIIToUTF16("Title"));
-  dialog()->GetWidget()->UpdateWindowTitle();
-  dialog()->set_modal_type_none(true);
-  dialog()->GetWidget()->LayoutRootViewIfNecessary();
-  BubbleFrameView* frame = static_cast<BubbleFrameView*>(view->frame_view());
-
-  constexpr struct {
-    const int point;
-    const int hit;
-  } kCases[] = {
-      {0, HTTRANSPARENT}, {10, HTCAPTION}, {20, HTCAPTION},
-      {50, HTCLIENT},     {60, HTCLIENT},  {1000, HTNOWHERE},
-  };
-
-  for (const auto test_case : kCases) {
-    gfx::Point point(test_case.point, test_case.point);
-    EXPECT_EQ(test_case.hit, frame->NonClientHitTest(point))
-        << " at point " << test_case.point;
+  for (size_t i = 0; i < base::size(cases); ++i) {
+    gfx::Point point(cases[i].point, cases[i].point);
+    EXPECT_EQ(cases[i].hit, frame->NonClientHitTest(point))
+        << " at point " << cases[i].point;
   }
 }
 
diff --git a/url/url_canon_pathurl.cc b/url/url_canon_pathurl.cc
index 494fbdaf..36fbce8e 100644
--- a/url/url_canon_pathurl.cc
+++ b/url/url_canon_pathurl.cc
@@ -16,13 +16,12 @@
 // Canonicalize the given |component| from |source| into |output| and
 // |new_component|. If |separator| is non-zero, it is pre-pended to |output|
 // prior to the canonicalized component; i.e. for the '?' or '#' characters.
-template<typename CHAR, typename UCHAR>
-bool DoCanonicalizePathComponent(const CHAR* source,
+template <typename CHAR, typename UCHAR>
+void DoCanonicalizePathComponent(const CHAR* source,
                                  const Component& component,
                                  char separator,
                                  CanonOutput* output,
                                  Component* new_component) {
-  bool success = true;
   if (component.is_valid()) {
     if (separator)
       output->push_back(separator);
@@ -34,7 +33,7 @@
     for (int i = component.begin; i < end; i++) {
       UCHAR uch = static_cast<UCHAR>(source[i]);
       if (uch < 0x20 || uch >= 0x80)
-        success &= AppendUTF8EscapedChar(source, &i, end, output);
+        AppendUTF8EscapedChar(source, &i, end, output);
       else
         output->push_back(static_cast<char>(uch));
     }
@@ -43,7 +42,6 @@
     // Empty part.
     new_component->reset();
   }
-  return success;
 }
 
 template <typename CHAR, typename UCHAR>
@@ -63,12 +61,15 @@
   new_parsed->port.reset();
   // We allow path URLs to have the path, query and fragment components, but we
   // will canonicalize each of the via the weaker path URL rules.
-  success &= DoCanonicalizePathComponent<CHAR, UCHAR>(
-      source.path, parsed.path, '\0', output, &new_parsed->path);
-  success &= DoCanonicalizePathComponent<CHAR, UCHAR>(
-      source.query, parsed.query, '?', output, &new_parsed->query);
-  success &= DoCanonicalizePathComponent<CHAR, UCHAR>(
-      source.ref, parsed.ref, '#', output, &new_parsed->ref);
+  //
+  // Note: parsing the path part should never cause a failure, see
+  // https://url.spec.whatwg.org/#cannot-be-a-base-url-path-state
+  DoCanonicalizePathComponent<CHAR, UCHAR>(source.path, parsed.path, '\0',
+                                           output, &new_parsed->path);
+  DoCanonicalizePathComponent<CHAR, UCHAR>(source.query, parsed.query, '?',
+                                           output, &new_parsed->query);
+  DoCanonicalizePathComponent<CHAR, UCHAR>(source.ref, parsed.ref, '#', output,
+                                           &new_parsed->ref);
 
   return success;
 }
diff --git a/url/url_canon_unittest.cc b/url/url_canon_unittest.cc
index 3f80036..5a487ca 100644
--- a/url/url_canon_unittest.cc
+++ b/url/url_canon_unittest.cc
@@ -1873,9 +1873,13 @@
     const char* input;
     const char* expected;
   } path_cases[] = {
-    {"javascript:", "javascript:"},
-    {"JavaScript:Foo", "javascript:Foo"},
-    {"Foo:\":This /is interesting;?#", "foo:\":This /is interesting;?#"},
+      {"javascript:", "javascript:"},
+      {"JavaScript:Foo", "javascript:Foo"},
+      {"Foo:\":This /is interesting;?#", "foo:\":This /is interesting;?#"},
+
+      // Validation errors should not cause failure. See
+      // https://crbug.com/925614.
+      {"javascript:\uFFFF", "javascript:%EF%BF%BD"},
   };
 
   for (size_t i = 0; i < base::size(path_cases); i++) {