diff --git a/DEPS b/DEPS
index be26caa..92e42f7 100644
--- a/DEPS
+++ b/DEPS
@@ -199,11 +199,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': 'db2dad5c644f5a57059ef57483d52090f0e3c0c9',
+  'skia_revision': 'd2f51b18065a1ed93a4195afb8852b12e2fae1cd',
   # 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': 'd87c3006482190728da53f87875b2a528dfb7ef3',
+  'v8_revision': 'ff24c7121a8adc0450e1368daf5e060b7b4f64ca',
   # 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.
@@ -250,7 +250,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'cbc14b10e623a5d08c89a1323ae9c420c8492118',
+  'freetype_revision': '0901a6f74c707f4e631d810b9f3ac7f0c0d2042e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -274,7 +274,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'b43fbe8a06ea52e110218c82644181ed12917400',
+  'devtools_frontend_revision': '51690fcadbede4f2005aece3d76191b7464d367f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -314,11 +314,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '00fcab636a055c78036ee20bb1a89339b0a28bad',
+  'dawn_revision': 'a2e02836a2600aa751f1b7eeb6bb56c4e573e230',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '63bdfe586f8ceddf5e0823b75b1c595cbd380b2e',
+  'quiche_revision': '425c25a8ad2d190f61531fd46e0a65050cc1b98a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -1266,7 +1266,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '070ef598892aee3d5cfb628f577867217c863142',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'afc78f5e6ae999fd26a7473e8e2b068b6e666063',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1344,7 +1344,7 @@
       'packages': [
           {
               'package': 'fuchsia/third_party/aemu/linux-amd64',
-              'version': 'e-bgBYXeOkMw5xrqjCgQDp16bMPZeKKmilHzC-t2-1QC'
+              'version': 'luM2HIHgfBKxr1C7UPo8RdQPAvyLNd74T9rYfhWFOC8C'
           },
       ],
       'condition': 'host_os == "linux" and checkout_fuchsia',
@@ -1483,9 +1483,9 @@
   },
 
   'src/third_party/usrsctp/usrsctplib':
-    Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '4191ca1784d8774dbf62d48ab9426c7311a91bc5',
+    Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + 'a3c3ef666b7a5e4c93ebae5a7462add6f86f5cf2',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@ffb3b84133b03dfc4b83a8430a418f4fa94308d8',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@e395b66ed41c80a28349f18f9b16bce6bd80e2b3',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '6c656df63da5995a932aafd45b32af1974e497d9',
@@ -1518,7 +1518,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3c2fe3888658d82b47ca831d59a2e07579619c2d',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'cdb3bc3b93dae5237610d0e45bccd900812a0cfe',
+    Var('webrtc_git') + '/src.git' + '@' + '1607b3aa8ccd55e48e2fc0204ee5cbe873d41928',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1590,7 +1590,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8e33046d8bd53448771287085284e477d29aef1d',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@36b254fb9b13503b7b3b181da82ab3a626c770e6',
     'condition': 'checkout_src_internal',
   },
 
@@ -1609,7 +1609,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'mqExY_CKGV64CkBK6pxd74QrlY0Ui90tdf5wIZPeB38C',
+        'version': 'G2hKrv9siNlrFfUOPnGITX-1vPXVfef552IDKjdJcgoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 33c3deba..a8c68cc 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -316,6 +316,12 @@
                 cl.appendSwitch(AwSwitches.WEBVIEW_SANDBOXED_RENDERER);
             }
 
+            // Enable modern SameSite cookie behavior if the app targets at least S.
+            if (BuildInfo.targetsAtLeastS()) {
+                CommandLine cl = CommandLine.getInstance();
+                cl.appendSwitch(AwSwitches.WEBVIEW_ENABLE_MODERN_COOKIE_SAME_SITE);
+            }
+
             int applicationFlags = ctx.getApplicationInfo().flags;
             boolean isAppDebuggable = (applicationFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
             boolean isOsDebuggable = BuildInfo.isDebugAndroid();
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index 1ed65800..b913b48 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -177,8 +177,6 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
-    @CommandLineFlags.Add({"enable-blink-features=CookieStoreDocument"})
-    // TODO(https://crbug.com/968649) Remove switch when CookieStore launched.
     public void testAcceptCookie_falseWontSetCookies() throws Throwable {
         testAcceptCookieHelper(false, "-disabled");
     }
@@ -186,8 +184,6 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
-    @CommandLineFlags.Add({"enable-blink-features=CookieStoreDocument"})
-    // TODO(https://crbug.com/968649) Remove switch when CookieStore launched.
     public void testAcceptCookie_trueWillSetCookies() throws Throwable {
         testAcceptCookieHelper(true, "-enabled");
     }
@@ -686,8 +682,6 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
-    @CommandLineFlags.Add({"enable-blink-features=CookieStoreDocument"})
-    // TODO(https://crbug.com/968649) Remove switch when CookieStore launched.
     public void testCookieStoreListener() throws Throwable {
         TestWebServer webServer = TestWebServer.startSsl();
         try {
@@ -1005,8 +999,6 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
-    @CommandLineFlags.Add({"enable-blink-features=CookieStoreDocument"})
-    // TODO(https://crbug.com/968649) Remove switch when CookieStore launched.
     public void testThirdPartyJavascriptCookie() throws Throwable {
         // Using SSL server here since CookieStore API requires a secure schema.
         TestWebServer webServer = TestWebServer.startSsl();
@@ -1033,8 +1025,6 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
-    @CommandLineFlags.Add({"enable-blink-features=CookieStoreDocument"})
-    // TODO(https://crbug.com/968649) Remove switch when CookieStore launched.
     public void testThirdPartyCookiesArePerWebview() throws Throwable {
         // Using SSL server here since CookieStore API requires a secure schema.
         TestWebServer webServer = TestWebServer.startSsl();
diff --git a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_LOCATE_BUTTON_NOT_AVAILABLE_TOOLTIP.png.sha1 b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_LOCATE_BUTTON_NOT_AVAILABLE_TOOLTIP.png.sha1
index 834b4364..48675ec 100644
--- a/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_LOCATE_BUTTON_NOT_AVAILABLE_TOOLTIP.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_PHONE_HUB_LOCATE_BUTTON_NOT_AVAILABLE_TOOLTIP.png.sha1
@@ -1 +1 @@
-d7fa90d6f286617baaaef959c781bab2d64f43ad
\ No newline at end of file
+b0805735ec8f8c3fe148d54af99053ff89594236
\ No newline at end of file
diff --git a/ash/system/network/tray_network_state_model.cc b/ash/system/network/tray_network_state_model.cc
index c71254c..7fc789b 100644
--- a/ash/system/network/tray_network_state_model.cc
+++ b/ash/system/network/tray_network_state_model.cc
@@ -174,10 +174,10 @@
   impl_->SetNetworkTypeEnabledState(type, enabled);
 }
 
-bool TrayNetworkStateModel::IsBuiltinVpnEnabled() const {
+bool TrayNetworkStateModel::IsBuiltinVpnProhibited() const {
   return TrayNetworkStateModel::GetDeviceState(
              chromeos::network_config::mojom::NetworkType::kVPN) ==
-         chromeos::network_config::mojom::DeviceStateType::kEnabled;
+         chromeos::network_config::mojom::DeviceStateType::kProhibited;
 }
 
 chromeos::network_config::mojom::CrosNetworkConfig*
diff --git a/ash/system/network/tray_network_state_model.h b/ash/system/network/tray_network_state_model.h
index 80e8f36..48d08064 100644
--- a/ash/system/network/tray_network_state_model.h
+++ b/ash/system/network/tray_network_state_model.h
@@ -46,9 +46,9 @@
       chromeos::network_config::mojom::NetworkType type,
       bool enabled);
 
-  // Returns true if built-in VPN is enabled.
-  // Note: Currently only built-in VPNs can be disabled by policy.
-  bool IsBuiltinVpnEnabled() const;
+  // Returns true if built-in VPN is prohibited.
+  // Note: Currently only built-in VPNs can be prohibited by policy.
+  bool IsBuiltinVpnProhibited() const;
 
   // This used to be inlined but now requires details from the Impl class.
   chromeos::network_config::mojom::CrosNetworkConfig* cros_network_config();
diff --git a/ash/system/network/vpn_feature_pod_controller.cc b/ash/system/network/vpn_feature_pod_controller.cc
index d8973fc0..f53e8e3 100644
--- a/ash/system/network/vpn_feature_pod_controller.cc
+++ b/ash/system/network/vpn_feature_pod_controller.cc
@@ -36,7 +36,7 @@
     return true;
 
   // Note: At this point, only built-in VPNs are considered.
-  return model->IsBuiltinVpnEnabled() && model->has_vpn();
+  return !model->IsBuiltinVpnProhibited() && model->has_vpn();
 }
 
 }  // namespace
diff --git a/ash/system/network/vpn_list_view.cc b/ash/system/network/vpn_list_view.cc
index 8c70d3a1..1ad37729 100644
--- a/ash/system/network/vpn_list_view.cc
+++ b/ash/system/network/vpn_list_view.cc
@@ -440,8 +440,8 @@
   views::View* provider_view = nullptr;
 
   // Note: Currently only built-in VPNs can be disabled by policy.
-  bool vpn_enabled =
-      vpn_provider->type != VpnType::kOpenVPN || model()->IsBuiltinVpnEnabled();
+  bool vpn_enabled = vpn_provider->type != VpnType::kOpenVPN ||
+                     !model()->IsBuiltinVpnProhibited();
 
   provider_view =
       new VPNListProviderEntry(vpn_provider, list_empty_, vpn_name, vpn_enabled,
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index e012c891..b236c35 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -1102,7 +1102,18 @@
   //   a. Call to the thread cache, if it succeeds, go to step 3.
   //   b. Otherwise, call the "raw" allocator <-- Locking
   // 3. Handle cookies/ref-count, zero allocation if required
-  size_t raw_size = AdjustSizeForExtrasAdd(requested_size);
+
+  size_t raw_size = requested_size;
+#if ENABLE_REF_COUNT_FOR_BACKUP_REF_PTR
+  // Without the size adjustment below, |Alloc()| returns a pointer past the end
+  // of a slot (most of the time a pointer to the beginning of the next slot)
+  // for zero-sized allocations when |PartitionRefCount| is used. The returned
+  // value may lead to incorrect results when passed to a function that performs
+  // bitwise operations on pointers, e.g., |PartitionAllocGetSlotOffset()|.
+  if (UNLIKELY(raw_size == 0))
+    raw_size = 1;
+#endif  // ENABLE_REF_COUNT_FOR_BACKUP_REF_PTR
+  raw_size = AdjustSizeForExtrasAdd(raw_size);
   PA_CHECK(raw_size >= requested_size);  // check for overflows
 
   uint16_t bucket_index = SizeToBucketIndex(raw_size);
diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
index b405d92..0f403f9 100644
--- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
+++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java
@@ -112,7 +112,7 @@
     private final Object mNonMainDexLock = new Object();
 
     // Mediates all communication between Linker instances in different processes.
-    private final MultiProcessMediator mMessageHandler = new MultiProcessMediator();
+    private final MultiProcessMediator mMediator = new MultiProcessMediator();
 
     // Guards all the fields below.
     private final Object mLock = new Object();
@@ -248,7 +248,7 @@
     }
 
     public final MultiProcessMediator getMediator() {
-        return mMessageHandler;
+        return mMediator;
     }
 
     /**
diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc
index a4de2af9..1368e5b1 100644
--- a/base/files/important_file_writer.cc
+++ b/base/files/important_file_writer.cc
@@ -124,12 +124,9 @@
   if (tmp_file.IsValid()) {
     if (tmp_file.DeleteOnClose(true))
       return;
-    // The file was opened with exclusive r/w access, so it would be very odd
-    // for this to fail.
-    UmaHistogramExactLinearWithSuffix(
-        "ImportantFile.DeleteOnCloseError", histogram_suffix,
-        -File::GetLastFileError(), -File::FILE_ERROR_MAX);
-    // Go ahead and close the file. The call to DeleteFile below will basically
+    // The file was opened with exclusive r/w access, so failures are primarily
+    // due to I/O errors or other phenomena out of the process's control. Go
+    // ahead and close the file. The call to DeleteFile below will basically
     // repeat the above, but maybe it will somehow succeed.
     tmp_file.Close();
   }
diff --git a/base/memory/checked_ptr_unittest.cc b/base/memory/checked_ptr_unittest.cc
index 064f48b..6d1bf09 100644
--- a/base/memory/checked_ptr_unittest.cc
+++ b/base/memory/checked_ptr_unittest.cc
@@ -754,6 +754,26 @@
 #endif  // DCHECK_IS_ON()
 }
 
+TEST(BackupRefPtrImpl, ZeroSized) {
+  // This test works only if GigaCage is enabled. Bail out otherwise.
+  if (!features::IsPartitionAllocGigaCageEnabled())
+    return;
+
+  // TODO(bartekn): Avoid using PartitionAlloc API directly. Switch to
+  // new/delete once PartitionAlloc Everywhere is fully enabled.
+  PartitionAllocGlobalInit(HandleOOM);
+  PartitionAllocator<ThreadSafe> allocator;
+  allocator.init({});
+
+  std::vector<CheckedPtr<void>> ptrs;
+  // Use a reasonable number of elements to fill up the slot span.
+  for (int i = 0; i < 128 * 1024; ++i) {
+    // Constructing a CheckedPtr instance from a zero-sized allocation should
+    // not result in a crash.
+    ptrs.emplace_back(allocator.root()->Alloc(0, ""));
+  }
+}
+
 #endif  // BUILDFLAG(USE_PARTITION_ALLOC) && ENABLE_BACKUP_REF_PTR_IMPL &&
         // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
 }  // namespace internal
diff --git a/base/strings/string16.cc b/base/strings/string16.cc
index 64a5951..60f26f3 100644
--- a/base/strings/string16.cc
+++ b/base/strings/string16.cc
@@ -4,86 +4,28 @@
 
 #include "base/strings/string16.h"
 
-#if defined(WCHAR_T_IS_UTF16) && !defined(_AIX)
+#include <string>
 
-#error This file should not be used on 2-byte wchar_t systems
-// If this winds up being needed on 2-byte wchar_t systems, either the
-// definitions below can be used, or the host system's wide character
-// functions like wmemcmp can be wrapped.
+#include "base/strings/utf_string_conversions.h"
 
-#elif defined(WCHAR_T_IS_UTF32)
-
-#include <string.h>
-
-#include <ostream>
-
-#include "base/strings/string_piece.h"
+#if defined(WCHAR_T_IS_UTF32)
+std::ostream& std::operator<<(std::ostream& out, const std::u16string& str16) {
+  return out << base::UTF16ToUTF8(str16);
+}
+#endif
 
 namespace base {
 
 int c16memcmp(const char16* s1, const char16* s2, size_t n) {
-  // We cannot call memcmp because that changes the semantics.
-  while (n-- > 0) {
-    if (*s1 != *s2) {
-      // We cannot use (*s1 - *s2) because char16 is unsigned.
-      return ((*s1 < *s2) ? -1 : 1);
-    }
-    ++s1;
-    ++s2;
-  }
-  return 0;
+  return std::char_traits<char16>::compare(s1, s2, n);
 }
 
 size_t c16len(const char16* s) {
-  const char16 *s_orig = s;
-  while (*s) {
-    ++s;
-  }
-  return s - s_orig;
-}
-
-const char16* c16memchr(const char16* s, char16 c, size_t n) {
-  while (n-- > 0) {
-    if (*s == c) {
-      return s;
-    }
-    ++s;
-  }
-  return nullptr;
-}
-
-char16* c16memmove(char16* s1, const char16* s2, size_t n) {
-  return static_cast<char16*>(memmove(s1, s2, n * sizeof(char16)));
+  return std::char_traits<char16>::length(s);
 }
 
 char16* c16memcpy(char16* s1, const char16* s2, size_t n) {
-  return static_cast<char16*>(memcpy(s1, s2, n * sizeof(char16)));
+  return std::char_traits<char16>::copy(s1, s2, n);
 }
 
-char16* c16memset(char16* s, char16 c, size_t n) {
-  char16 *s_orig = s;
-  while (n-- > 0) {
-    *s = c;
-    ++s;
-  }
-  return s_orig;
-}
-
-namespace string16_internals {
-
-std::ostream& operator<<(std::ostream& out, const string16& str) {
-  return out << base::StringPiece16(str);
-}
-
-void PrintTo(const string16& str, std::ostream* out) {
-  *out << str;
-}
-
-}  // namespace string16_internals
-
 }  // namespace base
-
-template class std::
-    basic_string<base::char16, base::string16_internals::string16_char_traits>;
-
-#endif  // WCHAR_T_IS_UTF32
diff --git a/base/strings/string16.h b/base/strings/string16.h
index 3cb6c7c..212b01d 100644
--- a/base/strings/string16.h
+++ b/base/strings/string16.h
@@ -31,6 +31,7 @@
 #include <stdio.h>
 
 #include <functional>
+#include <ostream>
 #include <string>
 
 #include "base/base_export.h"
@@ -42,188 +43,40 @@
 // a literal string. This indirection allows for an easier migration of
 // base::char16 to char16_t on platforms where WCHAR_T_IS_UTF16, as only a one
 // character change to the macro will be necessary.
-// This macro does not exist when WCHAR_T_IS_UTF32, as it is currently not
-// possible to create a char array form a literal in this case.
 // TODO(https://crbug.com/911896): Remove this macro once base::char16 is
 // char16_t on all platforms.
 #define STRING16_LITERAL(x) L##x
 
 namespace base {
-
-typedef wchar_t char16;
-typedef std::wstring string16;
-
+using string16 = std::wstring;
 }  // namespace base
 
-#elif defined(WCHAR_T_IS_UTF32)
+#else
 
-#include <wchar.h>  // for mbstate_t
+#define STRING16_LITERAL(x) u##x
+
+namespace base {
+using string16 = std::u16string;
+}  // namespace base
+
+// TODO(crbug.com/911896): Move this logging logic to base/logging.h.
+namespace std {
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+                                     const std::u16string& str16);
+}  // namespace std
+
+#endif  // WCHAR_T_IS_UTF16
 
 namespace base {
 
-typedef uint16_t char16;
+using char16 = ::base::string16::value_type;
 
-// char16 versions of the functions required by string16_char_traits; these
-// are based on the wide character functions of similar names ("w" or "wcs"
-// instead of "c16").
+// TODO(crbug.com/911896): Remove these functions in favor of using
+// std::char_traits<base::char16> directly.
 BASE_EXPORT int c16memcmp(const char16* s1, const char16* s2, size_t n);
 BASE_EXPORT size_t c16len(const char16* s);
-BASE_EXPORT const char16* c16memchr(const char16* s, char16 c, size_t n);
-BASE_EXPORT char16* c16memmove(char16* s1, const char16* s2, size_t n);
 BASE_EXPORT char16* c16memcpy(char16* s1, const char16* s2, size_t n);
-BASE_EXPORT char16* c16memset(char16* s, char16 c, size_t n);
-
-// This namespace contains the implementation of base::string16 along with
-// things that need to be found via argument-dependent lookup from a
-// base::string16.
-namespace string16_internals {
-
-struct string16_char_traits {
-  typedef char16 char_type;
-  typedef int int_type;
-
-  // int_type needs to be able to hold each possible value of char_type, and in
-  // addition, the distinct value of eof().
-  static_assert(sizeof(int_type) > sizeof(char_type),
-                "int must be larger than 16 bits wide");
-
-  typedef std::streamoff off_type;
-  typedef mbstate_t state_type;
-  typedef std::fpos<state_type> pos_type;
-
-  static void assign(char_type& c1, const char_type& c2) {
-    c1 = c2;
-  }
-
-  static bool eq(const char_type& c1, const char_type& c2) {
-    return c1 == c2;
-  }
-  static bool lt(const char_type& c1, const char_type& c2) {
-    return c1 < c2;
-  }
-
-  static int compare(const char_type* s1, const char_type* s2, size_t n) {
-    return c16memcmp(s1, s2, n);
-  }
-
-  static size_t length(const char_type* s) {
-    return c16len(s);
-  }
-
-  static const char_type* find(const char_type* s, size_t n,
-                               const char_type& a) {
-    return c16memchr(s, a, n);
-  }
-
-  static char_type* move(char_type* s1, const char_type* s2, size_t n) {
-    return c16memmove(s1, s2, n);
-  }
-
-  static char_type* copy(char_type* s1, const char_type* s2, size_t n) {
-    return c16memcpy(s1, s2, n);
-  }
-
-  static char_type* assign(char_type* s, size_t n, char_type a) {
-    return c16memset(s, a, n);
-  }
-
-  static int_type not_eof(const int_type& c) {
-    return eq_int_type(c, eof()) ? 0 : c;
-  }
-
-  static char_type to_char_type(const int_type& c) {
-    return char_type(c);
-  }
-
-  static int_type to_int_type(const char_type& c) {
-    return int_type(c);
-  }
-
-  static bool eq_int_type(const int_type& c1, const int_type& c2) {
-    return c1 == c2;
-  }
-
-  static int_type eof() {
-    return static_cast<int_type>(EOF);
-  }
-};
-
-}  // namespace string16_internals
-
-typedef std::basic_string<char16,
-                          base::string16_internals::string16_char_traits>
-    string16;
-
-namespace string16_internals {
-
-BASE_EXPORT extern std::ostream& operator<<(std::ostream& out,
-                                            const string16& str);
-
-// This is required by googletest to print a readable output on test failures.
-BASE_EXPORT extern void PrintTo(const string16& str, std::ostream* out);
-
-}  // namespace string16_internals
 
 }  // namespace base
 
-// The string class will be explicitly instantiated only once, in string16.cc.
-//
-// std::basic_string<> in GNU libstdc++ contains a static data member,
-// _S_empty_rep_storage, to represent empty strings.  When an operation such
-// as assignment or destruction is performed on a string, causing its existing
-// data member to be invalidated, it must not be freed if this static data
-// member is being used.  Otherwise, it counts as an attempt to free static
-// (and not allocated) data, which is a memory error.
-//
-// Generally, due to C++ template magic, _S_empty_rep_storage will be marked
-// as a coalesced symbol, meaning that the linker will combine multiple
-// instances into a single one when generating output.
-//
-// If a string class is used by multiple shared libraries, a problem occurs.
-// Each library will get its own copy of _S_empty_rep_storage.  When strings
-// are passed across a library boundary for alteration or destruction, memory
-// errors will result.  GNU libstdc++ contains a configuration option,
-// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which
-// disables the static data member optimization, but it's a good optimization
-// and non-STL code is generally at the mercy of the system's STL
-// configuration.  Fully-dynamic strings are not the default for GNU libstdc++
-// libstdc++ itself or for the libstdc++ installations on the systems we care
-// about, such as Mac OS X and relevant flavors of Linux.
-//
-// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 .
-//
-// To avoid problems, string classes need to be explicitly instantiated only
-// once, in exactly one library.  All other string users see it via an "extern"
-// declaration.  This is precisely how GNU libstdc++ handles
-// std::basic_string<char> (string) and std::basic_string<wchar_t> (wstring).
-//
-// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2),
-// in which the linker does not fully coalesce symbols when dead code
-// stripping is enabled.  This bug causes the memory errors described above
-// to occur even when a std::basic_string<> does not cross shared library
-// boundaries, such as in statically-linked executables.
-//
-// TODO(mark): File this bug with Apple and update this note with a bug number.
-
-extern template class BASE_EXPORT
-    std::basic_string<base::char16,
-                      base::string16_internals::string16_char_traits>;
-
-// Specialize std::hash for base::string16. Although the style guide forbids
-// this in general, it is necessary for consistency with WCHAR_T_IS_UTF16
-// platforms, where base::string16 is a type alias for std::wstring.
-namespace std {
-template <>
-struct hash<base::string16> {
-  std::size_t operator()(const base::string16& s) const {
-    std::size_t result = 0;
-    for (base::char16 c : s)
-      result = (result * 131) + c;
-    return result;
-  }
-};
-}  // namespace std
-
-#endif  // WCHAR_T_IS_UTF32
-
 #endif  // BASE_STRINGS_STRING16_H_
diff --git a/base/strings/string16_unittest.cc b/base/strings/string16_unittest.cc
index 0e41864..5c020e2a 100644
--- a/base/strings/string16_unittest.cc
+++ b/base/strings/string16_unittest.cc
@@ -13,13 +13,15 @@
 
 namespace base {
 
-#if defined(WCHAR_T_IS_UTF16)
 TEST(String16Test, String16Literal) {
   static constexpr char16 kHelloWorld[] = STRING16_LITERAL("Hello, World");
+  constexpr StringPiece16 kPiece = kHelloWorld;
+  static_assert(kHelloWorld == kPiece, "");
+  static_assert(kHelloWorld == kPiece.data(), "");
+
   string16 hello_world = kHelloWorld;
   EXPECT_EQ(kHelloWorld, hello_world);
 }
-#endif
 
 // We define a custom operator<< for string16 so we can use it with logging.
 // This tests that conversion.
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index e4c9b00..b85c644 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -1402,36 +1402,44 @@
 
 void TaskQueueImpl::DelayedIncomingQueue::SweepCancelledTasks(
     SequenceManagerImpl* sequence_manager) {
-  // Under the hood a std::priority_queue is a heap and usually it's built on
-  // top of a std::vector. We poke at that vector directly here to filter out
-  // canceled tasks in place.
+  pending_high_res_tasks_ -= queue_.SweepCancelledTasks(sequence_manager);
+}
+
+size_t TaskQueueImpl::DelayedIncomingQueue::PQueue::SweepCancelledTasks(
+    SequenceManagerImpl* sequence_manager) {
+  // Under the hood a std::priority_queue is a heap implemented top of a
+  // std::vector. We poke at that vector directly here to filter out canceled
+  // tasks in place.
   bool task_deleted = false;
-  auto it = queue_.c.begin();
-  while (!queue_.c.empty() && it != queue_.c.end()) {
-    // TODO(crbug.com/1155905): Remove after figuring out the cause of the
-    // crash.
-    sequence_manager->RecordCrashKeys(*it);
-    if (it->task.IsCancelled()) {
-      if (it->is_high_res)
-        pending_high_res_tasks_--;
-      *it = std::move(queue_.c.back());
-      queue_.c.pop_back();
-      task_deleted = true;
-      // Note: if we just removed the last task in the queue, |it| is now
-      // invalid and shouldn't be used.
-    } else {
-      it++;
-    }
-  }
+  size_t num_high_res_tasks_swept = 0u;
+  auto is_cancelled = [sequence_manager, &num_high_res_tasks_swept,
+                       &task_deleted](const Task& task) {
+    // TODO(crbug.com/1155905): Remove after figuring out
+    // the cause of the crash.
+    sequence_manager->RecordCrashKeys(task);
+    if (!task.task.IsCancelled())
+      return false;
+    if (task.is_high_res)
+      num_high_res_tasks_swept++;
+    task_deleted = true;
+    return true;
+  };
+  c.erase(std::remove_if(c.begin(), c.end(), is_cancelled), c.end());
 
   // If we deleted something, re-enforce the heap property.
   if (task_deleted)
-    ranges::make_heap(queue_.c, queue_.comp);
+    ranges::make_heap(c, comp);
+  return num_high_res_tasks_swept;
 }
 
 Value TaskQueueImpl::DelayedIncomingQueue::AsValue(TimeTicks now) const {
+  return queue_.AsValue(now);
+}
+
+Value TaskQueueImpl::DelayedIncomingQueue::PQueue::AsValue(
+    TimeTicks now) const {
   Value state(Value::Type::LIST);
-  for (const Task& task : queue_.c)
+  for (const Task& task : c)
     state.Append(TaskAsValue(task, now));
   return state;
 }
diff --git a/base/task/sequence_manager/task_queue_impl.h b/base/task/sequence_manager/task_queue_impl.h
index 8265ee6..82e5b58 100644
--- a/base/task/sequence_manager/task_queue_impl.h
+++ b/base/task/sequence_manager/task_queue_impl.h
@@ -332,9 +332,14 @@
 
    private:
     struct PQueue : public std::priority_queue<Task> {
-      // Expose the container and comparator.
-      using std::priority_queue<Task>::c;
-      using std::priority_queue<Task>::comp;
+      // Removes all cancelled tasks from the queue. Returns the number of
+      // removed high resolution tasks (which could be lower than the total
+      // number of removed tasks).
+      //
+      // TODO(crbug.com/1155905): we pass SequenceManager to be able to record
+      // crash keys. Remove this parameter after chasing down this crash.
+      size_t SweepCancelledTasks(SequenceManagerImpl* sequence_manager);
+      Value AsValue(TimeTicks now) const;
     };
 
     PQueue queue_;
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index 116eafeff..4926ea1e 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -150,13 +150,14 @@
 class BrowserTestBase;
 class CategorizedWorkerPool;
 class DesktopCaptureDevice;
+class EmergencyTraceFinalisationCoordinator;
 class InProcessUtilityThread;
 class NestedMessagePumpAndroid;
 class NetworkServiceInstancePrivate;
 class PepperPrintSettingsManagerImpl;
+class RTCVideoDecoder;
 class RenderProcessHostImpl;
 class RenderWidgetHostViewMac;
-class RTCVideoDecoder;
 class SandboxHostLinux;
 class ScopedAllowWaitForDebugURL;
 class ServiceWorkerContextClient;
@@ -530,6 +531,7 @@
   friend class cc::TileTaskManagerImpl;
   friend class content::CategorizedWorkerPool;
   friend class content::DesktopCaptureDevice;
+  friend class content::EmergencyTraceFinalisationCoordinator;
   friend class content::InProcessUtilityThread;
   friend class content::RTCVideoDecoder;
   friend class content::SandboxHostLinux;
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 6d69793f..3629cf1 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20210107.2.1
+0.20210108.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 6d69793f..3629cf1 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20210107.2.1
+0.20210108.1.1
diff --git a/chrome/VERSION b/chrome/VERSION
index eaff515..e372072 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=89
 MINOR=0
-BUILD=4382
+BUILD=4383
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index c5fd07d..1337737 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1213,7 +1213,6 @@
   "java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java",
   "java/src/org/chromium/chrome/browser/send_tab_to_self/SendTabToSelfShareActivity.java",
   "java/src/org/chromium/chrome/browser/services/AccountsChangedReceiver.java",
-  "java/src/org/chromium/chrome/browser/services/AndroidChildAccountHelper.java",
   "java/src/org/chromium/chrome/browser/services/gcm/ChromeGcmListenerServiceImpl.java",
   "java/src/org/chromium/chrome/browser/services/gcm/GCMBackgroundServiceImpl.java",
   "java/src/org/chromium/chrome/browser/services/gcm/GCMBackgroundTask.java",
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index d862b63a..3e2c05e 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -44,6 +44,7 @@
   "junit/src/org/chromium/chrome/browser/browserservices/ui/view/DisclosureInfobarTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ui/view/DisclosureNotificationTest.java",
   "junit/src/org/chromium/chrome/browser/browserservices/ui/view/DisclosureSnackbarTest.java",
+  "junit/src/org/chromium/chrome/browser/childaccounts/ChildAccountServiceTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/CompositorSurfaceManagerImplTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/CompositorViewHolderUnitTest.java",
   "junit/src/org/chromium/chrome/browser/compositor/layouts/SceneOverlayTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
index 7aeca2b..83dc4e5 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiController.java
@@ -384,6 +384,20 @@
         return chip;
     }
 
+    /**
+     * Creates a feedback button button. It shows the feedback form and then *directly* executes
+     * {@code actionIndex}.
+     */
+    @CalledByNative
+    private AssistantChip createFeedbackButton(int icon, String text, int actionIndex,
+            boolean disabled, boolean sticky, boolean visible,
+            @Nullable String contentDescription) {
+        AssistantChip chip = AssistantChip.createHairlineAssistantChip(
+                icon, text, disabled, sticky, visible, contentDescription);
+        chip.setSelectedListener(() -> safeNativeOnFeedbackButtonClicked(actionIndex));
+        return chip;
+    }
+
     // TODO(arbesser): Remove this and use methods in {@code AssistantChip} instead.
     @CalledByNative
     private static void appendChipToList(List<AssistantChip> chips, AssistantChip chip) {
@@ -461,6 +475,13 @@
         }
     }
 
+    private void safeNativeOnFeedbackButtonClicked(int index) {
+        if (mNativeUiController != 0) {
+            AutofillAssistantUiControllerJni.get().onFeedbackButtonClicked(
+                    mNativeUiController, AutofillAssistantUiController.this, index);
+        }
+    }
+
     private void safeNativeOnKeyboardVisibilityChanged(boolean visible) {
         if (mNativeUiController != 0) {
             AutofillAssistantUiControllerJni.get().onKeyboardVisibilityChanged(
@@ -503,6 +524,8 @@
                 long nativeUiControllerAndroid, AutofillAssistantUiController caller, int index);
         void onCloseButtonClicked(
                 long nativeUiControllerAndroid, AutofillAssistantUiController caller);
+        void onFeedbackButtonClicked(
+                long nativeUiControllerAndroid, AutofillAssistantUiController caller, int index);
         void onKeyboardVisibilityChanged(long nativeUiControllerAndroid,
                 AutofillAssistantUiController caller, boolean visible);
         void setVisible(long nativeUiControllerAndroid, AutofillAssistantUiController caller,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java b/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java
index 891a578..4a192210 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/childaccounts/ChildAccountService.java
@@ -7,12 +7,14 @@
 import android.accounts.Account;
 import android.app.Activity;
 
-import org.chromium.base.Callback;
+import androidx.annotation.MainThread;
+
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.base.task.PostTask;
 import org.chromium.components.signin.AccountManagerFacade;
+import org.chromium.components.signin.AccountManagerFacade.ChildAccountStatusListener;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.AccountUtils;
 import org.chromium.components.signin.ChildAccountStatus;
@@ -38,19 +40,30 @@
     }
 
     /**
-     * Checks for the presence of child accounts on the device.
+     * Checks the child account status on device.
      *
-     * @param callback A callback which will be called with a @ChildAccountStatus.Status value.
+     * Since child accounts cannot share a device, the listener will be invoked with the status
+     * {@link ChildAccountStatus#NOT_CHILD} if there are no accounts or more than one account on
+     * device. If there is a single account on device, the listener will be passed to
+     * {@link AccountManagerFacade#checkChildAccountStatus} to check the child account status of
+     * the account.
+     *
+     * It should be safe to invoke this method before the native library is initialized (after
+     * AccountManagerFacade is set).
+     *
+     * @param listener The listener is called when the {@link ChildAccountStatus.Status} is ready.
      */
-    public static void checkChildAccountStatus(final Callback<Integer> callback) {
+    @MainThread
+    public static void checkChildAccountStatus(ChildAccountStatusListener listener) {
         ThreadUtils.assertOnUiThread();
-        final AccountManagerFacade accountManager = AccountManagerFacadeProvider.getInstance();
-        accountManager.tryGetGoogleAccounts(accounts -> {
-            if (accounts.size() != 1) {
+        final AccountManagerFacade accountManagerFacade =
+                AccountManagerFacadeProvider.getInstance();
+        accountManagerFacade.tryGetGoogleAccounts(accounts -> {
+            if (accounts.size() == 1) {
                 // Child accounts can't share a device.
-                callback.onResult(ChildAccountStatus.NOT_CHILD);
+                accountManagerFacade.checkChildAccountStatus(accounts.get(0), listener);
             } else {
-                accountManager.checkChildAccountStatus(accounts.get(0), callback);
+                listener.onStatusReady(ChildAccountStatus.NOT_CHILD);
             }
         });
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
index 1114264..da1b36c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunFlowSequencer.java
@@ -22,11 +22,11 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.LaunchIntentDispatcher;
+import org.chromium.chrome.browser.childaccounts.ChildAccountService;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.locale.LocaleManager;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.services.AndroidChildAccountHelper;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.chrome.browser.vr.VrModuleProvider;
@@ -77,15 +77,12 @@
         }
 
         long childAccountStatusStart = SystemClock.elapsedRealtime();
-        new AndroidChildAccountHelper() {
-            @Override
-            public void onParametersReady() {
-                RecordHistogram.recordTimesHistogram("MobileFre.ChildAccountStatusDuration",
-                        SystemClock.elapsedRealtime() - childAccountStatusStart);
-                initializeSharedState(getChildAccountStatus());
-                processFreEnvironmentPreNative();
-            }
-        }.start();
+        ChildAccountService.checkChildAccountStatus(status -> {
+            RecordHistogram.recordTimesHistogram("MobileFre.ChildAccountStatusDuration",
+                    SystemClock.elapsedRealtime() - childAccountStatusStart);
+            initializeSharedState(status);
+            processFreEnvironmentPreNative();
+        });
     }
 
     @VisibleForTesting
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
index 75266016..3e0c60f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ForcedSigninProcessor.java
@@ -10,8 +10,8 @@
 
 import org.chromium.base.Log;
 import org.chromium.chrome.browser.SyncFirstSetupCompleteSource;
+import org.chromium.chrome.browser.childaccounts.ChildAccountService;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.services.AndroidChildAccountHelper;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.services.SigninManager;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
@@ -48,16 +48,13 @@
      * changes with early exit if an account has already been signed in.
      */
     public static void start(@Nullable final Runnable onComplete) {
-        new AndroidChildAccountHelper() {
-            @Override
-            public void onParametersReady() {
-                boolean hasChildAccount = ChildAccountStatus.isChild(getChildAccountStatus());
-                AccountManagementFragment.setSignOutAllowedPreferenceValue(!hasChildAccount);
-                if (hasChildAccount) {
-                    processForcedSignIn(onComplete);
-                }
+        ChildAccountService.checkChildAccountStatus(status -> {
+            boolean hasChildAccount = ChildAccountStatus.isChild(status);
+            AccountManagementFragment.setSignOutAllowedPreferenceValue(!hasChildAccount);
+            if (hasChildAccount) {
+                processForcedSignIn(onComplete);
             }
-        }.start();
+        });
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/services/AndroidChildAccountHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/services/AndroidChildAccountHelper.java
deleted file mode 100644
index 6f6c62a..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/services/AndroidChildAccountHelper.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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.
-
-package org.chromium.chrome.browser.services;
-
-import org.chromium.base.Callback;
-import org.chromium.chrome.browser.childaccounts.ChildAccountService;
-import org.chromium.components.signin.ChildAccountStatus;
-
-/**
- * A helper for child account checks.
- * Usage:
- * new AndroidChildAccountHelper() { override onParametersReady() }.start(appContext).
- */
-public abstract class AndroidChildAccountHelper implements Callback<Integer> {
-    private @ChildAccountStatus.Status Integer mChildAccountStatus;
-
-    /** The callback called when child account parameters are known. */
-    public abstract void onParametersReady();
-
-    /** @return The status of the device regarding child accounts. */
-    protected @ChildAccountStatus.Status int getChildAccountStatus() {
-        return mChildAccountStatus;
-    }
-
-    /**
-     * Starts fetching the child accounts information.
-     * Calls onParametersReady() once the information is fetched.
-     */
-    public void start() {
-        ChildAccountService.checkChildAccountStatus(this);
-    }
-
-    // Callback<Integer>:
-    @Override
-    public void onResult(@ChildAccountStatus.Status Integer status) {
-        mChildAccountStatus = status;
-        if (mChildAccountStatus != null) {
-            onParametersReady();
-        }
-    }
-}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/childaccounts/ChildAccountServiceTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/childaccounts/ChildAccountServiceTest.java
new file mode 100644
index 0000000..18240c5
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/childaccounts/ChildAccountServiceTest.java
@@ -0,0 +1,98 @@
+// Copyright 2021 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.childaccounts;
+
+import static org.mockito.Mockito.verify;
+
+import android.accounts.Account;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.test.util.browser.signin.AccountManagerTestRule;
+import org.chromium.components.signin.AccountManagerFacade.ChildAccountStatusListener;
+import org.chromium.components.signin.AccountUtils;
+import org.chromium.components.signin.ChildAccountStatus;
+import org.chromium.components.signin.test.util.FakeAccountManagerFacade;
+
+/**
+ * Unit tests for {@link ChildAccountService}.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class ChildAccountServiceTest {
+    private static final Account CHILD_ACCOUNT =
+            AccountUtils.createAccountFromName("child.account@gmail.com");
+
+    private final FakeAccountManagerFacade mFakeFacade = new FakeAccountManagerFacade(null) {
+        @Override
+        public void checkChildAccountStatus(Account account, ChildAccountStatusListener listener) {
+            listener.onStatusReady(account.name.startsWith("child")
+                            ? ChildAccountStatus.REGULAR_CHILD
+                            : ChildAccountStatus.NOT_CHILD);
+        }
+    };
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Rule
+    public final AccountManagerTestRule mAccountManagerTestRule =
+            new AccountManagerTestRule(mFakeFacade);
+
+    @Mock
+    private ChildAccountStatusListener mListenerMock;
+
+    @Test
+    public void testChildAccountStatusWhenNoAccountsOnDevice() {
+        ChildAccountService.checkChildAccountStatus(mListenerMock);
+        verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD);
+    }
+
+    @Test
+    public void testChildAccountStatusWhenTwoChildAccountsOnDevice() {
+        // For product reason, child account cannot share device, so as long
+        // as more than one account detected on device, the child account status
+        // on device should be NOT_CHILD.
+        mAccountManagerTestRule.addAccount(CHILD_ACCOUNT);
+        mAccountManagerTestRule.addAccount("child.account2@gmail.com");
+        ChildAccountService.checkChildAccountStatus(mListenerMock);
+        verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD);
+    }
+
+    @Test
+    public void testChildAccountStatusWhenOneChildAndOneAdultAccountsOnDevice() {
+        mAccountManagerTestRule.addAccount(CHILD_ACCOUNT);
+        mAccountManagerTestRule.addAccount("adult.account@gmail.com");
+        ChildAccountService.checkChildAccountStatus(mListenerMock);
+        verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD);
+    }
+
+    @Test
+    public void testChildAccountStatusWhenTwoAdultAccountsOnDevice() {
+        mAccountManagerTestRule.addAccount("adult.account1@gmail.com");
+        mAccountManagerTestRule.addAccount("adult.account2@gmail.com");
+        ChildAccountService.checkChildAccountStatus(mListenerMock);
+        verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD);
+    }
+
+    @Test
+    public void testChildAccountStatusWhenOnlyOneAdultAccountOnDevice() {
+        mAccountManagerTestRule.addAccount("adult.account1@gmail.com");
+        ChildAccountService.checkChildAccountStatus(mListenerMock);
+        verify(mListenerMock).onStatusReady(ChildAccountStatus.NOT_CHILD);
+    }
+
+    @Test
+    public void testChildAccountStatusWhenOnlyOneChildAccountOnDevice() {
+        mAccountManagerTestRule.addAccount(CHILD_ACCOUNT);
+        ChildAccountService.checkChildAccountStatus(mListenerMock);
+        verify(mListenerMock).onStatusReady(ChildAccountStatus.REGULAR_CHILD);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
index 9903ddb..7e71670 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/signin/SigninManagerTest.java
@@ -59,8 +59,9 @@
     @Rule
     public final Features.JUnitProcessor processor = new Features.JUnitProcessor();
 
-    private static final AccountInfo ACCOUNT_INFO = new AccountInfo(
-            new CoreAccountId("gaia-id-user"), "user@domain.com", "gaia-id-user", null);
+    private static final AccountInfo ACCOUNT_INFO =
+            new AccountInfo(new CoreAccountId("gaia-id-user"), "user@domain.com", "gaia-id-user",
+                    "full name", "given name", null);
 
     private final SigninManagerImpl.Natives mNativeMock = mock(SigninManagerImpl.Natives.class);
     private final AccountTrackerService mAccountTrackerService = mock(AccountTrackerService.class);
@@ -335,7 +336,7 @@
     @Test
     public void callbackNotifiedOnSignin() {
         AccountInfo account = new AccountInfo(new CoreAccountId("test_at_gmail.com"),
-                "test@gmail.com", "test_at_gmail.com", null);
+                "test@gmail.com", "test_at_gmail.com", "full name", "given name", null);
 
         // No need to seed accounts to the native code.
         doReturn(true).when(mAccountTrackerService).checkAndSeedSystemAccounts();
@@ -370,7 +371,7 @@
     @Test(expected = AssertionError.class)
     public void failIfAlreadySignedin() {
         AccountInfo account = new AccountInfo(new CoreAccountId("test_at_gmail.com"),
-                "test@gmail.com", "test_at_gmail.com", null);
+                "test@gmail.com", "test_at_gmail.com", "full name", "given name", null);
 
         // No need to seed accounts to the native code.
         doReturn(true).when(mAccountTrackerService).checkAndSeedSystemAccounts();
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 8835c567..e0d2f42 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-89.0.4380.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-89.0.4381.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c50fad4..89e618b5 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1228,6 +1228,7 @@
     "policy/messaging_layer/storage/resources/resource_interface.h",
     "policy/messaging_layer/storage/storage.cc",
     "policy/messaging_layer/storage/storage.h",
+    "policy/messaging_layer/storage/storage_configuration.cc",
     "policy/messaging_layer/storage/storage_configuration.h",
     "policy/messaging_layer/storage/storage_module.cc",
     "policy/messaging_layer/storage/storage_module.h",
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 334bf20c..98f058f 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -751,6 +751,18 @@
                 : nullptr);
         break;
 
+      case FEEDBACK_ACTION:
+        // A "Send feedback" button which will show the feedback form before
+        // executing the action.
+        jchip = Java_AutofillAssistantUiController_createFeedbackButton(
+            env, java_object_, chip.icon,
+            ConvertUTF8ToJavaString(env, chip.text), i, !action.enabled(),
+            chip.sticky, chip.visible,
+            chip.is_content_description_set
+                ? ConvertUTF8ToJavaString(env, chip.content_description)
+                : nullptr);
+        break;
+
       case CANCEL_ACTION:
         // A Cancel button sneaks in an UNDO snackbar before executing the
         // action, while a close button behaves like a normal button.
@@ -856,6 +868,22 @@
   DestroySelf();
 }
 
+void UiControllerAndroid::OnFeedbackButtonClicked(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& jcaller,
+    jint index) {
+  // Show the feedback form then directly run the associated action.
+  // Unfortunately there is no way to associate a callback to run after the user
+  // actually sent (or close) the form, so we have to continue directly after
+  // showing it. It should be good enough, given that in most use cases we will
+  // directly stop.
+  Java_AutofillAssistantUiController_showFeedback(
+      env, java_object_,
+      ConvertUTF8ToJavaString(env, ui_delegate_->GetDebugContext()));
+
+  OnUserActionSelected(env, jcaller, index);
+}
+
 void UiControllerAndroid::OnKeyboardVisibilityChanged(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index ca52983..9f03a66 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -194,6 +194,10 @@
   void OnCloseButtonClicked(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller);
+  void OnFeedbackButtonClicked(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& jcaller,
+      jint actionIndex);
   void OnKeyboardVisibilityChanged(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jcaller,
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc b/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
index 2b2fa51..241d351 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android_utils.cc
@@ -445,6 +445,7 @@
     case NORMAL_ACTION:
     case CANCEL_ACTION:
     case CLOSE_ACTION:
+    case FEEDBACK_ACTION:
       return Java_AssistantChip_createHairlineAssistantChip(
           env, chip.icon(),
           base::android::ConvertUTF8ToJavaString(env, chip.text()),
diff --git a/chrome/browser/android/webapps/add_to_homescreen_mediator.cc b/chrome/browser/android/webapps/add_to_homescreen_mediator.cc
index 84b78ca..7f063e6 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_mediator.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_mediator.cc
@@ -16,11 +16,9 @@
 #include "chrome/browser/banners/app_banner_manager_android.h"
 #include "chrome/browser/banners/app_banner_metrics.h"
 #include "chrome/browser/banners/app_banner_settings_helper.h"
-#include "chrome/browser/feature_engagement/tracker_factory.h"
-#include "components/feature_engagement/public/event_constants.h"
-#include "components/feature_engagement/public/tracker.h"
 #include "components/url_formatter/elide_url.h"
 #include "components/webapps/installable/installable_metrics.h"
+#include "components/webapps/webapps_client.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/gfx/android/java_bitmap.h"
 
@@ -216,11 +214,8 @@
                             entry, AppTypeToMenuEntry::kAppTypeFinalEntry);
 
   if (is_webapk) {
-    DVLOG(2) << "Sending event: IPH used for Installing PWA";
-    feature_engagement::Tracker* tracker =
-        feature_engagement::TrackerFactory::GetForBrowserContext(
-            data_fetcher_->web_contents()->GetBrowserContext());
-    tracker->NotifyEvent(feature_engagement::events::kPwaInstallMenuSelected);
+    webapps::WebappsClient::Get()->OnWebApkInstallInitiatedFromAppMenu(
+        data_fetcher_->web_contents());
   }
 }
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 243572a..b7c489d 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -209,6 +209,8 @@
         <include name="IDR_SYNC_CONFIRMATION_APP_JS" file="${root_gen_dir}\chrome\browser\resources\signin\sync_confirmation\sync_confirmation_app.js" use_base_dir="false" preprocess="true" type="BINDATA" />
         <include name="IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_ILLUSTRATION_SVG" file="resources\signin\sync_confirmation\images\sync_confirmation_illustration.svg" type="BINDATA" />
         <include name="IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_ILLUSTRATION_DARK_SVG" file="resources\signin\sync_confirmation\images\sync_confirmation_illustration_dark.svg" type="BINDATA" />
+        <include name="IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_REFRESHED_ILLUSTRATION_SVG" file="resources\signin\sync_confirmation\images\sync_confirmation_refreshed_illustration.svg" type="BINDATA" />
+        <include name="IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_REFRESHED_ILLUSTRATION_DARK_SVG" file="resources\signin\sync_confirmation\images\sync_confirmation_refreshed_illustration_dark.svg" type="BINDATA" />
       </if>
       <if expr="is_win or is_macosx or desktop_linux">
         <include name="IDR_SIGNIN_DICE_WEB_INTERCEPT_HTML" file="resources\signin\dice_web_signin_intercept\dice_web_signin_intercept.html" type="BINDATA" />
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1ca05604..c4647ab 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -408,6 +408,7 @@
 #include "ash/public/cpp/tablet_mode.h"
 #include "chrome/app/chrome_crash_reporter_client.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_content_file_system_backend_delegate.h"
 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_backend_delegate.h"
 #include "chrome/browser/chromeos/chrome_browser_main_chromeos.h"
@@ -425,6 +426,7 @@
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
 #include "chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h"
+#include "chrome/browser/chromeos/policy/system_proxy_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/smb_client/fileapi/smbfs_file_system_backend_delegate.h"
 #include "chrome/browser/chromeos/system/input_device_settings.h"
@@ -5095,6 +5097,21 @@
     scoped_refptr<net::HttpResponseHeaders> response_headers,
     bool first_auth_attempt,
     LoginAuthRequiredCallback auth_required_callback) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  policy::SystemProxyManager* system_proxy_manager =
+      g_browser_process->platform_part()
+          ->browser_policy_connector_chromeos()
+          ->GetSystemProxyManager();
+  // For Managed Guest Session and Kiosk devices, the credentials configured
+  // via the policy SystemProxySettings may be used for proxy authentication.
+  // Note: |system_proxy_manager| may be missing in tests.
+  if (system_proxy_manager && system_proxy_manager->CanUsePolicyCredentials(
+                                  auth_info, first_auth_attempt)) {
+    return system_proxy_manager->CreateLoginDelegate(
+        std::move(auth_required_callback));
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
   // For subresources, create a LoginHandler directly, which may show a login
   // prompt to the user. Main frame resources go through LoginTabHelper, which
   // manages a more complicated flow to avoid confusion about which website is
diff --git a/chrome/browser/chromeos/crosapi/account_manager_ash.cc b/chrome/browser/chromeos/crosapi/account_manager_ash.cc
index 65665ee..63c924c 100644
--- a/chrome/browser/chromeos/crosapi/account_manager_ash.cc
+++ b/chrome/browser/chromeos/crosapi/account_manager_ash.cc
@@ -13,10 +13,8 @@
 
 namespace crosapi {
 
-AccountManagerAsh::AccountManagerAsh(
-    chromeos::AccountManager* account_manager,
-    mojo::PendingReceiver<mojom::AccountManager> receiver)
-    : account_manager_(account_manager), receiver_(this, std::move(receiver)) {
+AccountManagerAsh::AccountManagerAsh(chromeos::AccountManager* account_manager)
+    : account_manager_(account_manager) {
   DCHECK(account_manager_);
   account_manager_->AddObserver(this);
 }
@@ -25,6 +23,11 @@
   account_manager_->RemoveObserver(this);
 }
 
+void AccountManagerAsh::BindReceiver(
+    mojo::PendingReceiver<mojom::AccountManager> receiver) {
+  receivers_.Add(this, std::move(receiver));
+}
+
 void AccountManagerAsh::IsInitialized(IsInitializedCallback callback) {
   std::move(callback).Run(account_manager_->IsInitialized());
 }
diff --git a/chrome/browser/chromeos/crosapi/account_manager_ash.h b/chrome/browser/chromeos/crosapi/account_manager_ash.h
index 6e79a8e..73529ea 100644
--- a/chrome/browser/chromeos/crosapi/account_manager_ash.h
+++ b/chrome/browser/chromeos/crosapi/account_manager_ash.h
@@ -10,7 +10,7 @@
 #include "chromeos/crosapi/mojom/account_manager.mojom.h"
 #include "components/account_manager_core/account.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
 
 namespace crosapi {
@@ -21,12 +21,13 @@
 class AccountManagerAsh : public mojom::AccountManager,
                           public chromeos::AccountManager::Observer {
  public:
-  AccountManagerAsh(chromeos::AccountManager* account_manager,
-                    mojo::PendingReceiver<mojom::AccountManager> receiver);
+  explicit AccountManagerAsh(chromeos::AccountManager* account_manager);
   AccountManagerAsh(const AccountManagerAsh&) = delete;
   AccountManagerAsh& operator=(const AccountManagerAsh&) = delete;
   ~AccountManagerAsh() override;
 
+  void BindReceiver(mojo::PendingReceiver<mojom::AccountManager> receiver);
+
   // crosapi::mojom::AccountManager:
   void IsInitialized(IsInitializedCallback callback) override;
   void AddObserver(AddObserverCallback callback) override;
@@ -42,7 +43,7 @@
   void FlushMojoForTesting();
 
   chromeos::AccountManager* const account_manager_;
-  mojo::Receiver<mojom::AccountManager> receiver_;
+  mojo::ReceiverSet<mojom::AccountManager> receivers_;
   mojo::RemoteSet<mojom::AccountManagerObserver> observers_;
 };
 
diff --git a/chrome/browser/chromeos/crosapi/account_manager_ash_unittest.cc b/chrome/browser/chromeos/crosapi/account_manager_ash_unittest.cc
index 0e2bf03..d4f6b433 100644
--- a/chrome/browser/chromeos/crosapi/account_manager_ash_unittest.cc
+++ b/chrome/browser/chromeos/crosapi/account_manager_ash_unittest.cc
@@ -93,8 +93,9 @@
 
  protected:
   void SetUp() override {
-    account_manager_ash_ = std::make_unique<AccountManagerAsh>(
-        &account_manager_, remote_.BindNewPipeAndPassReceiver());
+    account_manager_ash_ =
+        std::make_unique<AccountManagerAsh>(&account_manager_);
+    account_manager_ash_->BindReceiver(remote_.BindNewPipeAndPassReceiver());
     account_manager_async_waiter_ =
         std::make_unique<mojom::AccountManagerAsyncWaiter>(
             account_manager_ash_.get());
diff --git a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
index 41f78ad..9bdadfc2 100644
--- a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
+++ b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
@@ -62,35 +62,38 @@
 
 void AshChromeServiceImpl::BindAccountManager(
     mojo::PendingReceiver<mojom::AccountManager> receiver) {
-  DVLOG(1) << "Binding AccountManager receiver";
-  // Assumptions:
-  // 1. TODO(https://crbug.com/1102768): Multi-Signin / Fast-User-Switching is
-  // disabled.
-  // 2. ash-chrome has 1 and only 1 "regular" |Profile|.
+  // TODO(https://crrev.com/c/2601750): Move AccountManagerAsh ownership to
+  // chromeos::AccountManager.
+  if (!account_manager_ash_) {
+    // Assumptions:
+    // 1. TODO(https://crbug.com/1102768): Multi-Signin / Fast-User-Switching is
+    // disabled.
+    // 2. ash-chrome has 1 and only 1 "regular" |Profile|.
 #if DCHECK_IS_ON()
-  int num_regular_profiles = 0;
-  for (const Profile* profile :
-       g_browser_process->profile_manager()->GetLoadedProfiles()) {
-    if (chromeos::ProfileHelper::IsRegularProfile(profile))
-      num_regular_profiles++;
-  }
-  DCHECK_EQ(1, num_regular_profiles);
+    int num_regular_profiles = 0;
+    for (const Profile* profile :
+         g_browser_process->profile_manager()->GetLoadedProfiles()) {
+      if (chromeos::ProfileHelper::IsRegularProfile(profile))
+        num_regular_profiles++;
+    }
+    DCHECK_EQ(1, num_regular_profiles);
 #endif  // DCHECK_IS_ON()
-  // Given these assumptions, there is 1 and only 1 Account Manager that
-  // can/should be contacted - the one attached to the regular |Profile| in
-  // ash-chrome, for the current |User|.
-  const user_manager::User* const user =
-      user_manager::UserManager::Get()->GetActiveUser();
-  const Profile* const profile =
-      chromeos::ProfileHelper::Get()->GetProfileByUser(user);
-  chromeos::AccountManager* const account_manager =
-      g_browser_process->platform_part()
-          ->GetAccountManagerFactory()
-          ->GetAccountManager(/* profile_path = */ profile->GetPath().value());
-  // TODO(https://crbug.com/1148448): Convert this to allow multiple,
-  // simultaneous crosapi clients. See BindScreenManager for an example.
-  account_manager_ash_ = std::make_unique<crosapi::AccountManagerAsh>(
-      account_manager, std::move(receiver));
+    // Given these assumptions, there is 1 and only 1 Account Manager that
+    // can/should be contacted - the one attached to the regular |Profile| in
+    // ash-chrome, for the current |User|.
+    const user_manager::User* const user =
+        user_manager::UserManager::Get()->GetActiveUser();
+    const Profile* const profile =
+        chromeos::ProfileHelper::Get()->GetProfileByUser(user);
+    chromeos::AccountManager* const account_manager =
+        g_browser_process->platform_part()
+            ->GetAccountManagerFactory()
+            ->GetAccountManager(
+                /* profile_path = */ profile->GetPath().value());
+    account_manager_ash_ =
+        std::make_unique<crosapi::AccountManagerAsh>(account_manager);
+  }
+  account_manager_ash_->BindReceiver(std::move(receiver));
 }
 
 void AshChromeServiceImpl::BindFileManager(
diff --git a/chrome/browser/chromeos/input_method/autocorrect_manager.cc b/chrome/browser/chromeos/input_method/autocorrect_manager.cc
index 2bf4d24..e3902b0 100644
--- a/chrome/browser/chromeos/input_method/autocorrect_manager.cc
+++ b/chrome/browser/chromeos/input_method/autocorrect_manager.cc
@@ -7,11 +7,14 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
 #include "chrome/browser/chromeos/input_method/assistive_window_properties.h"
 #include "chrome/browser/chromeos/input_method/suggestion_enums.h"
 #include "chrome/grit/generated_resources.h"
+#include "ui/base/ime/chromeos/extension_ime_util.h"
 #include "ui/base/ime/chromeos/ime_bridge.h"
 #include "ui/base/ime/chromeos/ime_input_context_handler_interface.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/keycodes/dom/dom_code.h"
 
@@ -28,9 +31,31 @@
   kMaxValue = kReverted,
 };
 
+bool IsCurrentInputMethodExperimentalMultilingual() {
+  auto* input_method_manager = input_method::InputMethodManager::Get();
+  if (!input_method_manager) {
+    return false;
+  }
+  return extension_ime_util::IsExperimentalMultilingual(
+      input_method_manager->GetActiveIMEState()->GetCurrentInputMethod().id());
+}
+
 void LogAssistiveAutocorrectAction(AutocorrectActions action) {
   base::UmaHistogramEnumeration("InputMethod.Assistive.Autocorrect.Actions",
                                 action);
+  if (IsCurrentInputMethodExperimentalMultilingual()) {
+    base::UmaHistogramEnumeration(
+        "InputMethod.MultilingualExperiment.Autocorrect.Actions", action);
+  }
+}
+
+void LogAssistiveAutocorrectDelay(base::TimeDelta delay) {
+  base::UmaHistogramMediumTimes("InputMethod.Assistive.Autocorrect.Delay",
+                                delay);
+  if (IsCurrentInputMethodExperimentalMultilingual()) {
+    base::UmaHistogramMediumTimes(
+        "InputMethod.MultilingualExperiment.Autocorrect.Delay", delay);
+  }
 }
 
 void RecordAssistiveCoverage(AssistiveType type) {
@@ -193,8 +218,7 @@
   LogAssistiveAutocorrectAction(AutocorrectActions::kReverted);
   RecordAssistiveCoverage(AssistiveType::kAutocorrectReverted);
   RecordAssistiveSuccess(AssistiveType::kAutocorrectReverted);
-  base::UmaHistogramMediumTimes("InputMethod.Assistive.Autocorrect.Delay",
-                                (base::TimeTicks::Now() - autocorrect_time_));
+  LogAssistiveAutocorrectDelay(base::TimeTicks::Now() - autocorrect_time_);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/net/network_health/network_health_unittest.cc b/chrome/browser/chromeos/net/network_health/network_health_unittest.cc
index 4f6b281..80292df 100644
--- a/chrome/browser/chromeos/net/network_health/network_health_unittest.cc
+++ b/chrome/browser/chromeos/net/network_health/network_health_unittest.cc
@@ -60,20 +60,14 @@
     const auto& initial_network_health_state =
         network_health_.GetNetworkHealthState();
 
-    ASSERT_EQ(std::size_t(2), initial_network_health_state->networks.size());
-
-    // Check that VPN device state is always reported even if no VPNs exist.
-    ASSERT_EQ(network_config::mojom::NetworkType::kVPN,
-              initial_network_health_state->networks[0]->type);
-    ASSERT_EQ(network_health::mojom::NetworkState::kNotConnected,
-              initial_network_health_state->networks[0]->state);
+    ASSERT_EQ(std::size_t(1), initial_network_health_state->networks.size());
 
     // Check that the default wifi device created by CrosNetworkConfigTestHelper
     // exists.
     ASSERT_EQ(network_config::mojom::NetworkType::kWiFi,
-              initial_network_health_state->networks[1]->type);
+              initial_network_health_state->networks[0]->type);
     ASSERT_EQ(network_health::mojom::NetworkState::kNotConnected,
-              initial_network_health_state->networks[1]->state);
+              initial_network_health_state->networks[0]->state);
   }
 
   mojom::NetworkPtr GetNetworkHealthStateByType(
diff --git a/chrome/browser/chromeos/policy/system_proxy_manager.cc b/chrome/browser/chromeos/policy/system_proxy_manager.cc
index d9c290a..6b73c06b 100644
--- a/chrome/browser/chromeos/policy/system_proxy_manager.cc
+++ b/chrome/browser/chromeos/policy/system_proxy_manager.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/chromeos/policy/system_proxy_manager.h"
 
 #include "base/bind.h"
+#include "base/containers/contains.h"
+#include "base/memory/weak_ptr.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
@@ -17,8 +19,10 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/login/login_handler.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/system_proxy/system_proxy_client.h"
+#include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/network_event_log.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
@@ -46,6 +50,40 @@
 
 namespace {
 const char kSystemProxyService[] = "system-proxy-service";
+
+// A `content::LoginDelegate` implementation that returns to the caller the
+// proxy credentials set by the policy `SystemProxySettings`.
+class SystemProxyLoginHandler : public content::LoginDelegate {
+ public:
+  SystemProxyLoginHandler() = default;
+  ~SystemProxyLoginHandler() override = default;
+
+  SystemProxyLoginHandler(const SystemProxyLoginHandler&) = delete;
+  SystemProxyLoginHandler& operator=(const SystemProxyLoginHandler&) = delete;
+
+  void AuthenticateWithCredentials(
+      const std::string& username,
+      const std::string& password,
+      LoginAuthRequiredCallback auth_required_callback) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE,
+        base::BindOnce(&SystemProxyLoginHandler::InvokeWithCredentials,
+                       weak_factory_.GetWeakPtr(), username, password,
+                       std::move(auth_required_callback)));
+  }
+
+ private:
+  void InvokeWithCredentials(const std::string& username,
+                             const std::string& password,
+                             LoginAuthRequiredCallback auth_required_callback) {
+    std::move(auth_required_callback)
+        .Run(base::make_optional<net::AuthCredentials>(
+            base::UTF8ToUTF16(username), base::UTF8ToUTF16(password)));
+  }
+
+  base::WeakPtrFactory<SystemProxyLoginHandler> weak_factory_{this};
+};
+
 }  // namespace
 
 namespace policy {
@@ -380,6 +418,47 @@
                                /*default_value=*/std::string());
 }
 
+bool SystemProxyManager::CanUsePolicyCredentials(
+    const net::AuthChallengeInfo& auth_info,
+    bool first_auth_attempt) {
+  if (!auth_info.is_proxy || !first_auth_attempt)
+    return false;
+
+  if (!chromeos::LoginState::IsInitialized() ||
+      (!chromeos::LoginState::Get()->IsPublicSessionUser() &&
+       !chromeos::LoginState::Get()->IsKioskApp())) {
+    VLOG(1) << "Only kiosk app and MGS can reuse the policy provided proxy "
+               "credentials for authentication";
+    return false;
+  }
+
+  if (!system_proxy_enabled_ || system_services_username_.empty() ||
+      system_services_password_.empty()) {
+    return false;
+  }
+
+  if (!IsManagedProxyConfigured()) {
+    return false;
+  }
+  if (!policy_credentials_auth_schemes_.empty()) {
+    if (!base::Contains(policy_credentials_auth_schemes_, auth_info.scheme)) {
+      VLOG(1) << "Auth scheme not allowed by policy";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+std::unique_ptr<content::LoginDelegate> SystemProxyManager::CreateLoginDelegate(
+    LoginAuthRequiredCallback auth_required_callback) {
+  auto login_delegate = std::make_unique<SystemProxyLoginHandler>();
+  login_delegate->AuthenticateWithCredentials(
+      system_services_username_, system_services_password_,
+      std::move(auth_required_callback));
+  return std::move(login_delegate);
+}
+
 void SystemProxyManager::OnSetAuthenticationDetails(
     const system_proxy::SetAuthenticationDetailsResponse& response) {
   if (response.has_error_message()) {
diff --git a/chrome/browser/chromeos/policy/system_proxy_manager.h b/chrome/browser/chromeos/policy/system_proxy_manager.h
index dbde688..2d131c27 100644
--- a/chrome/browser/chromeos/policy/system_proxy_manager.h
+++ b/chrome/browser/chromeos/policy/system_proxy_manager.h
@@ -17,6 +17,8 @@
 #include "chrome/browser/extensions/api/settings_private/prefs_util.h"
 #include "chromeos/dbus/system_proxy/system_proxy_service.pb.h"
 #include "chromeos/network/network_state_handler_observer.h"
+#include "components/user_manager/user_manager.h"
+#include "content/public/browser/content_browser_client.h"
 #include "net/base/auth.h"
 
 namespace chromeos {
@@ -25,6 +27,10 @@
 class SystemProxyNotification;
 }  // namespace chromeos
 
+namespace content {
+class LoginDelegate;
+}
+
 namespace system_proxy {
 class SetAuthenticationDetailsResponse;
 class ShutDownResponse;
@@ -81,6 +87,23 @@
   // Registers prefs stored in user profiles.
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
+  // Indicates whether the credentials set via the device policy
+  // SystemProxySettings can be used for proxy authentication in Chrome. The
+  // following conditions must be true:
+  // - the current session must be Managed Guest Session (MGS) or Kiosk app;
+  // - the proxy is set via policy;
+  // - System-proxy is enabled and credentials are set via policy;
+  // - `first_auth_attempt` is true;
+  // - `auth_info.scheme` must be allowed by the SystemProxySettings policy.
+  bool CanUsePolicyCredentials(const net::AuthChallengeInfo& auth_info,
+                               bool first_auth_attempt);
+
+  // Returns a login delegate that posts `auth_required_callback` with the
+  // credentials provided by the policy SystemProxySettings. Callers must verify
+  // that `CanUsePolicyCredentials` is true before calling this method.
+  std::unique_ptr<content::LoginDelegate> CreateLoginDelegate(
+      LoginAuthRequiredCallback auth_required_callback);
+
  private:
   // NetworkStateHandlerObserver implementation
   void DefaultNetworkChanged(const chromeos::NetworkState* network) override;
diff --git a/chrome/browser/chromeos/policy/system_proxy_manager_browsertest.cc b/chrome/browser/chromeos/policy/system_proxy_manager_browsertest.cc
index 45bbc133..06a07168 100644
--- a/chrome/browser/chromeos/policy/system_proxy_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/system_proxy_manager_browsertest.cc
@@ -17,7 +17,10 @@
 #include "chrome/browser/chromeos/policy/system_proxy_manager.h"
 #include "chrome/browser/chromeos/ui/request_system_proxy_credentials_view.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
+#include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_test_utils.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/login/login_handler.h"
+#include "chrome/browser/ui/login/login_handler_test_utils.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/testing_browser_process.h"
@@ -29,6 +32,7 @@
 #include "chromeos/dbus/shill/shill_service_client.h"
 #include "chromeos/dbus/system_proxy/system_proxy_client.h"
 #include "chromeos/dbus/system_proxy/system_proxy_service.pb.h"
+#include "chromeos/login/login_state/login_state.h"
 #include "chromeos/network/network_handler.h"
 #include "chromeos/network/network_state.h"
 #include "chromeos/network/network_state_handler.h"
@@ -46,8 +50,16 @@
 #include "components/proxy_config/proxy_prefs.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
+#include "net/base/proxy_server.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/http/http_auth_cache.h"
+#include "net/test/embedded_test_server/default_handlers.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/spawned_test_server/spawned_test_server.h"
+#include "net/url_request/url_request_context.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/textfield/textfield.h"
 
@@ -618,4 +630,167 @@
   ExpectSystemCredentialsSent(kUsername, kPassword, {"ntlm"});
 }
 
+namespace {
+constexpr char kProxyUsername[] = "foo";
+constexpr char kProxyPassword[] = "bar";
+constexpr char kBadUsername[] = "bad-username";
+constexpr char kBadPassword[] = "bad-pwd";
+constexpr char kOriginHostname[] = "a.test";
+}  // namespace
+
+class SystemProxyCredentialsReuseBrowserTest
+    : public SystemProxyManagerPolicyCredentialsBrowserTest {
+ public:
+  SystemProxyCredentialsReuseBrowserTest()
+      : proxy_server_(std::make_unique<net::SpawnedTestServer>(
+            net::SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
+            base::FilePath())) {}
+  SystemProxyCredentialsReuseBrowserTest(
+      const SystemProxyCredentialsReuseBrowserTest&) = delete;
+  SystemProxyCredentialsReuseBrowserTest& operator=(
+      const SystemProxyCredentialsReuseBrowserTest&) = delete;
+  ~SystemProxyCredentialsReuseBrowserTest() override = default;
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    host_resolver()->AddRule(kOriginHostname, "127.0.0.1");
+    proxy_server_->set_redirect_connect_to_localhost(true);
+    ASSERT_TRUE(proxy_server_->Start());
+
+    https_server_ = std::make_unique<net::EmbeddedTestServer>(
+        net::EmbeddedTestServer::TYPE_HTTPS);
+    https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
+    https_server_->ServeFilesFromSourceDirectory("chrome/test/data");
+    net::test_server::RegisterDefaultHandlers(https_server_.get());
+    ASSERT_TRUE(https_server_->Start());
+  }
+
+ protected:
+  content::WebContents* GetWebContents() const {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  void SetManagedProxy() {
+    // Configure a proxy via user policy.
+    base::Value proxy_config(base::Value::Type::DICTIONARY);
+    proxy_config.SetKey("mode",
+                        base::Value(ProxyPrefs::kFixedServersProxyModeName));
+    proxy_config.SetKey(
+        "server", base::Value(proxy_server_->host_port_pair().ToString()));
+    browser()->profile()->GetPrefs()->Set(proxy_config::prefs::kProxy,
+                                          proxy_config);
+    RunUntilIdle();
+  }
+
+  GURL GetServerUrl(const std::string& page) {
+    return https_server_->GetURL(kOriginHostname, page);
+  }
+
+  // Navigates to the test page "/simple.html" and authenticates in the proxy
+  // login dialog with `username` and `password`.
+  void LoginWithDialog(const std::string& username,
+                       const std::string& password) {
+    LoginPromptBrowserTestObserver login_observer;
+    login_observer.Register(content::Source<content::NavigationController>(
+        &GetWebContents()->GetController()));
+    WindowedAuthNeededObserver auth_needed(&GetWebContents()->GetController());
+    ui_test_utils::NavigateToURL(browser(), GetServerUrl("/simple.html"));
+    auth_needed.Wait();
+    WindowedAuthSuppliedObserver auth_supplied(
+        &GetWebContents()->GetController());
+    LoginHandler* login_handler = login_observer.handlers().front();
+    login_handler->SetAuth(base::ASCIIToUTF16(username),
+                           base::ASCIIToUTF16(password));
+    auth_supplied.Wait();
+    EXPECT_EQ(1, login_observer.auth_supplied_count());
+  }
+
+  void CheckEntryInHttpAuthCache(const std::string& auth_scheme,
+                                 const std::string& expected_username,
+                                 const std::string& expected_password) {
+    network::mojom::NetworkContext* network_context =
+        content::BrowserContext::GetDefaultStoragePartition(
+            browser()->profile())
+            ->GetNetworkContext();
+    std::string username;
+    std::string password;
+    base::RunLoop loop;
+    network_context->LookupProxyAuthCredentials(
+        net::ProxyServer(net::ProxyServer::SCHEME_HTTP,
+                         proxy_server_->host_port_pair()),
+        auth_scheme, "MyRealm1",
+        base::BindOnce(
+            [](std::string* username, std::string* password,
+               base::OnceClosure closure,
+               const base::Optional<net::AuthCredentials>& credentials) {
+              if (credentials) {
+                *username = base::UTF16ToUTF8(credentials->username());
+                *password = base::UTF16ToUTF8(credentials->password());
+              }
+              std::move(closure).Run();
+            },
+            &username, &password, loop.QuitClosure()));
+    loop.Run();
+    EXPECT_EQ(username, expected_username);
+    EXPECT_EQ(password, expected_password);
+  }
+
+  SystemProxyManager* GetSystemProxyManager() {
+    return g_browser_process->platform_part()
+        ->browser_policy_connector_chromeos()
+        ->GetSystemProxyManager();
+  }
+
+  std::unique_ptr<net::EmbeddedTestServer> https_server_;
+  // A proxy server which requires authentication using the 'Basic'
+  // authentication method.
+  std::unique_ptr<net::SpawnedTestServer> proxy_server_;
+};
+
+// Verifies that the policy provided credentials are not used for regular users.
+IN_PROC_BROWSER_TEST_F(SystemProxyCredentialsReuseBrowserTest, RegularUser) {
+  SetManagedProxy();
+  SetPolicyCredentials(kProxyUsername, kProxyPassword);
+  LoginWithDialog(kProxyUsername, kProxyPassword);
+  CheckEntryInHttpAuthCache("Basic", kProxyUsername, kProxyPassword);
+}
+
+// Verifies that the policy provided credentials are used for MGS.
+IN_PROC_BROWSER_TEST_F(SystemProxyCredentialsReuseBrowserTest,
+                       PolicyCredentialsUsed) {
+  SetManagedProxy();
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+  SetPolicyCredentials(kProxyUsername, kProxyPassword);
+  ui_test_utils::NavigateToURL(browser(), GetServerUrl("/simple.html"));
+  CheckEntryInHttpAuthCache("Basic", kProxyUsername, kProxyPassword);
+}
+
+// Verifies that if the policy provided proxy credentials are not correct in a
+// MGS, then the user is prompted for credentials.
+IN_PROC_BROWSER_TEST_F(SystemProxyCredentialsReuseBrowserTest,
+                       BadPolicyCredentials) {
+  SetManagedProxy();
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+  SetPolicyCredentials(kBadUsername, kBadPassword);
+  LoginWithDialog(kProxyUsername, kProxyPassword);
+  CheckEntryInHttpAuthCache("Basic", kProxyUsername, kProxyPassword);
+}
+
+// Verifies that the policy provided proxy credentials are only used for
+// authentication schemes allowed by the SystemProxySettings policy.
+IN_PROC_BROWSER_TEST_F(SystemProxyCredentialsReuseBrowserTest,
+                       RestrictedPolicyCredentials) {
+  SetManagedProxy();
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+  SetPolicyCredentials(kProxyUsername, kProxyPassword, R"("ntlm","digest")");
+  LoginWithDialog(kProxyUsername, kProxyPassword);
+  CheckEntryInHttpAuthCache("Basic", kProxyUsername, kProxyPassword);
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/system_proxy_manager_unittest.cc b/chrome/browser/chromeos/policy/system_proxy_manager_unittest.cc
index 741002e3..8080a24 100644
--- a/chrome/browser/chromeos/policy/system_proxy_manager_unittest.cc
+++ b/chrome/browser/chromeos/policy/system_proxy_manager_unittest.cc
@@ -20,6 +20,8 @@
 #include "chromeos/network/network_handler.h"
 #include "components/arc/arc_prefs.h"
 #include "components/prefs/pref_service.h"
+#include "components/proxy_config/proxy_config_pref_names.h"
+#include "components/proxy_config/proxy_prefs.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
 #include "content/public/test/browser_task_environment.h"
@@ -43,6 +45,8 @@
 namespace {
 constexpr char kBrowserUsername[] = "browser_username";
 constexpr char kBrowserPassword[] = "browser_password";
+constexpr char kPolicyUsername[] = "policy_username";
+constexpr char kPolicyPassword[] = "policy_password";
 constexpr char kKerberosActivePrincipalName[] = "kerberos_princ_name";
 constexpr char kProxyAuthUrl[] = "http://example.com:3128";
 constexpr char kProxyAuthEmptyPath[] = "http://example.com:3128/";
@@ -73,6 +77,22 @@
   return network::NetworkService::GetNetworkServiceForTesting();
 }
 
+void SetManagedProxy(Profile* profile) {
+  // Configure a proxy via user policy.
+  base::Value proxy_config(base::Value::Type::DICTIONARY);
+  proxy_config.SetKey("mode",
+                      base::Value(ProxyPrefs::kFixedServersProxyModeName));
+  proxy_config.SetKey("server", base::Value(kProxyAuthUrl));
+  profile->GetPrefs()->Set(proxy_config::prefs::kProxy, proxy_config);
+}
+
+net::AuthChallengeInfo GetAuthInfo() {
+  net::AuthChallengeInfo auth_info;
+  auth_info.is_proxy = true;
+  auth_info.scheme = kScheme;
+  return auth_info;
+}
+
 }  // namespace
 
 namespace policy {
@@ -88,6 +108,7 @@
     testing::Test::SetUp();
     chromeos::shill_clients::InitializeFakes();
     chromeos::NetworkHandler::Initialize();
+    chromeos::LoginState::Initialize();
 
     profile_ = std::make_unique<TestingProfile>();
     chromeos::SystemProxyClient::InitializeFake();
@@ -95,11 +116,14 @@
         chromeos::CrosSettings::Get(), local_state_.Get());
     // Listen for pref changes for the primary profile.
     system_proxy_manager_->StartObservingPrimaryProfilePrefs(profile_.get());
+    chromeos::NetworkHandler::Get()->InitializePrefServices(
+        profile_->GetPrefs(), local_state_.Get());
   }
 
   void TearDown() override {
     system_proxy_manager_->StopObservingPrimaryProfilePrefs();
     system_proxy_manager_.reset();
+    chromeos::LoginState::Shutdown();
     chromeos::SystemProxyClient::Shutdown();
     chromeos::NetworkHandler::Shutdown();
     chromeos::shill_clients::Shutdown();
@@ -334,4 +358,98 @@
                   .empty());
 }
 
+// Verifies that only MGS and Kiosk can use the policy provided credentials.
+TEST_F(SystemProxyManagerTest, CanUsePolicyCredentialsUserType) {
+  SetPolicy(/*system_proxy_enabled=*/true,
+            /*system_services_username=*/kPolicyUsername,
+            /*system_services_password=*/kPolicyPassword);
+  SetManagedProxy(profile_.get());
+
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+
+  EXPECT_TRUE(system_proxy_manager_->CanUsePolicyCredentials(
+      GetAuthInfo(), /*first_auth_attempt=*/true));
+
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_KIOSK_APP);
+
+  EXPECT_TRUE(system_proxy_manager_->CanUsePolicyCredentials(
+      GetAuthInfo(), /*first_auth_attempt=*/true));
+
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_REGULAR);
+
+  EXPECT_FALSE(system_proxy_manager_->CanUsePolicyCredentials(
+      GetAuthInfo(), /*first_auth_attempt=*/true));
+}
+
+// Verifies that the policy provided credentials are only used for proxy auth.
+TEST_F(SystemProxyManagerTest, CanUsePolicyCredentialsOriginServer) {
+  SetPolicy(/*system_proxy_enabled=*/true,
+            /*system_services_username=*/kPolicyUsername,
+            /*system_services_password=*/kPolicyPassword);
+  SetManagedProxy(profile_.get());
+
+  net::AuthChallengeInfo auth_info = GetAuthInfo();
+  auth_info.is_proxy = false;
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+
+  EXPECT_FALSE(system_proxy_manager_->CanUsePolicyCredentials(
+      auth_info, /*first_auth_attempt=*/true));
+}
+
+// Verifies that the policy provided credentials are only used for managed
+// proxies.
+TEST_F(SystemProxyManagerTest, CanUsePolicyCredentialsNoManagedProxy) {
+  SetPolicy(/*system_proxy_enabled=*/true,
+            /*system_services_username=*/kPolicyUsername,
+            /*system_services_password=*/kPolicyPassword);
+
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+
+  EXPECT_FALSE(system_proxy_manager_->CanUsePolicyCredentials(
+      GetAuthInfo(), /*first_auth_attempt=*/true));
+}
+
+// Verifies that `CanUsePolicyCredentials` returns false if no credentials are
+// specified by policy.
+TEST_F(SystemProxyManagerTest, NoPolicyCredentials) {
+  SetPolicy(/*system_proxy_enabled=*/true,
+            /*system_services_username=*/"",
+            /*system_services_password=*/"");
+  SetManagedProxy(profile_.get());
+
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+
+  EXPECT_FALSE(system_proxy_manager_->CanUsePolicyCredentials(
+      GetAuthInfo(), /*first_auth_attempt=*/true));
+}
+
+// Verifies that `CanUsePolicyCredentials` is only returning true for the first
+// auth attempt.
+TEST_F(SystemProxyManagerTest, CanUsePolicyCredentialsMgsMaxTries) {
+  SetPolicy(/*system_proxy_enabled=*/true,
+            /*system_services_username=*/kPolicyUsername,
+            /*system_services_password=*/kPolicyPassword);
+  SetManagedProxy(profile_.get());
+
+  chromeos::LoginState::Get()->SetLoggedInState(
+      chromeos::LoginState::LOGGED_IN_ACTIVE,
+      chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT_MANAGED);
+  EXPECT_TRUE(system_proxy_manager_->CanUsePolicyCredentials(
+      GetAuthInfo(), /*first_auth_attempt=*/true));
+  EXPECT_FALSE(system_proxy_manager_->CanUsePolicyCredentials(
+      GetAuthInfo(), /*first_auth_attempt=*/false));
+}
+
 }  // namespace policy
diff --git a/chrome/browser/content_settings/content_settings_browsertest.cc b/chrome/browser/content_settings/content_settings_browsertest.cc
index f1dd0a6c..cd1fa520 100644
--- a/chrome/browser/content_settings/content_settings_browsertest.cc
+++ b/chrome/browser/content_settings/content_settings_browsertest.cc
@@ -186,12 +186,6 @@
       set_secure_scheme();
   }
 
-  void SetUpCommandLine(base::CommandLine* cmd) override {
-    // Get access to CookieStore API.
-    cmd->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures);
-    ContentSettingsTest::SetUpCommandLine(cmd);
-  }
-
   void set_secure_scheme() { secure_scheme_ = true; }
 
   std::string ReadCookie(Browser* browser) {
@@ -963,13 +957,6 @@
   ContentSettingsWorkerModulesBrowserTest() = default;
   ~ContentSettingsWorkerModulesBrowserTest() override = default;
 
-  void SetUpCommandLine(base::CommandLine* cmd) override {
-    // Module scripts on Dedicated Worker is still an experimental feature.
-    // Likewise for CookieStore.
-    // TODO(crbug/680046,crbug/729800): Remove this after shipping.
-    cmd->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures);
-  }
-
  protected:
   void RegisterStaticFile(net::EmbeddedTestServer* server,
                           const std::string& relative_url,
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_notification_unittest.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_notification_unittest.cc
index fe22c062..ae1b741 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_notification_unittest.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_notification_unittest.cc
@@ -81,7 +81,6 @@
 #else
   request_notification.Show(base::BindOnce(&OnNotificationClosed, false));
 #endif
-  task_environment()->RunUntilIdle();
   EXPECT_FALSE(GetNotification().has_value());
 }
 
diff --git a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
index c437624..7f7767a 100644
--- a/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
+++ b/chrome/browser/extensions/chrome_component_extension_resource_manager.cc
@@ -21,8 +21,13 @@
 #include "content/public/browser/browser_thread.h"
 #include "extensions/common/constants.h"
 #include "pdf/buildflags.h"
+#include "ppapi/buildflags/buildflags.h"
 #include "ui/base/resource/resource_bundle.h"
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+#include "chrome/grit/pdf_resources_map.h"
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/keyboard/ui/resources/keyboard_resource_util.h"
 #include "base/command_line.h"
@@ -111,6 +116,11 @@
                               kComponentExtensionResourcesSize);
   AddComponentResourceEntries(kExtraComponentExtensionResources,
                               base::size(kExtraComponentExtensionResources));
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+  AddComponentResourceEntries(kPdfResources, kPdfResourcesSize);
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   // Add Files app JS modules resources.
   AddComponentResourceEntries(kFileManagerResources, kFileManagerResourcesSize);
@@ -153,28 +163,13 @@
 void ChromeComponentExtensionResourceManager::Data::AddComponentResourceEntries(
     const GritResourceMap* entries,
     size_t size) {
-  base::FilePath gen_folder_path = base::FilePath().AppendASCII(
-      "@out_folder@/gen/chrome/browser/resources/");
-  gen_folder_path = gen_folder_path.NormalizePathSeparators();
-
   for (size_t i = 0; i < size; ++i) {
     base::FilePath resource_path =
         base::FilePath().AppendASCII(entries[i].name);
     resource_path = resource_path.NormalizePathSeparators();
 
-    if (!gen_folder_path.IsParent(resource_path)) {
-      DCHECK(!base::Contains(path_to_resource_id_, resource_path));
-      path_to_resource_id_[resource_path] = entries[i].value;
-    } else {
-      // If the resource is a generated file, strip the generated folder's path,
-      // so that it can be served from a normal URL (as if it were not
-      // generated).
-      base::FilePath effective_path =
-          base::FilePath().AppendASCII(resource_path.AsUTF8Unsafe().substr(
-              gen_folder_path.value().length()));
-      DCHECK(!base::Contains(path_to_resource_id_, effective_path));
-      path_to_resource_id_[effective_path] = entries[i].value;
-    }
+    DCHECK(!base::Contains(path_to_resource_id_, resource_path));
+    path_to_resource_id_[resource_path] = entries[i].value;
   }
 }
 
diff --git a/chrome/browser/extensions/chrome_component_extension_resource_manager_unittest.cc b/chrome/browser/extensions/chrome_component_extension_resource_manager_unittest.cc
index 62f973f..8d169a5 100644
--- a/chrome/browser/extensions/chrome_component_extension_resource_manager_unittest.cc
+++ b/chrome/browser/extensions/chrome_component_extension_resource_manager_unittest.cc
@@ -80,38 +80,4 @@
 #endif
 }
 
-TEST_F(ChromeComponentExtensionResourceManagerTest,
-       IsComponentExtensionResource_Generated) {
-  // Check that the file being used for testing is indeed a generated resource.
-  int generated_resource_id = IDR_PDF_SHARED_VARS_JS;
-  bool found_resource = false;
-  for (size_t i = 0; i < kComponentExtensionResourcesSize; ++i) {
-    if (kComponentExtensionResources[i].value == generated_resource_id) {
-      ASSERT_TRUE(base::StartsWith(kComponentExtensionResources[i].name,
-                                   "@out_folder@",
-                                   base::CompareCase::SENSITIVE));
-      found_resource = true;
-      break;
-    }
-  }
-  ASSERT_TRUE(found_resource);
-
-  const ComponentExtensionResourceManager* resource_manager =
-      ExtensionsBrowserClient::Get()->GetComponentExtensionResourceManager();
-  ASSERT_TRUE(resource_manager);
-
-  base::FilePath resources_dir;
-  base::PathService::Get(chrome::DIR_RESOURCES, &resources_dir);
-
-  base::FilePath extension_path = resources_dir.AppendASCII("pdf");
-  base::FilePath resource_path =
-      base::FilePath().AppendASCII("elements/shared-vars.js");
-
-  // Check that the resource is classified as a component resource.
-  int resource_id = 0;
-  ASSERT_TRUE(resource_manager->IsComponentExtensionResource(
-      extension_path, resource_path, &resource_id));
-  ASSERT_EQ(generated_resource_id, resource_id);
-}
-
 }  // namespace extensions
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc b/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
index 373a41f5..75ff7dd 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_metrics_unittest.cc
@@ -26,7 +26,6 @@
 #include "extensions/browser/pref_names.h"
 #include "extensions/browser/updater/safe_manifest_parser.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/value_builder.h"
 #include "net/base/net_errors.h"
@@ -122,6 +121,7 @@
 
 namespace extensions {
 
+using ExtensionStatus = ForceInstalledTracker::ExtensionStatus;
 using testing::_;
 using testing::Return;
 
@@ -200,13 +200,13 @@
 
 TEST_F(ForceInstalledMetricsTest, ExtensionsInstalled) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
 
   histogram_tester_.ExpectTotalCount(kLoadTimeStats, 0);
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   histogram_tester_.ExpectTotalCount(kLoadTimeStats, 0);
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::LOADED);
 
   histogram_tester_.ExpectTotalCount(kLoadTimeStats, 1);
   histogram_tester_.ExpectTotalCount(kReadyTimeStats, 0);
@@ -227,8 +227,8 @@
 TEST_F(ForceInstalledMetricsTest, ExtensionSettingsOverrideForcedList) {
   SetupForceList(true /*is_from_store */);
   SetupExtensionManagementPref();
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::LOADED);
   // ForceInstalledMetrics shuts down timer because all extension are either
   // loaded or failed.
   EXPECT_FALSE(fake_timer_->IsRunning());
@@ -239,8 +239,9 @@
 
 TEST_F(ForceInstalledMetricsTest, ExtensionsInstallationTimedOut) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  registry()->AddEnabled(ext1.get());
+  scoped_refptr<const Extension> ext = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::PENDING);
+  registry()->AddEnabled(ext.get());
   EXPECT_TRUE(fake_timer_->IsRunning());
   fake_timer_->Fire();
   // Metrics are reported due to timeout.
@@ -267,8 +268,8 @@
   task_environment_.FastForwardBy(manifest_download_time);
   install_stage_tracker()->ReportDownloadingStage(
       kExtensionId1, ExtensionDownloaderDelegate::Stage::MANIFEST_LOADED);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::MANIFEST_INVALID);
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -286,8 +287,8 @@
   ReportDownloadingManifestStage();
   const base::TimeDelta install_time = base::TimeDelta::FromMilliseconds(200);
   ReportInstallationStarted(install_time);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::MANIFEST_INVALID);
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -308,8 +309,8 @@
       kExtensionId1, ExtensionDownloaderDelegate::Stage::FINISHED);
   install_stage_tracker()->ReportInstallationStage(
       kExtensionId1, InstallStageTracker::Stage::INSTALLING);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::MANIFEST_INVALID);
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -351,8 +352,8 @@
   install_stage_tracker()->ReportCRXInstallationStage(
       kExtensionId1, InstallationStage::kComplete);
 
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::MANIFEST_INVALID);
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -380,11 +381,13 @@
 TEST_F(ForceInstalledMetricsTest,
        ExtensionsInstalledButNotLoadedUniqueDisableReason) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::PENDING);
   registry()->AddDisabled(ext1.get());
   ExtensionPrefs::Get(profile())->AddDisableReason(
       kExtensionId1, disable_reason::DisableReason::DISABLE_NOT_VERIFIED);
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext2.get());
   force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
   // ForceInstalledMetrics should still keep running as kExtensionId1 is
@@ -400,13 +403,15 @@
 TEST_F(ForceInstalledMetricsTest,
        ExtensionsInstalledButNotLoadedMultipleDisableReason) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::PENDING);
   registry()->AddDisabled(ext1.get());
   ExtensionPrefs::Get(profile())->AddDisableReasons(
       kExtensionId1,
       disable_reason::DisableReason::DISABLE_NOT_VERIFIED |
           disable_reason::DisableReason::DISABLE_UNSUPPORTED_REQUIREMENT);
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext2.get());
   force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
   // ForceInstalledMetrics should still keep running as kExtensionId1 is
@@ -424,9 +429,11 @@
 TEST_F(ForceInstalledMetricsTest,
        ExtensionsInstalledButNotLoadedNoDisableReason) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext1.get());
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext2.get());
   force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
   // ForceInstalledMetrics should still keep running as kExtensionId1 is
@@ -439,9 +446,11 @@
 
 TEST_F(ForceInstalledMetricsTest, ExtensionForceInstalledAndBlocklisted) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::PENDING);
   registry()->AddBlocklisted(ext1.get());
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext2.get());
   force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
   // ForceInstalledMetrics should still keep running as kExtensionId1 is
@@ -475,9 +484,8 @@
   EXPECT_TRUE(fake_timer_->IsRunning());
   SetupForceList(true /*is_from_store */);
 
-  auto ext = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext.get());
-  force_installed_tracker()->OnExtensionReady(profile(), ext.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::READY);
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::INVALID_ID);
   // ForceInstalledMetrics shuts down timer because kExtensionId1 was loaded and
@@ -551,7 +559,8 @@
       kExtensionId1,
       CrxInstallError(SandboxedUnpackerFailureReason::CRX_HEADER_INVALID,
                       base::string16()));
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext2.get());
   force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -576,7 +585,8 @@
       kExtensionId1,
       CrxInstallError(SandboxedUnpackerFailureReason::CRX_HEADER_INVALID,
                       base::string16()));
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext2.get());
   force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -602,7 +612,8 @@
       kExtensionId1,
       CrxInstallError(SandboxedUnpackerFailureReason::CRX_HEADER_INVALID,
                       base::string16()));
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext2.get());
   force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -649,12 +660,12 @@
 TEST_F(ForceInstalledMetricsTest,
        ExtensionLoadedThenFailedWithAlreadyInstalledError) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportFailure(
       kExtensionId1, InstallStageTracker::FailureReason::ALREADY_INSTALLED);
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::LOADED);
   // ForceInstalledMetrics shuts down timer because all extension are either
   // loaded or failed.
   EXPECT_FALSE(fake_timer_->IsRunning());
@@ -668,14 +679,12 @@
 // after READY state is not reflected in the statistics.
 TEST_F(ForceInstalledMetricsTest, ExtensionsReady) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
-  force_installed_tracker()->OnExtensionReady(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::READY);
   install_stage_tracker()->ReportFailure(
       kExtensionId1, InstallStageTracker::FailureReason::ALREADY_INSTALLED);
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
-  force_installed_tracker()->OnExtensionReady(profile(), ext2.get());
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::READY);
   // ForceInstalledMetrics shuts down timer because all extension are either
   // loaded or failed.
   EXPECT_FALSE(fake_timer_->IsRunning());
@@ -690,9 +699,8 @@
 // extensions are failed.
 TEST_F(ForceInstalledMetricsTest, AllExtensionsNotReady) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
-  force_installed_tracker()->OnExtensionReady(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::READY);
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::INVALID_ID);
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -708,9 +716,8 @@
 TEST_F(ForceInstalledMetricsTest,
        ExtensionsPreviousInstallationStageReportedAgain) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportInstallationStage(
       kExtensionId2, InstallStageTracker::Stage::CREATED);
   install_stage_tracker()->ReportInstallationStage(
@@ -729,9 +736,8 @@
 // reported again after INSTALLING stage.
 TEST_F(ForceInstalledMetricsTest, ExtensionsDownloadingStageReportedAgain) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportInstallationStage(
       kExtensionId2, InstallStageTracker::Stage::DOWNLOADING);
   install_stage_tracker()->ReportInstallationStage(
@@ -775,9 +781,8 @@
 
 TEST_F(ForceInstalledMetricsTest, ExtensionStuckInCreatedStage) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportInstallationStage(
       kExtensionId2, InstallStageTracker::Stage::CREATED);
   install_stage_tracker()->ReportInstallCreationStage(
@@ -937,9 +942,8 @@
 // Errors occurred because the fetched update manifest was invalid.
 TEST_F(ForceInstalledMetricsTest, ExtensionManifestInvalid) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportManifestInvalidFailure(
       kExtensionId2,
       ExtensionDownloaderDelegate::FailureData(
@@ -957,9 +961,8 @@
 // "error-unknownApplication" is considered as a misconfiguration.
 TEST_F(ForceInstalledMetricsTest, ExtensionManifestInvalidAppStatusError) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportManifestInvalidFailure(
       kExtensionId2,
       ExtensionDownloaderDelegate::FailureData(
@@ -986,9 +989,8 @@
 TEST_F(ForceInstalledMetricsTest,
        NonMisconfigurationFailureNotPresentKioskModeOnlyError) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportCrxInstallError(
       kExtensionId2,
       InstallStageTracker::FailureReason::CRX_INSTALL_ERROR_DECLINED,
@@ -1013,9 +1015,8 @@
       ListBuilder().Append("extension").Append("theme").Build();
   prefs()->SetManagedPref(pref_names::kAllowedTypes, std::move(list));
 
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   // Hosted app is not a valid extension type, so this should report an error.
   install_stage_tracker()->ReportExtensionType(kExtensionId2,
                                                Manifest::Type::TYPE_HOSTED_APP);
@@ -1044,9 +1045,8 @@
       ListBuilder().Append("extension").Append("theme").Build();
   prefs()->SetManagedPref(pref_names::kAllowedTypes, std::move(list));
 
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportExtensionType(kExtensionId2,
                                                Manifest::Type::TYPE_EXTENSION);
   install_stage_tracker()->ReportCrxInstallError(
@@ -1091,9 +1091,8 @@
   prefs()->SetManagedPref(arc::prefs::kArcEnabled,
                           std::make_unique<base::Value>(true));
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::REPLACED_BY_ARC_APP);
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -1112,9 +1111,8 @@
   prefs()->SetManagedPref(arc::prefs::kArcEnabled,
                           std::make_unique<base::Value>(false));
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::REPLACED_BY_ARC_APP);
   // ForceInstalledMetrics shuts down timer because all extension are either
@@ -1131,9 +1129,8 @@
 TEST_F(ForceInstalledMetricsTest,
        NonMisconfigurationFailureNotPresentNotPerformingNewInstallError) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportFailure(
       kExtensionId2,
       InstallStageTracker::FailureReason::NOT_PERFORMING_NEW_INSTALL);
@@ -1150,9 +1147,8 @@
 TEST_F(ForceInstalledMetricsTest,
        NonMisconfigurationFailureNotPresentCrxFetchUrlEmptyError) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportInfoOnNoUpdatesFailure(kExtensionId2, "");
   install_stage_tracker()->ReportFailure(
       kExtensionId2, InstallStageTracker::FailureReason::CRX_FETCH_URL_EMPTY);
@@ -1168,9 +1164,8 @@
 TEST_F(ForceInstalledMetricsTest,
        NonMisconfigurationFailurePresentCrxFetchUrlEmptyError) {
   SetupForceList(true /*is_from_store */);
-  auto extension =
-      ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), extension.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   install_stage_tracker()->ReportInfoOnNoUpdatesFailure(kExtensionId2,
                                                         "rate limit");
   install_stage_tracker()->ReportFailure(
@@ -1201,7 +1196,8 @@
       kExtensionId1, ExtensionDownloaderDelegate::CacheStatus::CACHE_HIT);
   install_stage_tracker()->ReportDownloadingCacheStatus(
       kExtensionId2, ExtensionDownloaderDelegate::CacheStatus::CACHE_MISS);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::PENDING);
   registry()->AddEnabled(ext1.get());
   EXPECT_TRUE(fake_timer_->IsRunning());
   fake_timer_->Fire();
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_test_base.cc b/chrome/browser/extensions/forced_extensions/force_installed_test_base.cc
index 25abd55b..2b5d322 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_test_base.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_test_base.cc
@@ -8,7 +8,6 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/external_provider_impl.h"
-#include "chrome/browser/extensions/forced_extensions/force_installed_tracker.h"
 #include "chrome/browser/extensions/forced_extensions/install_stage_tracker.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/policy/core/common/policy_service_impl.h"
@@ -16,7 +15,6 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/pref_names.h"
-#include "extensions/common/extension.h"
 #include "extensions/common/extension_builder.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -107,4 +105,23 @@
   base::RunLoop().RunUntilIdle();
 }
 
+scoped_refptr<const Extension> ForceInstalledTestBase::CreateNewExtension(
+    const std::string& extension_name,
+    const std::string& extension_id,
+    const ForceInstalledTracker::ExtensionStatus& status) {
+  auto ext = ExtensionBuilder(extension_name).SetID(extension_id).Build();
+  switch (status) {
+    case ForceInstalledTracker::ExtensionStatus::PENDING:
+    case ForceInstalledTracker::ExtensionStatus::FAILED:
+      break;
+    case ForceInstalledTracker::ExtensionStatus::LOADED:
+      force_installed_tracker()->OnExtensionLoaded(profile(), ext.get());
+      break;
+    case ForceInstalledTracker::ExtensionStatus::READY:
+      force_installed_tracker()->OnExtensionLoaded(profile(), ext.get());
+      force_installed_tracker()->OnExtensionReady(profile(), ext.get());
+  }
+  return ext;
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_test_base.h b/chrome/browser/extensions/forced_extensions/force_installed_test_base.h
index 61247c95..eac74ad 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_test_base.h
+++ b/chrome/browser/extensions/forced_extensions/force_installed_test_base.h
@@ -5,10 +5,12 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_FORCED_EXTENSIONS_FORCE_INSTALLED_TEST_BASE_H_
 #define CHROME_BROWSER_EXTENSIONS_FORCED_EXTENSIONS_FORCE_INSTALLED_TEST_BASE_H_
 
+#include "chrome/browser/extensions/forced_extensions/force_installed_tracker.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "content/public/test/browser_task_environment.h"
+#include "extensions/common/extension.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace sync_preferences {
@@ -19,7 +21,6 @@
 
 class ExtensionRegistry;
 class InstallStageTracker;
-class ForceInstalledTracker;
 
 // This class is extended by tests to provide a setup for tracking installation
 // of force extensions. It also provides helper functions for creating and
@@ -44,6 +45,14 @@
   // kInstallForceList preference.
   void SetupEmptyForceList();
 
+  // Creates a new extension with |extension_id| and |extension_name| and fakes
+  // its status by calling one of ForceInstalledTracker's
+  // ExtensionRegistryObserver override.
+  scoped_refptr<const Extension> CreateNewExtension(
+      const std::string& extension_name,
+      const std::string& extension_id,
+      const ForceInstalledTracker::ExtensionStatus& status);
+
   Profile* profile() const { return profile_; }
 
   sync_preferences::TestingPrefServiceSyncable* prefs() const { return prefs_; }
diff --git a/chrome/browser/extensions/forced_extensions/force_installed_tracker_unittest.cc b/chrome/browser/extensions/forced_extensions/force_installed_tracker_unittest.cc
index 14b3f1fdc..f6a67cf5 100644
--- a/chrome/browser/extensions/forced_extensions/force_installed_tracker_unittest.cc
+++ b/chrome/browser/extensions/forced_extensions/force_installed_tracker_unittest.cc
@@ -8,11 +8,12 @@
 #include "base/values.h"
 #include "chrome/browser/extensions/forced_extensions/force_installed_test_base.h"
 #include "extensions/common/extension.h"
-#include "extensions/common/extension_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace extensions {
 
+using ExtensionStatus = ForceInstalledTracker::ExtensionStatus;
+
 class ForceInstalledTrackerTest : public ForceInstalledTestBase,
                                   public ForceInstalledTracker::Observer {
  public:
@@ -56,8 +57,10 @@
 // become ready for use.
 TEST_F(ForceInstalledTrackerTest, AllExtensionsInstalled) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::PENDING);
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::PENDING);
   EXPECT_FALSE(loaded_called_);
   EXPECT_FALSE(ready_called_);
   EXPECT_FALSE(force_installed_tracker()->IsDoneLoading());
@@ -81,8 +84,8 @@
 // all extensions have either successfully loaded or failed.
 TEST_F(ForceInstalledTrackerTest, ExtensionPendingInstall) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   EXPECT_FALSE(loaded_called_);
   EXPECT_FALSE(ready_called_);
   EXPECT_FALSE(force_installed_tracker()->IsDoneLoading());
@@ -100,10 +103,10 @@
   // Start with a non-empty force-list, and install them, which triggers
   // observer.
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  auto ext2 = ExtensionBuilder(kExtensionName2).SetID(kExtensionId2).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext2.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
+  scoped_refptr<const Extension> ext2 = CreateNewExtension(
+      kExtensionName2, kExtensionId2, ExtensionStatus::LOADED);
   EXPECT_TRUE(loaded_called_);
 
   force_installed_tracker()->OnExtensionReady(profile(), ext1.get());
@@ -119,8 +122,8 @@
 // either successfully loaded or failed.
 TEST_F(ForceInstalledTrackerTest, ExtensionsInstallationFailed) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   force_installed_tracker()->OnExtensionInstallationFailed(
       kExtensionId2, InstallStageTracker::FailureReason::INVALID_ID);
   EXPECT_TRUE(loaded_called_);
@@ -134,22 +137,22 @@
 TEST_F(ForceInstalledTrackerTest, ExtensionsStatus) {
   SetupForceList(true /*is_from_store */);
   EXPECT_EQ(force_installed_tracker()->extensions().at(kExtensionId1).status,
-            ForceInstalledTracker::ExtensionStatus::PENDING);
+            ExtensionStatus::PENDING);
   EXPECT_EQ(force_installed_tracker()->extensions().at(kExtensionId2).status,
-            ForceInstalledTracker::ExtensionStatus::PENDING);
+            ExtensionStatus::PENDING);
 
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::LOADED);
   force_installed_tracker()->OnExtensionInstallationFailed(
       kExtensionId2, InstallStageTracker::FailureReason::INVALID_ID);
   EXPECT_EQ(force_installed_tracker()->extensions().at(kExtensionId1).status,
-            ForceInstalledTracker::ExtensionStatus::LOADED);
+            ExtensionStatus::LOADED);
   EXPECT_EQ(force_installed_tracker()->extensions().at(kExtensionId2).status,
-            ForceInstalledTracker::ExtensionStatus::FAILED);
+            ExtensionStatus::FAILED);
 
   force_installed_tracker()->OnExtensionReady(profile(), ext1.get());
   EXPECT_EQ(force_installed_tracker()->extensions().at(kExtensionId1).status,
-            ForceInstalledTracker::ExtensionStatus::READY);
+            ExtensionStatus::READY);
 }
 
 // This test verifies that resetting the policy before all force installed
@@ -165,9 +168,8 @@
 // installed extension is either ready for use or failed.
 TEST_F(ForceInstalledTrackerTest, AllExtensionsReady) {
   SetupForceList(true /*is_from_store */);
-  auto ext1 = ExtensionBuilder(kExtensionName1).SetID(kExtensionId1).Build();
-  force_installed_tracker()->OnExtensionLoaded(profile(), ext1.get());
-  force_installed_tracker()->OnExtensionReady(profile(), ext1.get());
+  scoped_refptr<const Extension> ext1 = CreateNewExtension(
+      kExtensionName1, kExtensionId1, ExtensionStatus::READY);
   force_installed_tracker()->OnExtensionInstallationFailed(
       kExtensionId2, InstallStageTracker::FailureReason::INVALID_ID);
   EXPECT_TRUE(loaded_called_);
diff --git a/chrome/browser/lacros/account_manager_facade_lacros.cc b/chrome/browser/lacros/account_manager_facade_lacros.cc
index ad77517..dbaa6f30 100644
--- a/chrome/browser/lacros/account_manager_facade_lacros.cc
+++ b/chrome/browser/lacros/account_manager_facade_lacros.cc
@@ -38,6 +38,18 @@
   return is_initialized_;
 }
 
+void AccountManagerFacadeLacros::ShowAddAccountDialog(
+    const AccountAdditionSource& source,
+    base::OnceCallback<void(const AccountAdditionResult& result)> callback) {
+  // TODO(crbug.com/1140469): implement this.
+}
+
+void AccountManagerFacadeLacros::ShowReauthAccountDialog(
+    const AccountAdditionSource& source,
+    const std::string& email) {
+  // TODO(crbug.com/1140469): implement this.
+}
+
 void AccountManagerFacadeLacros::OnVersionCheck(uint32_t version) {
   if (version < kMinVersionWithObserver) {
     std::move(init_finished_).Run();
diff --git a/chrome/browser/lacros/account_manager_facade_lacros.h b/chrome/browser/lacros/account_manager_facade_lacros.h
index 485efd7..6103c2f 100644
--- a/chrome/browser/lacros/account_manager_facade_lacros.h
+++ b/chrome/browser/lacros/account_manager_facade_lacros.h
@@ -29,6 +29,12 @@
 
   // AccountManagerFacade overrides:
   bool IsInitialized() override;
+  void ShowAddAccountDialog(
+      const AccountAdditionSource& source,
+      base::OnceCallback<void(const AccountAdditionResult& result)> callback)
+      override;
+  void ShowReauthAccountDialog(const AccountAdditionSource& source,
+                               const std::string& email) override;
 
   // crosapi::mojom::AccountManagerObserver overrides:
   void OnTokenUpserted(crosapi::mojom::AccountPtr account) override;
diff --git a/chrome/browser/net/cookie_policy_browsertest.cc b/chrome/browser/net/cookie_policy_browsertest.cc
index 57a9987..827a736 100644
--- a/chrome/browser/net/cookie_policy_browsertest.cc
+++ b/chrome/browser/net/cookie_policy_browsertest.cc
@@ -51,8 +51,6 @@
     // HTTPS server only serves a valid cert for localhost, so this is needed
     // to load pages from other hosts without an error.
     command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
-                                    "CookieStoreDocument");
   }
 
   GURL GetURL(const std::string& host) {
diff --git a/chrome/browser/net/cookie_store_samesite_browsertest.cc b/chrome/browser/net/cookie_store_samesite_browsertest.cc
index d88eec5e..70b44e42 100644
--- a/chrome/browser/net/cookie_store_samesite_browsertest.cc
+++ b/chrome/browser/net/cookie_store_samesite_browsertest.cc
@@ -45,11 +45,6 @@
     ASSERT_TRUE(https_server_.Start());
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
-                                    "CookieStoreDocument");
-  }
-
   void NavigateToPageWithFrame(const std::string& host) {
     GURL main_url(https_server_.GetURL(host, "/iframe.html"));
     ui_test_utils::NavigateToURL(browser(), main_url);
diff --git a/chrome/browser/paint_preview/paint_preview_browsertest.cc b/chrome/browser/paint_preview/paint_preview_browsertest.cc
index c5ab507b..d4bc4ff 100644
--- a/chrome/browser/paint_preview/paint_preview_browsertest.cc
+++ b/chrome/browser/paint_preview/paint_preview_browsertest.cc
@@ -24,17 +24,44 @@
 #include "components/paint_preview/common/test_utils.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/skia/include/core/SkPicture.h"
 #include "third_party/skia/include/core/SkStream.h"
 #include "url/gurl.h"
 
 namespace paint_preview {
 
+class NoOpPaintPreviewRecorder : public mojom::PaintPreviewRecorder {
+ public:
+  NoOpPaintPreviewRecorder() = default;
+  ~NoOpPaintPreviewRecorder() override = default;
+
+  NoOpPaintPreviewRecorder(const NoOpPaintPreviewRecorder&) = delete;
+  NoOpPaintPreviewRecorder& operator=(const NoOpPaintPreviewRecorder&) = delete;
+
+  void CapturePaintPreview(
+      mojom::PaintPreviewCaptureParamsPtr params,
+      mojom::PaintPreviewRecorder::CapturePaintPreviewCallback callback)
+      override {
+    callback_ = std::move(callback);
+  }
+
+  void BindRequest(mojo::ScopedInterfaceEndpointHandle handle) {
+    binding_.Bind(mojo::PendingAssociatedReceiver<mojom::PaintPreviewRecorder>(
+        std::move(handle)));
+  }
+
+ private:
+  mojom::PaintPreviewRecorder::CapturePaintPreviewCallback callback_;
+  mojo::AssociatedReceiver<mojom::PaintPreviewRecorder> binding_{this};
+};
+
 // Test harness for a integration test of paint previews. In this test:
 // - Each RenderFrame has an instance of PaintPreviewRecorder attached.
 // - Each WebContents has an instance of PaintPreviewClient attached.
@@ -42,6 +69,10 @@
 class PaintPreviewBrowserTest
     : public InProcessBrowserTest,
       public testing::WithParamInterface<RecordingPersistence> {
+ public:
+  PaintPreviewBrowserTest(const PaintPreviewBrowserTest&) = delete;
+  PaintPreviewBrowserTest& operator=(const PaintPreviewBrowserTest&) = delete;
+
  protected:
   PaintPreviewBrowserTest() = default;
   ~PaintPreviewBrowserTest() override = default;
@@ -92,6 +123,15 @@
     return params;
   }
 
+  void OverrideInterface(NoOpPaintPreviewRecorder* service) {
+    blink::AssociatedInterfaceProvider* remote_interfaces =
+        GetWebContents()->GetMainFrame()->GetRemoteAssociatedInterfaces();
+    remote_interfaces->OverrideBinderForTesting(
+        mojom::PaintPreviewRecorder::Name_,
+        base::BindRepeating(&NoOpPaintPreviewRecorder::BindRequest,
+                            base::Unretained(service)));
+  }
+
   void WaitForLoadStopWithoutSuccessCheck() {
     // In many cases, the load may have finished before we get here.  Only wait
     // if the tab still has a pending navigation.
@@ -134,10 +174,6 @@
   base::ScopedTempDir temp_dir_;
   net::EmbeddedTestServer http_server_;
   net::EmbeddedTestServer http_server_different_origin_;
-
- private:
-  PaintPreviewBrowserTest(const PaintPreviewBrowserTest&) = delete;
-  PaintPreviewBrowserTest& operator=(const PaintPreviewBrowserTest&) = delete;
 };
 
 IN_PROC_BROWSER_TEST_P(PaintPreviewBrowserTest, CaptureFrame) {
@@ -324,6 +360,79 @@
   loop.Run();
 }
 
+// https://crbug.com/1146573 reproduction. If a renderer crashes,
+// WebContentsObserver::RenderFrameDeleted. Paint preview implements this in an
+// observer which in turn calls DecrementCapturerCount which can cause the
+// WebContents to be reloaded on Android where we have auto-reload. This reload
+// occurs *during* crash handling, leaving the frame in an invalid state and
+// leading to a crash when it subsequently unloaded.
+// This is fixed by deferring it to a PostTask.
+// Flaky on Mac. TODO(https://crbug.com/1160608): Enabled this test.
+#if defined(OS_MAC)
+#define MAYBE_DontReloadInRenderProcessExit \
+  DISABLED_DontReloadInRenderProcessExit
+#else
+#define MAYBE_DontReloadInRenderProcessExit DontReloadInRenderProcessExit
+#endif
+IN_PROC_BROWSER_TEST_P(PaintPreviewBrowserTest,
+                       MAYBE_DontReloadInRenderProcessExit) {
+  LoadPage(http_server_.GetURL("a.com", "/title1.html"));
+
+  content::WebContents* web_contents = GetWebContents();
+
+  // Override remote interfaces with a no-op.
+  NoOpPaintPreviewRecorder noop_recorder;
+  OverrideInterface(&noop_recorder);
+
+  CreateClient();
+  auto* client = PaintPreviewClient::FromWebContents(web_contents);
+  // Do this twice to simulate conditions for crash.
+  web_contents->IncrementCapturerCount(gfx::Size(), true);
+  web_contents->IncrementCapturerCount(gfx::Size(), true);
+
+  // A callback that causes the frame to reload and end up in an invalid state
+  // if it is allowed to run during crash handling.
+  base::RunLoop loop;
+  auto params = MakeParams();
+  bool did_run = false;
+  client->CapturePaintPreview(
+      params, web_contents->GetMainFrame(),
+      // This callback is now posted so it shouldn't cause a crash.
+      base::BindOnce(
+          [](content::WebContents* web_contents, bool* did_run_ptr,
+             base::UnguessableToken guid, mojom::PaintPreviewStatus status,
+             std::unique_ptr<CaptureResult> result) {
+            EXPECT_EQ(status, mojom::PaintPreviewStatus::kFailed);
+            EXPECT_EQ(result, nullptr);
+            // On Android crashed frames are marked as needing reload.
+            web_contents->GetController().SetNeedsReload();
+            web_contents->DecrementCapturerCount(true);
+            web_contents->DecrementCapturerCount(true);
+            *did_run_ptr = true;
+          },
+          web_contents, &did_run)
+          .Then(loop.QuitClosure()));
+
+  // Crash the renderer.
+  {
+    base::ScopedAllowBlockingForTesting scope;
+    content::RenderProcessHost* process =
+        GetWebContents()->GetMainFrame()->GetProcess();
+    content::RenderProcessHostWatcher crash_observer(
+        process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+    process->Shutdown(0);
+    crash_observer.Wait();
+  }
+
+  // The browser would have crashed before the loop exited if the callback was
+  // not posted.
+  if (!did_run)
+    loop.Run();
+
+  // Now navigate away and ensure that the frame unloads successfully.
+  LoadPage(http_server_.GetURL("a.com", "/title2.html"));
+}
+
 INSTANTIATE_TEST_SUITE_P(All,
                          PaintPreviewBrowserTest,
                          testing::Values(RecordingPersistence::kFileSystem,
diff --git a/chrome/browser/password_manager/generated_password_leak_detection_pref.cc b/chrome/browser/password_manager/generated_password_leak_detection_pref.cc
index a016c56f..ea4c253 100644
--- a/chrome/browser/password_manager/generated_password_leak_detection_pref.cc
+++ b/chrome/browser/password_manager/generated_password_leak_detection_pref.cc
@@ -141,14 +141,16 @@
   identity_manager_observer_.RemoveAll();
 }
 
-void GeneratedPasswordLeakDetectionPref::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  NotifyObservers(kGeneratedPasswordLeakDetectionPref);
-}
-
-void GeneratedPasswordLeakDetectionPref::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
-  NotifyObservers(kGeneratedPasswordLeakDetectionPref);
+void GeneratedPasswordLeakDetectionPref::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event_details) {
+  switch (event_details.GetEventTypeFor(signin::ConsentLevel::kSync)) {
+    case signin::PrimaryAccountChangeEvent::Type::kSet:
+    case signin::PrimaryAccountChangeEvent::Type::kCleared:
+      NotifyObservers(kGeneratedPasswordLeakDetectionPref);
+      break;
+    case signin::PrimaryAccountChangeEvent::Type::kNone:
+      break;
+  }
 }
 
 void GeneratedPasswordLeakDetectionPref::OnExtendedAccountInfoUpdated(
diff --git a/chrome/browser/password_manager/generated_password_leak_detection_pref.h b/chrome/browser/password_manager/generated_password_leak_detection_pref.h
index f57e601..9cfe2b2 100644
--- a/chrome/browser/password_manager/generated_password_leak_detection_pref.h
+++ b/chrome/browser/password_manager/generated_password_leak_detection_pref.h
@@ -39,10 +39,8 @@
   void OnSourcePreferencesChanged();
 
   // IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event_details) override;
   void OnExtendedAccountInfoUpdated(const AccountInfo& info) override;
   void OnExtendedAccountInfoRemoved(const AccountInfo& info) override;
 
diff --git a/chrome/browser/policy/messaging_layer/storage/storage.cc b/chrome/browser/policy/messaging_layer/storage/storage.cc
index 9e5a41f..7f2c51b 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/policy/messaging_layer/storage/storage.h"
 
+#include <cstdint>
 #include <utility>
 #include <vector>
 
@@ -22,6 +23,7 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/policy/messaging_layer/encryption/encryption_module.h"
+#include "chrome/browser/policy/messaging_layer/encryption/verification.h"
 #include "chrome/browser/policy/messaging_layer/storage/storage_configuration.h"
 #include "chrome/browser/policy/messaging_layer/storage/storage_queue.h"
 #include "chrome/browser/policy/messaging_layer/util/status.h"
@@ -29,6 +31,7 @@
 #include "chrome/browser/policy/messaging_layer/util/statusor.h"
 #include "chrome/browser/policy/messaging_layer/util/task_runner_context.h"
 #include "components/policy/proto/record.pb.h"
+#include "third_party/boringssl/src/include/openssl/curve25519.h"
 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl.h"
 
 namespace reporting {
@@ -157,8 +160,9 @@
 
 class Storage::KeyInStorage {
  public:
-  explicit KeyInStorage(const base::FilePath& directory)
-      : directory_(directory) {}
+  explicit KeyInStorage(base::StringPiece signature_verification_public_key,
+                        const base::FilePath& directory)
+      : verifier_(signature_verification_public_key), directory_(directory) {}
   ~KeyInStorage() = default;
 
   // Uploads signed encryption key to a file with an |index| >=
@@ -219,6 +223,24 @@
         signed_encryption_key_result.value().second.public_key_id());
   }
 
+  Status VerifySignature(const SignedEncryptionInfo& signed_encryption_key) {
+    if (signed_encryption_key.public_asymmetric_key().size() !=
+        X25519_PUBLIC_VALUE_LEN) {
+      return Status{error::FAILED_PRECONDITION, "Key size mismatch"};
+    }
+    char value_to_verify[sizeof(Encryptor::PublicKeyId) +
+                         X25519_PUBLIC_VALUE_LEN];
+    const Encryptor::PublicKeyId public_key_id =
+        signed_encryption_key.public_key_id();
+    memcpy(value_to_verify, &public_key_id, sizeof(Encryptor::PublicKeyId));
+    memcpy(value_to_verify + sizeof(Encryptor::PublicKeyId),
+           signed_encryption_key.public_asymmetric_key().data(),
+           X25519_PUBLIC_VALUE_LEN);
+    return verifier_.Verify(
+        std::string(value_to_verify, sizeof(value_to_verify)),
+        signed_encryption_key.signature());
+  }
+
  private:
   // Writes key into file. Called during key upload.
   Status WriteKeyInfoFile(uint64_t new_file_index,
@@ -375,8 +397,16 @@
         }
       }
 
-      // Parsed successfully.
-      // TODO(b/170054326): Validate signature.
+      // Parsed successfully. Verify signature of the whole "id"+"key" string.
+      const auto signature_verification_status =
+          VerifySignature(signed_encryption_key);
+      if (!signature_verification_status.ok()) {
+        LOG(WARNING) << "Loaded key failed verification, status="
+                     << signature_verification_status << ", full_name='"
+                     << key_file_it->second.MaybeAsASCII() << "'";
+        continue;
+      }
+
       // Validated successfully. Return file name and signed key proto.
       return std::make_pair(key_file_it->second, signed_encryption_key);
     }
@@ -392,6 +422,8 @@
   // to successfully encrypt records and for the server to then decrypt them.
   std::atomic<uint64_t> next_key_file_index_{0};
 
+  SignatureVerifier verifier_;
+
   const base::FilePath directory_;
 };
 
@@ -537,7 +569,9 @@
                  StartUploadCb start_upload_cb)
     : options_(options),
       encryption_module_(encryption_module),
-      key_in_storage_(std::make_unique<KeyInStorage>(options.directory())),
+      key_in_storage_(std::make_unique<KeyInStorage>(
+          options.signature_verification_public_key(),
+          options.directory())),
       start_upload_cb_(std::move(start_upload_cb)) {}
 
 Storage::~Storage() = default;
@@ -571,7 +605,14 @@
 }
 
 void Storage::UpdateEncryptionKey(SignedEncryptionInfo signed_encryption_key) {
-  // TODO(b/170054326): Verify received key signature. Bail out if failed.
+  // Verify received key signature. Bail out if failed.
+  const auto signature_verification_status =
+      key_in_storage_->VerifySignature(signed_encryption_key);
+  if (!signature_verification_status.ok()) {
+    LOG(WARNING) << "Key failed verification, status="
+                 << signature_verification_status;
+    return;
+  }
 
   // Assign the received key to encryption module.
   encryption_module_->UpdateAsymmetricKey(
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_configuration.cc b/chrome/browser/policy/messaging_layer/storage/storage_configuration.cc
new file mode 100644
index 0000000..96dfe382
--- /dev/null
+++ b/chrome/browser/policy/messaging_layer/storage/storage_configuration.cc
@@ -0,0 +1,15 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/policy/messaging_layer/storage/storage_configuration.h"
+
+namespace reporting {
+
+StorageOptions::StorageOptions() = default;
+StorageOptions::StorageOptions(const StorageOptions& options) = default;
+StorageOptions& StorageOptions::operator=(const StorageOptions& options) =
+    default;
+StorageOptions::~StorageOptions() = default;
+
+}  // namespace reporting
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_configuration.h b/chrome/browser/policy/messaging_layer/storage/storage_configuration.h
index 8c847bd..d47296a7 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_configuration.h
+++ b/chrome/browser/policy/messaging_layer/storage/storage_configuration.h
@@ -5,6 +5,12 @@
 #ifndef CHROME_BROWSER_POLICY_MESSAGING_LAYER_STORAGE_STORAGE_CONFIGURATION_H_
 #define CHROME_BROWSER_POLICY_MESSAGING_LAYER_STORAGE_STORAGE_CONFIGURATION_H_
 
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_piece.h"
+#include "base/time/time.h"
+
 namespace reporting {
 
 // Storage options class allowing to set parameters individually, e.g.:
@@ -16,13 +22,20 @@
 //                 callback);
 class StorageOptions {
  public:
-  StorageOptions() = default;
-  StorageOptions(const StorageOptions& options) = default;
-  StorageOptions& operator=(const StorageOptions& options) = default;
+  StorageOptions();
+  StorageOptions(const StorageOptions& options);
+  StorageOptions& operator=(const StorageOptions& options);
+  ~StorageOptions();
   StorageOptions& set_directory(const base::FilePath& directory) {
     directory_ = directory;
     return *this;
   }
+  StorageOptions& set_signature_verification_public_key(
+      base::StringPiece signature_verification_public_key) {
+    signature_verification_public_key_ =
+        std::string(signature_verification_public_key);
+    return *this;
+  }
   StorageOptions& set_max_record_size(size_t max_record_size) {
     max_record_size_ = max_record_size;
     return *this;
@@ -40,6 +53,9 @@
     return *this;
   }
   const base::FilePath& directory() const { return directory_; }
+  base::StringPiece signature_verification_public_key() const {
+    return signature_verification_public_key_;
+  }
   size_t max_record_size() const { return max_record_size_; }
   uint64_t max_total_files_size() const { return max_total_files_size_; }
   uint64_t max_total_memory_size() const { return max_total_memory_size_; }
@@ -49,6 +65,10 @@
   // Subdirectory of the location assigned for this Storage.
   base::FilePath directory_;
 
+  // Public key for signature verification when encryption key
+  // is delivered to Storage.
+  std::string signature_verification_public_key_;
+
   // Maximum record size.
   size_t max_record_size_ = 1 * 1024LL * 1024LL;  // 1 MiB
 
diff --git a/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc b/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
index 5cf47df..eb4ccd9 100644
--- a/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/storage/storage_unittest.cc
@@ -438,6 +438,8 @@
       // Enable encryption.
       scoped_feature_list_.InitFromCommandLine(
           {EncryptionModule::kEncryptedReporting}, {});
+      // Generate signing key pair.
+      ED25519_keypair(signature_verification_public_key_, signing_private_key_);
       // Create decryption module.
       auto decryptor_result = Decryptor::Create();
       ASSERT_OK(decryptor_result.status()) << decryptor_result.status();
@@ -495,9 +497,16 @@
   }
 
   StorageOptions BuildTestStorageOptions() const {
-    return StorageOptions()
-        .set_directory(base::FilePath(location_.GetPath()))
-        .set_single_file_size(::testing::get<1>(GetParam()));
+    auto options = StorageOptions()
+                       .set_directory(base::FilePath(location_.GetPath()))
+                       .set_single_file_size(::testing::get<1>(GetParam()));
+    if (::testing::get<0>(GetParam())) {
+      // Encryption enabled.
+      options.set_signature_verification_public_key(std::string(
+          reinterpret_cast<const char*>(signature_verification_public_key_),
+          ED25519_PUBLIC_KEY_LEN));
+    }
+    return options;
   }
 
   StatusOr<std::unique_ptr<Storage::UploaderInterface>> BuildMockUploader(
@@ -534,8 +543,9 @@
   void GenerateAndDeliverKey(Storage* storage) {
     ASSERT_TRUE(decryptor_) << "Decryptor not created";
     // Generate new pair of private key and public value.
-    uint8_t public_value[X25519_PUBLIC_VALUE_LEN];
     uint8_t private_key[X25519_PRIVATE_KEY_LEN];
+    Encryptor::PublicKeyId public_key_id;
+    uint8_t public_value[X25519_PUBLIC_VALUE_LEN];
     X25519_keypair(public_value, private_key);
     TestEvent<StatusOr<Encryptor::PublicKeyId>> prepare_key_pair;
     decryptor_->RecordKeyPair(
@@ -546,13 +556,28 @@
         prepare_key_pair.cb());
     auto prepare_key_result = prepare_key_pair.result();
     ASSERT_OK(prepare_key_result.status());
-    Encryptor::PublicKeyId new_public_key_id = prepare_key_result.ValueOrDie();
+    public_key_id = prepare_key_result.ValueOrDie();
     // Deliver public key to storage.
     SignedEncryptionInfo signed_encryption_key;
     signed_encryption_key.set_public_asymmetric_key(std::string(
         reinterpret_cast<const char*>(public_value), X25519_PUBLIC_VALUE_LEN));
-    signed_encryption_key.set_public_key_id(new_public_key_id);
-    // TODO(b/170054326): Add key signature.
+    signed_encryption_key.set_public_key_id(public_key_id);
+    // Sign public key.
+    uint8_t
+        value_to_sign[sizeof(Encryptor::PublicKeyId) + X25519_PUBLIC_VALUE_LEN];
+    memcpy(value_to_sign, &public_key_id, sizeof(Encryptor::PublicKeyId));
+    memcpy(value_to_sign + sizeof(Encryptor::PublicKeyId), public_value,
+           X25519_PUBLIC_VALUE_LEN);
+    uint8_t signature[ED25519_SIGNATURE_LEN];
+    ASSERT_THAT(ED25519_sign(signature, value_to_sign, sizeof(value_to_sign),
+                             signing_private_key_),
+                Eq(1));
+    signed_encryption_key.set_signature(std::string(
+        reinterpret_cast<const char*>(signature), ED25519_SIGNATURE_LEN));
+    // Double check signature.
+    ASSERT_THAT(ED25519_verify(value_to_sign, sizeof(value_to_sign), signature,
+                               signature_verification_public_key_),
+                Eq(1));
     storage->UpdateEncryptionKey(signed_encryption_key);
   }
 
@@ -561,6 +586,9 @@
 
   base::test::ScopedFeatureList scoped_feature_list_;
 
+  uint8_t signature_verification_public_key_[ED25519_PUBLIC_KEY_LEN];
+  uint8_t signing_private_key_[ED25519_PRIVATE_KEY_LEN];
+
   base::ScopedTempDir location_;
   scoped_refptr<Decryptor> decryptor_;
   scoped_refptr<Storage> storage_;
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
index 4815d57..e5fc66e 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
+++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
@@ -257,17 +257,14 @@
         signed_encryption_key_record->FindStringKey("publicKey");
     const auto public_key_id_result =
         signed_encryption_key_record->FindIntKey("publicKeyId");
-    // TODO(b/170054326): Make signature mandatory too.
-    // const std::string* public_key_signature_str =
-    //     signed_encryption_key_record->FindStringKey("publicKeySignature");
+    const std::string* public_key_signature_str =
+        signed_encryption_key_record->FindStringKey("publicKeySignature");
     std::string public_key;
     std::string public_key_signature;
     if (public_key_str != nullptr &&
         base::Base64Decode(*public_key_str, &public_key) &&
-        // TODO(b/170054326): Make signature mandatory too.
-        // public_key_signature_str != nullptr
-        // base::Base64Decode(*public_key_signature_str,
-        //                    &public_key_signature) &&
+        public_key_signature_str != nullptr &&
+        base::Base64Decode(*public_key_signature_str, &public_key_signature) &&
         public_key_id_result.has_value()) {
       SignedEncryptionInfo signed_encryption_key;
       signed_encryption_key.set_public_asymmetric_key(public_key);
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
index 4162b8e8..f8234f8 100644
--- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
@@ -28,9 +28,13 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 using ::testing::_;
+using ::testing::AllOf;
 using ::testing::Eq;
+using ::testing::Gt;
 using ::testing::Invoke;
+using ::testing::IsEmpty;
 using ::testing::MockFunction;
+using ::testing::Not;
 using ::testing::Property;
 using ::testing::Return;
 using ::testing::StrictMock;
@@ -132,7 +136,6 @@
   encryption_settings.SetStringKey("publicKey", public_key);
   encryption_settings.SetIntKey("publicKeyId", 12345);
   std::string public_key_signature;
-  // TODO(b/170054326): Generate signature.
   base::Base64Encode("PUBLIC KEY SIG", &public_key_signature);
   encryption_settings.SetStringKey("publicKeySignature", public_key_signature);
   return encryption_settings;
@@ -249,7 +252,12 @@
   StrictMock<TestCompletionResponder> responder;
   TestCallbackWaiter responder_waiter;
 
-  EXPECT_CALL(encryption_key_attached, Call(_))
+  EXPECT_CALL(
+      encryption_key_attached,
+      Call(AllOf(Property(&SignedEncryptionInfo::public_asymmetric_key,
+                          Not(IsEmpty())),
+                 Property(&SignedEncryptionInfo::public_key_id, Gt(0)),
+                 Property(&SignedEncryptionInfo::signature, Not(IsEmpty())))))
       .Times(need_encryption_key() ? 1 : 0);
 
   EXPECT_CALL(
@@ -313,7 +321,12 @@
       .WillOnce(Invoke([&responder_waiter]() { responder_waiter.Signal(); }));
 
   StrictMock<TestEncryptionKeyAttached> encryption_key_attached;
-  EXPECT_CALL(encryption_key_attached, Call(_))
+  EXPECT_CALL(
+      encryption_key_attached,
+      Call(AllOf(Property(&SignedEncryptionInfo::public_asymmetric_key,
+                          Not(IsEmpty())),
+                 Property(&SignedEncryptionInfo::public_key_id, Gt(0)),
+                 Property(&SignedEncryptionInfo::signature, Not(IsEmpty())))))
       .Times(need_encryption_key() ? 1 : 0);
 
   auto encryption_key_attached_callback =
@@ -389,7 +402,12 @@
       .WillOnce(Invoke([&responder_waiter]() { responder_waiter.Signal(); }));
 
   StrictMock<TestEncryptionKeyAttached> encryption_key_attached;
-  EXPECT_CALL(encryption_key_attached, Call(_))
+  EXPECT_CALL(
+      encryption_key_attached,
+      Call(AllOf(Property(&SignedEncryptionInfo::public_asymmetric_key,
+                          Not(IsEmpty())),
+                 Property(&SignedEncryptionInfo::public_key_id, Gt(0)),
+                 Property(&SignedEncryptionInfo::signature, Not(IsEmpty())))))
       .Times(need_encryption_key() ? 1 : 0);
   auto encryption_key_attached_callback =
       base::BindRepeating(&TestEncryptionKeyAttached::Call,
diff --git a/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc b/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
index 7b415dc..11bae9c0 100644
--- a/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
+++ b/chrome/browser/policy/messaging_layer/upload/upload_client_unittest.cc
@@ -33,9 +33,14 @@
 
 using ::policy::MockCloudPolicyClient;
 using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Gt;
 using ::testing::Invoke;
 using ::testing::InvokeArgument;
+using ::testing::IsEmpty;
 using ::testing::MockFunction;
+using ::testing::Not;
+using ::testing::Property;
 using ::testing::StrictMock;
 using ::testing::WithArgs;
 
@@ -154,7 +159,6 @@
     encryption_settings.SetStringKey("publicKey", public_key);
     encryption_settings.SetIntKey("publicKeyId", 12345);
     std::string public_key_signature;
-    // TODO(b/170054326): Generate signature.
     base::Base64Encode("PUBLIC KEY SIG", &public_key_signature);
     encryption_settings.SetStringKey("publicKeySignature",
                                      public_key_signature);
@@ -240,7 +244,12 @@
   TestCallbackWaiterWithCounter waiter(kExpectedCallTimes);
 
   StrictMock<TestEncryptionKeyAttached> encryption_key_attached;
-  EXPECT_CALL(encryption_key_attached, Call(_))
+  EXPECT_CALL(
+      encryption_key_attached,
+      Call(AllOf(Property(&SignedEncryptionInfo::public_asymmetric_key,
+                          Not(IsEmpty())),
+                 Property(&SignedEncryptionInfo::public_key_id, Gt(0)),
+                 Property(&SignedEncryptionInfo::signature, Not(IsEmpty())))))
       .Times(need_encryption_key() ? 1 : 0);
   auto encryption_key_attached_cb =
       base::BindRepeating(&TestEncryptionKeyAttached::Call,
diff --git a/chrome/browser/profiles/profile_browsertest.cc b/chrome/browser/profiles/profile_browsertest.cc
index 58e9e54..11c48ce 100644
--- a/chrome/browser/profiles/profile_browsertest.cc
+++ b/chrome/browser/profiles/profile_browsertest.cc
@@ -48,6 +48,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/version_info/version_info.h"
@@ -947,17 +948,38 @@
 #if !defined(OS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 
 // TODO(https://crbug.com/1125474): Expand to cover ChromeOS.
-IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, ProfileLifetimeTestUnderOneMinute) {
+class GuestProfileLifetimeBrowserTest
+    : public ProfileBrowserTest,
+      public testing::WithParamInterface<bool> {
+ public:
+  GuestProfileLifetimeBrowserTest() : is_ephemeral_(GetParam()) {
+    // Change the value if Ephemeral is not supported.
+    is_ephemeral_ &=
+        TestingProfile::SetScopedFeatureListForEphemeralGuestProfiles(
+            scoped_feature_list_, is_ephemeral_);
+  }
+
+  bool is_ephemeral() const { return is_ephemeral_; }
+
+ private:
+  bool is_ephemeral_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(GuestProfileLifetimeBrowserTest, UnderOneMinute) {
   base::HistogramTester tester;
   Browser* browser = CreateGuestBrowser();
   BrowserCloseObserver close_observer(browser);
 
   BrowserList::CloseAllBrowsersWithProfile(browser->profile());
   close_observer.Wait();
-  tester.ExpectUniqueSample("Profile.Guest.OTR.Lifetime", 0, 1);
+  tester.ExpectUniqueSample("Profile.Guest.OTR.Lifetime", 0,
+                            is_ephemeral() ? 0 : 1);
+  tester.ExpectUniqueSample("Profile.Guest.Ephemeral.Lifetime", 0,
+                            is_ephemeral() ? 1 : 0);
 }
 
-IN_PROC_BROWSER_TEST_F(ProfileBrowserTest, ProfileLifetimeTestOneHour) {
+IN_PROC_BROWSER_TEST_P(GuestProfileLifetimeBrowserTest, OneHour) {
   base::HistogramTester tester;
   Browser* browser = CreateGuestBrowser();
   BrowserCloseObserver close_observer(browser);
@@ -966,9 +988,16 @@
       base::Time::Now() - base::TimeDelta::FromSeconds(60) * 60);
   BrowserList::CloseAllBrowsersWithProfile(browser->profile());
   close_observer.Wait();
-  tester.ExpectUniqueSample("Profile.Guest.OTR.Lifetime", 60, 1);
+  tester.ExpectUniqueSample("Profile.Guest.OTR.Lifetime", 60,
+                            is_ephemeral() ? 0 : 1);
+  tester.ExpectUniqueSample("Profile.Guest.Ephemeral.Lifetime", 60,
+                            is_ephemeral() ? 1 : 0);
 }
 
+INSTANTIATE_TEST_SUITE_P(AllGuestTypes,
+                         GuestProfileLifetimeBrowserTest,
+                         /*is_ephemeral=*/testing::Bool());
+
 class EphemeralGuestProfileBrowserTest : public ProfileBrowserTest {
  public:
   EphemeralGuestProfileBrowserTest() {
@@ -990,30 +1019,6 @@
   EXPECT_TRUE(guest_profile->IsEphemeralGuestProfile());
 }
 
-IN_PROC_BROWSER_TEST_F(EphemeralGuestProfileBrowserTest,
-                       ProfileLifetimeTestUnderOneMinute) {
-  base::HistogramTester tester;
-  Browser* browser = CreateGuestBrowser();
-  BrowserCloseObserver close_observer(browser);
-
-  BrowserList::CloseAllBrowsersWithProfile(browser->profile());
-  close_observer.Wait();
-  tester.ExpectUniqueSample("Profile.Guest.Ephemeral.Lifetime", 0, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(EphemeralGuestProfileBrowserTest,
-                       ProfileLifetimeTestOneHour) {
-  base::HistogramTester tester;
-  Browser* browser = CreateGuestBrowser();
-  BrowserCloseObserver close_observer(browser);
-
-  browser->profile()->SetCreationTimeForTesting(
-      base::Time::Now() - base::TimeDelta::FromSeconds(60) * 60);
-  BrowserList::CloseAllBrowsersWithProfile(browser->profile());
-  close_observer.Wait();
-  tester.ExpectUniqueSample("Profile.Guest.Ephemeral.Lifetime", 60, 1);
-}
-
 // Tests if ephemeral Guest profile paths are persistent as long as one does not
 // close all Guest browsers.
 IN_PROC_BROWSER_TEST_F(EphemeralGuestProfileBrowserTest,
diff --git a/chrome/browser/profiles/profile_keep_alive_types.h b/chrome/browser/profiles/profile_keep_alive_types.h
index 23e6ce6..1979c14 100644
--- a/chrome/browser/profiles/profile_keep_alive_types.h
+++ b/chrome/browser/profiles/profile_keep_alive_types.h
@@ -9,22 +9,29 @@
 
 // Refers to what a ScopedProfileKeepAlive's lifetime is tied to, to help
 // debugging.
+//
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+//
+// Keep this in sync with ProfileKeepAliveOrigin in enums.xml.
 enum class ProfileKeepAliveOrigin {
   // When a Profile gets created by ProfileManager, it initially has this type
   // of keep-alive. This ensures that the Profile has a refcount >=1, at least
   // until RemoveKeepAlive() gets called.
   //
   // When a kBrowserWindow keep-alive gets added, this one gets removed.
-  kWaitingForFirstBrowserWindow,
+  kWaitingForFirstBrowserWindow = 0,
 
   // This Profile has browser windows open.
-  kBrowserWindow,
+  kBrowserWindow = 1,
 
   // This Profile is running extensions with persistent background scripts.
-  kBackgroundMode,
+  kBackgroundMode = 2,
 
   // A child off-the-record profile holds a strong reference to its parent.
-  kOffTheRecordProfile,
+  kOffTheRecordProfile = 3,
+
+  kMaxValue = kOffTheRecordProfile,
 };
 
 std::ostream& operator<<(std::ostream& out,
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index 23380358..b8eb9ecd 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -378,6 +378,20 @@
 
 ProfileManager::~ProfileManager() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (base::FeatureList::IsEnabled(features::kDestroyProfileOnBrowserClose)) {
+    // Ideally, all the keepalives should've been cleared already. Report
+    // metrics for incorrect usage of ScopedProfileKeepAlive.
+    for (const auto& path_and_profile_info : profiles_info_) {
+      const ProfileInfo* profile_info = path_and_profile_info.second.get();
+      for (const auto& origin_and_count : profile_info->keep_alives) {
+        ProfileKeepAliveOrigin origin = origin_and_count.first;
+        int count = origin_and_count.second;
+        if (count > 0) {
+          UMA_HISTOGRAM_ENUMERATION("Profile.KeepAliveLeakAtShutdown", origin);
+        }
+      }
+    }
+  }
 }
 
 // static
@@ -1278,8 +1292,10 @@
 
   int& waiting_for_first_browser_window =
       info->keep_alives[ProfileKeepAliveOrigin::kWaitingForFirstBrowserWindow];
-  if (waiting_for_first_browser_window != 0)
+  if (origin == ProfileKeepAliveOrigin::kBrowserWindow &&
+      waiting_for_first_browser_window != 0) {
     waiting_for_first_browser_window = 0;
+  }
 }
 
 void ProfileManager::RemoveKeepAlive(const Profile* profile,
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index 54206f0..77e8483 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -68,6 +68,10 @@
     ]
   }
 
+  if (enable_pdf) {
+    public_deps += [ "pdf:pdf_resources" ]
+  }
+
   if (enable_print_preview) {
     public_deps += [ "print_preview:print_preview_resources" ]
   }
@@ -178,10 +182,6 @@
   grit("component_extension_resources") {
     source = "component_extension_resources.grd"
 
-    if (enable_pdf) {
-      deps = [ "//chrome/browser/resources/pdf:web_components" ]
-    }
-
     defines = chrome_grit_defines
     if (enable_hangout_services_extension) {
       defines += [ "enable_hangout_services_extension" ]
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index e475112..d56d4e6 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -93,9 +93,6 @@
         <include name="IDR_ARC_SUPPORT_RECOMMEND_APP_LIST_VIEW_JS" file="chromeos/arc_support/recommend_app_list_view.js" type="BINDATA" />
         <include name="IDR_ARC_SUPPORT_RECOMMEND_APP_LIST_VIEW_HTML" file="chromeos/arc_support/recommend_app_list_view.html" type="chrome_html" flattenhtml="true" />
       </if>
-      <if expr="enable_plugins">
-        <part file="pdf/pdf_resources.grdp" />
-      </if>
       <include name="IDR_CRYPTOTOKEN_UTIL_JS" file="cryptotoken/util.js" type="BINDATA" />
       <include name="IDR_CRYPTOTOKEN_B64_JS" file="cryptotoken/b64.js" type="BINDATA" />
       <include name="IDR_CRYPTOTOKEN_COUNTDOWN_JS" file="cryptotoken/countdown.js" type="BINDATA" />
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index da52f99..dd25aa9 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -3,9 +3,122 @@
 # found in the LICENSE file.
 
 import("//build/config/chromeos/ui_mode.gni")
+import("//chrome/common/features.gni")
 import("//pdf/features.gni")
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/grit/grit_rule.gni")
+import("//tools/grit/preprocess_if_expr.gni")
 import("//tools/polymer/html_to_js.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
+
+preprocess_folder = "preprocessed"
+preprocess_manifest = "preprocessed_manifest.json"
+preprocess_gen_manifest = "preprocessed_gen_manifest.json"
+
+assert(enable_pdf, "enable_pdf check failed")
+
+preprocess_if_expr("preprocess") {
+  in_folder = "./"
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_manifest = "$target_gen_dir/$preprocess_manifest"
+  in_files = [
+    "bookmark_type.js",
+    "browser_api.js",
+    "constants.js",
+    "controller.js",
+    "gesture_detector.js",
+    "index.css",
+    "index.html",
+    "local_storage_proxy.js",
+    "main.js",
+    "metrics.js",
+    "navigator.js",
+    "open_pdf_params_parser.js",
+    "pdf_scripting_api.js",
+    "pdf_viewer_base.js",
+    "pdf_viewer_utils.js",
+    "toolbar_manager.js",
+    "viewport.js",
+    "viewport_scroller.js",
+    "zoom_manager.js",
+  ]
+
+  if (is_chromeos) {
+    in_files += [
+      "ink_controller.js",
+      "ink/index.html",
+      "ink/ink_api.js",
+    ]
+  }
+}
+
+preprocess_if_expr("preprocess_generated") {
+  deps = [ ":web_components" ]
+  in_folder = target_gen_dir
+  out_folder = "$target_gen_dir/$preprocess_folder"
+  out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
+  in_files = [
+    "elements/icons.js",
+    "elements/shared-css.js",
+    "elements/shared-vars.js",
+    "elements/viewer-bookmark.js",
+    "elements/viewer-document-outline.js",
+    "elements/viewer-download-controls.js",
+    "elements/viewer-error-screen.js",
+    "elements/viewer-page-selector.js",
+    "elements/viewer-password-screen.js",
+    "elements/viewer-pdf-sidenav.js",
+    "elements/viewer-pdf-toolbar.js",
+    "elements/viewer-pdf-toolbar-new.js",
+    "elements/viewer-properties-dialog.js",
+    "elements/viewer-thumbnail-bar.js",
+    "elements/viewer-thumbnail.js",
+    "elements/viewer-toolbar-dropdown.js",
+    "elements/viewer-zoom-button.js",
+    "elements/viewer-zoom-toolbar.js",
+    "pdf_viewer.js",
+    "pdf_viewer_shared_style.js",
+  ]
+  if (is_chromeos) {
+    in_files += [
+      "elements/viewer-annotations-bar.js",
+      "elements/viewer-annotations-mode-dialog.js",
+      "elements/viewer-form-warning.js",
+      "elements/viewer-ink-host.js",
+      "elements/viewer-pen-options.js",
+    ]
+  }
+}
+
+generate_grd("build_grd") {
+  deps = [
+    ":preprocess",
+    ":preprocess_generated",
+  ]
+  manifest_files = [
+    "$target_gen_dir/$preprocess_manifest",
+    "$target_gen_dir/$preprocess_gen_manifest",
+  ]
+  grd_prefix = "pdf"
+  out_grd = "$target_gen_dir/resources.grd"
+  resource_path_prefix = "pdf"
+}
+
+grit("pdf_resources") {
+  defines = chrome_grit_defines
+
+  enable_input_discovery_for_gn_analyze = false
+  source = "$target_gen_dir/resources.grd"
+  deps = [ ":build_grd" ]
+
+  outputs = [
+    "grit/pdf_resources.h",
+    "grit/pdf_resources_map.cc",
+    "grit/pdf_resources_map.h",
+    "pdf_resources.pak",
+  ]
+  output_dir = "$root_gen_dir/chrome"
+}
 
 assert(enable_pdf, "enable_pdf check failed")
 
@@ -26,7 +139,7 @@
 
 group("closure_compile") {
   deps = [
-    ":pdf_resources",
+    ":closure_compile_local",
     "elements:closure_compile",
   ]
   if (is_chromeos_ash) {
@@ -220,7 +333,7 @@
   ]
 }
 
-js_type_check("pdf_resources") {
+js_type_check("closure_compile_local") {
   is_polymer3 = true
   deps = [
     ":annotation_tool",
diff --git a/chrome/browser/resources/pdf/pdf_resources.grdp b/chrome/browser/resources/pdf/pdf_resources.grdp
deleted file mode 100644
index 1016c65f..0000000
--- a/chrome/browser/resources/pdf/pdf_resources.grdp
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<grit-part>
-  <!-- Note that resources included here that are used in Print Preview
-       also must be included in print_preview_ui.cc such these resources
-       will be exposed to PDF in print preview. -->
-  <include name="IDR_PDF_INDEX_HTML" file="pdf/index.html" type="BINDATA" preprocess="true" />
-  <include name="IDR_PDF_INDEX_CSS" file="pdf/index.css" type="BINDATA" />
-  <include name="IDR_PDF_MAIN_JS" file="pdf/main.js" type="BINDATA" preprocess="true" />
-  <include name="IDR_PDF_PDF_VIEWER_UTILS_JS" file="pdf/pdf_viewer_utils.js" type="BINDATA" />
-  <include name="IDR_PDF_PDF_VIEWER_BASE_JS" file="pdf/pdf_viewer_base.js" type="BINDATA" />
-  <include name="IDR_PDF_PDF_VIEWER_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/pdf_viewer.js" use_base_dir="false" type="BINDATA" preprocess="true" />
-  <include name="IDR_PDF_PDF_VIEWER_SHARED_STYLE_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/pdf_viewer_shared_style.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_BOOKMARK_TYPE_JS" file="pdf/bookmark_type.js" type="BINDATA" />
-  <include name="IDR_PDF_CONSTANTS_JS" file="pdf/constants.js" type="BINDATA" />
-  <include name="IDR_PDF_CONTROLLER_JS" file="pdf/controller.js" type="BINDATA" />
-  <include name="IDR_PDF_TOOLBAR_MANAGER_JS" file="pdf/toolbar_manager.js" type="BINDATA" />
-  <include name="IDR_PDF_VIEWPORT_JS" file="pdf/viewport.js" type="BINDATA" />
-  <include name="IDR_PDF_OPEN_PDF_PARAMS_PARSER_JS" file="pdf/open_pdf_params_parser.js" type="BINDATA" />
-  <include name="IDR_PDF_NAVIGATOR_JS" file="pdf/navigator.js" type="BINDATA" />
-  <include name="IDR_PDF_VIEWPORT_SCROLLER_JS" file="pdf/viewport_scroller.js" type="BINDATA" />
-  <include name="IDR_PDF_PDF_SCRIPTING_API_JS" file="pdf/pdf_scripting_api.js" type="BINDATA" />
-  <include name="IDR_PDF_ZOOM_MANAGER_JS" file="pdf/zoom_manager.js" type="BINDATA" />
-  <include name="IDR_PDF_GESTURE_DETECTOR_JS" file="pdf/gesture_detector.js" type="BINDATA" />
-  <include name="IDR_PDF_BROWSER_API_JS" file="pdf/browser_api.js" type="BINDATA" />
-  <include name="IDR_PDF_METRICS_JS" file="pdf/metrics.js" type="BINDATA" />
-  <include name="IDR_PDF_LOCAL_STORAGE_PROXY_JS" file="pdf/local_storage_proxy.js" type="BINDATA" />
-  <include name="IDR_PDF_ELEMENTS_SHARED_CSS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/shared-css.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_SHARED_VARS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/shared-vars.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_ICONS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/icons.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_BOOKMARK_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-bookmark.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_DOCUMENT_OUTLINE_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-document-outline.js" use_base_dir="false" type="BINDATA"/>
-  <include name="IDR_PDF_VIEWER_DOWNLOADS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-download-controls.js" use_base_dir="false" type="BINDATA"/>
-  <include name="IDR_PDF_VIEWER_ERROR_SCREEN_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-error-screen.js" use_base_dir="false" type="BINDATA" />
-  <if expr="chromeos">
-    <include name="IDR_PDF_VIEWER_ANNOTATIONS_MODE_DIALOG_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-annotations-mode-dialog.js" use_base_dir="false" type="BINDATA"/>
-    <include name="IDR_PDF_VIEWER_ANNOTATIONS_BAR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-annotations-bar.js" use_base_dir="false" type="BINDATA"/>
-    <include name="IDR_PDF_VIEWER_INK_CONTROLLER_JS" file="pdf/ink_controller.js" type="BINDATA" />
-    <include name="IDR_PDF_VIEWER_INK_INDEX_HTML" file="pdf/ink/index.html" type="BINDATA" />
-    <include name="IDR_PDF_VIEWER_INK_INK_API_JS" file="pdf/ink/ink_api.js" type="BINDATA" />
-    <include name="IDR_PDF_VIEWER_INK_HOST_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-ink-host.js" use_base_dir="false" type="BINDATA" />
-    <include name="IDR_PDF_VIEWER_PEN_OPTIONS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pen-options.js" use_base_dir="false" type="BINDATA" />
-    <include name="IDR_PDF_VIEWER_FORM_WARNING_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-form-warning.js" use_base_dir="false" type="BINDATA" />
-  </if>
-  <include name="IDR_PDF_VIEWER_PAGE_SELECTOR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-page-selector.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_PASSWORD_SCREEN_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-password-screen.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_PDF_SIDENAV_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.js" use_base_dir="false" type="BINDATA"/>
-  <include name="IDR_PDF_VIEWER_PDF_TOOLBAR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js" use_base_dir="false" type="BINDATA" preprocess="true"/>
-  <include name="IDR_PDF_VIEWER_PDF_TOOLBAR_NEW_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js" use_base_dir="false" type="BINDATA" preprocess="true"/>
-  <include name="IDR_PDF_VIEWER_PROPERTIES_DIALOG_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-properties-dialog.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_THUMBNAIL_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-thumbnail.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_THUMBNAIL_BAR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-thumbnail-bar.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_ZOOM_BUTTON_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-zoom-button.js" use_base_dir="false" type="BINDATA" />
-  <include name="IDR_PDF_VIEWER_ZOOM_SELECTOR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-zoom-toolbar.js" use_base_dir="false" type="BINDATA" />
-</grit-part>
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_page.html b/chrome/browser/resources/settings/chromeos/internet_page/internet_page.html
index a0ab4d6b..b8e4fca 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_page.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_page.html
@@ -71,13 +71,13 @@
                       aria-labelledby="add-wifi-label"></cr-icon-button>
                 </div>
               </template>
-              <div actionable$="[[vpnIsEnabled_]]" class="list-item"
+              <div actionable$="[[!vpnIsProhibited_]]" class="list-item"
                   on-click="onAddVPNTap_">
                 <div class="start settings-box-text"
                     id="add-vpn-label" aria-hidden="true">
                   $i18n{internetAddVPN}
                 </div>
-                <template is="dom-if" if="[[!vpnIsEnabled_]]">
+                <template is="dom-if" if="[[vpnIsProhibited_]]">
                   <cr-policy-indicator id="vpnPolicyIndicator"
                       icon-aria-label="$i18n{networkVpnBuiltin}"
                       indicator-type="devicePolicy"
@@ -86,7 +86,7 @@
                 </template>
                 <cr-icon-button class="icon-add-circle"
                     aria-labelledby="add-vpn-label"
-                    disabled="[[!vpnIsEnabled_]]">
+                    disabled="[[vpnIsProhibited_]]">
                 </cr-icon-button>
               </div>
               <template is="dom-repeat" items="[[vpnProviders_]]">
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
index cede6b2..ad4acad 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_page.js
@@ -81,10 +81,10 @@
     },
 
     /**
-     * False if VPN is disabled by policy.
+     * True if VPN is prohibited by policy.
      * @private {boolean}
      */
-    vpnIsEnabled_: {
+    vpnIsProhibited_: {
       type: Boolean,
       value: false,
     },
@@ -502,9 +502,9 @@
     }
 
     const vpn = this.deviceStates[mojom.NetworkType.kVPN];
-    this.vpnIsEnabled_ = !!vpn &&
+    this.vpnIsProhibited_ = !!vpn &&
         vpn.deviceState ===
-            chromeos.networkConfig.mojom.DeviceStateType.kEnabled;
+            chromeos.networkConfig.mojom.DeviceStateType.kProhibited;
 
     if (this.detailType_ && !this.deviceStates[this.detailType_]) {
       // If the device type associated with the current network has been
@@ -541,7 +541,7 @@
 
   /** @private */
   onAddVPNTap_() {
-    if (this.vpnIsEnabled_) {
+    if (!this.vpnIsProhibited_) {
       this.showConfig_(
           true /* configAndConnect */,
           chromeos.networkConfig.mojom.NetworkType.kVPN);
diff --git a/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.html b/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.html
index ab7a811e..b904f6c5 100644
--- a/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.html
+++ b/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.html
@@ -165,7 +165,8 @@
     </div>
   </div>
   <!-- TODO(https://crbug.com/1157764): Change to make only link clickable. -->
-  <template is="dom-if" if="[[shouldShowGuest_]]" restamp>
+  <template is="dom-if" if="[[interceptionParameters_.showGuestOption]]"
+      restamp>
     <div slot="footer">
       <div class="divider"></div>
       <div id="footer-description" on-click="onGuest_"
diff --git a/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.js b/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.js
index de500aa..9c0c79a 100644
--- a/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.js
+++ b/chrome/browser/resources/signin/dice_web_signin_intercept/dice_web_signin_intercept_app.js
@@ -35,14 +35,6 @@
       value: false,
     },
 
-    /** @private {boolean} */
-    shouldShowGuest_: {
-      type: Boolean,
-      value() {
-        return loadTimeData.getBoolean('shouldShowGuest');
-      },
-    },
-
     /** @private {string} */
     guestLink_: {
       type: String,
diff --git a/chrome/browser/resources/signin/sync_confirmation/images/sync_confirmation_refreshed_illustration.svg b/chrome/browser/resources/signin/sync_confirmation/images/sync_confirmation_refreshed_illustration.svg
new file mode 100644
index 0000000..0faf745
--- /dev/null
+++ b/chrome/browser/resources/signin/sync_confirmation/images/sync_confirmation_refreshed_illustration.svg
@@ -0,0 +1 @@
+<svg width="678" height="180" viewBox="0 0 678 180" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M275.704 64.913c0 3.852-3.185 7.037-7.111 6.963-3.853 0-7.038-3.185-6.964-7.112.074-3.926 3.186-7.037 7.112-6.963 3.926.074 7.037 3.185 6.963 7.112z" stroke="#8AB4F8" stroke-width="2"/><path d="M483.646 86.322c2 0 3.704-1.63 3.704-3.63s-1.63-3.704-3.63-3.704-3.704 1.63-3.704 3.63c-.074 2.074 1.556 3.704 3.63 3.704z" fill="#FBBC04"/><path d="M367.637 59.648c6.519.074 11.926-5.185 11.926-11.778 0-6.593-5.185-11.927-11.778-11.927-6.519-.074-11.927 5.186-11.927 11.779s5.186 11.852 11.779 11.926z" fill="#4285F4"/><path d="M461.491 84.54l9.63-18.594c2.592-4.816.666-10.89-4.149-13.409-4.815-2.592-10.889-.666-13.408 4.149l-9.704 18.593c-2.593 4.816-.667 10.89 4.148 13.409 4.964 2.592 10.89.666 13.483-4.149z" stroke="#34A853" stroke-width="2"/><path d="M311.121 71.321l-.004-.002-18.435-11.402c-1.273-.836-1.674-2.457-.874-3.737l.002-.004 11.402-18.435c.836-1.273 2.457-1.674 3.737-.874l.004.003 18.435 11.4c1.248.822 1.619 2.512.87 3.746l-.002.003-11.398 18.428c-.836 1.273-2.457 1.674-3.737.874z" stroke="#F0A3F8" stroke-width="2"/><path d="M330.298 40.543c.815-.815 2.222-.37 2.445.741l3.185 13.705c.296 1.11-.741 2.074-1.852 1.777l-13.482-4.074c-1.111-.296-1.408-1.704-.593-2.519l10.297-9.63z" fill="#5BB974"/><path d="M324.15 46.395l1.778 1.11c1.704 1.112 2.297 3.334 1.186 5.112l-1.038 1.704" stroke="#FDD663" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M406.231 66.248c.815-.074 1.63-.222 2.371-.518l2.518-.519c2.223-.518 4.593-.074 6.519 1.186l2.445 1.555 1.111.667c.296.148.593.222.889.37 3.852 1.26 7.926-.889 9.186-4.74 1.259-3.853-.889-7.927-4.741-9.186l-.889-.223-1.26-.148-2.963-.148c-2.296-.074-4.444-1.111-6-2.815l-1.778-1.926c-.444-.667-1.037-1.26-1.63-1.778a9.988 9.988 0 0 0-4.593-2.296c-4.963-1.111-10.074 1.63-11.926 6.37-2.222 5.63.889 11.927 6.519 13.705 1.407.444 2.889.593 4.222.444z" stroke="#8AB4F8" stroke-width="2"/><path d="M516.488 156.568l-25.483-12.519a6.08 6.08 0 0 1-2.815-8.148l32.742-66.819a6.08 6.08 0 0 1 8.149-2.815l25.483 12.52a6.08 6.08 0 0 1 2.815 8.148l-32.743 66.818a6.078 6.078 0 0 1-8.148 2.815z" stroke="#4285F4" stroke-width="2" stroke-linecap="round"/><path d="M517.53 153.17l-24.594-12.001c-1.408-.667-2-2.444-1.334-3.852l30.817-62.966c.667-1.408 2.444-2 3.852-1.334l24.594 12.075c1.407.667 2 2.445 1.333 3.852l-30.816 62.892c-.667 1.482-2.371 2.075-3.852 1.334z" fill="#D2E3FC"/><path d="M537.448 73.632l-.62 1.264 9.246 4.532.619-1.264-9.245-4.532zm-1.805-.106a.666.666 0 1 0-1.196-.587.666.666 0 0 0 1.196.587zm14.902 31.694l-1.929-.945-5.51 11.241 1.929.945 5.51-11.241zm5.575-11.467l-1.929-.946-3.195 6.519 1.929.945 3.195-6.518z" fill="#4285F4"/><path d="M516.634 138.052c3.482 1.704 7.704.296 9.408-3.186 1.704-3.481.296-7.704-3.185-9.407-3.482-1.704-7.704-.297-9.408 3.185-1.704 3.482-.297 7.704 3.185 9.408z" fill="#FBBC04"/><path d="M515.746 146.727l-5.408-2.667c-1.481-.741-2.074-2.592-1.333-4 .74-1.482 2.592-2.074 4-1.333l5.408 2.666c1.481.741 2.074 2.593 1.333 4.001-.667 1.407-2.445 2.074-4 1.333z" fill="#4285F4"/><path d="M499.227 128.131c-1.556 4.519.148 9.63 4.148 12.297.667.445 1.556.222 1.852-.518l3.556-7.26c.296-.667.074-1.408-.593-1.704l-7.259-3.556c-.593-.37-1.482 0-1.704.741z" fill="#34A853"/><path d="M248.32 117.005l-101.709 37.262c-1.334.518-2.889-.223-3.334-1.556l-23.853-64.818c-.518-1.334.222-2.89 1.556-3.334l101.709-37.261c1.333-.519 2.889.222 3.334 1.555l23.779 64.819c.592 1.333-.149 2.815-1.482 3.333z" stroke="#4285F4" stroke-width="2" stroke-linecap="round"/><path d="M222.533 52.028l-98.277 36.029 22.514 61.414 98.277-36.028-22.514-61.415z" fill="#D2E3FC"/><path d="M172.98 69.522a.592.592 0 1 0 0-1.185.592.592 0 0 0 0 1.185z" fill="#4285F4"/><path d="M141.575 161.375l57.707-21.186 57.781-21.187" stroke="#4285F4" stroke-width="2" stroke-linecap="round"/><path d="M167.717 126.557c4.371-1.629 6.593-6.444 4.963-10.741-1.629-4.371-6.444-6.593-10.741-4.963-4.37 1.63-6.593 6.445-4.963 10.741 1.63 4.297 6.445 6.519 10.741 4.963z" fill="#FBBC04"/><path d="M174.461 134.339l-6.741 2.519c-1.852.667-3.926-.296-4.593-2.148-.667-1.852.296-3.927 2.148-4.593l6.742-2.519c1.851-.667 3.926.296 4.592 2.148.667 1.852-.222 3.927-2.148 4.593z" fill="#4285F4"/><path d="M144.827 133.367c2.667 5.038 8.445 7.779 14.075 6.519.889-.222 1.407-1.185 1.111-2.074l-3.334-9.038c-.296-.814-1.185-1.185-2-.888l-9.037 3.333c-.815.296-1.26 1.333-.815 2.148z" fill="#34A853"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/signin/sync_confirmation/images/sync_confirmation_refreshed_illustration_dark.svg b/chrome/browser/resources/signin/sync_confirmation/images/sync_confirmation_refreshed_illustration_dark.svg
new file mode 100644
index 0000000..259c96e
--- /dev/null
+++ b/chrome/browser/resources/signin/sync_confirmation/images/sync_confirmation_refreshed_illustration_dark.svg
@@ -0,0 +1 @@
+<svg width="678" height="180" viewBox="0 0 678 180" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M275.704 64.913c0 3.852-3.185 7.037-7.111 6.963-3.853 0-7.038-3.185-6.964-7.112.074-3.926 3.186-7.037 7.112-6.963 3.926.074 7.037 3.185 6.963 7.112z" stroke="#8AB4F8" stroke-width="2"/><path d="M483.646 86.322c2.001 0 3.704-1.63 3.704-3.63s-1.629-3.704-3.63-3.704c-2 0-3.703 1.63-3.703 3.63-.074 2.074 1.555 3.704 3.629 3.704z" fill="#FBBC04"/><path d="M367.637 59.648c6.519.074 11.926-5.185 11.926-11.778 0-6.593-5.185-11.927-11.778-11.927-6.519-.074-11.927 5.186-11.927 11.779s5.186 11.852 11.779 11.926z" fill="#4285F4"/><path d="M461.491 84.54l9.63-18.594c2.593-4.816.667-10.89-4.148-13.409-4.816-2.592-10.89-.666-13.409 4.149l-9.704 18.593c-2.593 4.816-.667 10.89 4.149 13.409 4.963 2.592 10.889.666 13.482-4.149z" stroke="#34A853" stroke-width="2"/><path d="M311.121 71.321l-.004-.002-18.435-11.402c-1.273-.836-1.674-2.457-.874-3.737l.002-.004 11.402-18.435c.836-1.273 2.457-1.674 3.737-.874l.004.003 18.435 11.4c1.247.822 1.619 2.512.869 3.746l-.001.003-11.398 18.428c-.836 1.273-2.457 1.674-3.737.874z" stroke="#F0A3F8" stroke-width="2"/><path d="M330.298 40.543c.815-.815 2.222-.37 2.445.741l3.185 13.705c.296 1.11-.741 2.074-1.852 1.777l-13.482-4.074c-1.111-.296-1.408-1.704-.593-2.519l10.297-9.63z" fill="#5BB974"/><path d="M324.15 46.394l1.778 1.111c1.704 1.112 2.297 3.334 1.186 5.112l-1.038 1.704" stroke="#FDD663" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/><path d="M406.231 66.249c.815-.075 1.63-.223 2.371-.519l2.518-.519c2.223-.518 4.593-.074 6.519 1.186l2.445 1.555 1.111.667c.296.148.593.222.889.37 3.852 1.26 7.926-.889 9.186-4.74 1.259-3.853-.889-7.927-4.741-9.186l-.889-.223-1.26-.148-2.963-.148c-2.296-.074-4.444-1.11-6-2.815l-1.778-1.926c-.444-.666-1.037-1.26-1.63-1.778a9.989 9.989 0 0 0-4.593-2.296c-4.963-1.111-10.074 1.63-11.926 6.37-2.222 5.63.889 11.927 6.519 13.705 1.407.445 2.889.593 4.222.445z" stroke="#8AB4F8" stroke-width="2"/><path d="M516.488 156.569l-25.483-12.52a6.08 6.08 0 0 1-2.815-8.148l32.743-66.819a6.08 6.08 0 0 1 8.148-2.815l25.483 12.52a6.08 6.08 0 0 1 2.815 8.148l-32.743 66.819a6.08 6.08 0 0 1-8.148 2.815z" stroke="#4285F4" stroke-width="2" stroke-linecap="round"/><path d="M517.53 153.17l-24.594-12.001c-1.408-.667-2-2.444-1.334-3.852l30.817-62.966c.666-1.408 2.444-2 3.852-1.334l24.594 12.075c1.407.667 2 2.445 1.333 3.852l-30.816 62.892c-.667 1.482-2.371 2.075-3.852 1.334z" fill="#D2E3FC"/><path d="M537.448 73.632l-.619 1.264 9.245 4.532.62-1.264-9.246-4.532zm-1.805-.105a.667.667 0 1 0-1.198-.587.667.667 0 0 0 1.198.587zm14.902 31.694l-1.929-.946-5.51 11.241 1.929.946 5.51-11.241zm5.575-11.469l-1.929-.945-3.195 6.518 1.929.946 3.195-6.519z" fill="#4285F4"/><path d="M516.634 138.052c3.482 1.703 7.704.296 9.408-3.186 1.704-3.481.296-7.704-3.185-9.408-3.482-1.704-7.704-.296-9.408 3.186-1.704 3.481-.297 7.704 3.185 9.408z" fill="#FBBC04"/><path d="M515.746 146.727l-5.408-2.667c-1.481-.74-2.074-2.592-1.333-4 .741-1.481 2.593-2.074 4-1.333l5.408 2.667c1.481.74 2.074 2.592 1.333 4-.666 1.407-2.444 2.074-4 1.333z" fill="#4285F4"/><path d="M499.227 128.131c-1.556 4.519.148 9.63 4.148 12.297.667.444 1.556.222 1.852-.519l3.556-7.259c.296-.667.074-1.408-.593-1.704l-7.259-3.556c-.593-.37-1.482 0-1.704.741z" fill="#34A853"/><path d="M248.32 117.005l-101.709 37.261c-1.334.519-2.889-.222-3.334-1.555l-23.853-64.819c-.518-1.333.222-2.889 1.556-3.333l101.709-37.261c1.333-.519 2.889.222 3.334 1.555l23.779 64.819c.592 1.333-.149 2.815-1.482 3.333z" stroke="#4285F4" stroke-width="2" stroke-linecap="round"/><path d="M222.533 52.028l-98.277 36.028 22.514 61.415 98.277-36.029-22.514-61.414z" fill="#D2E3FC"/><path d="M172.98 69.521a.592.592 0 1 0 0-1.184.592.592 0 0 0 0 1.184z" fill="#4285F4"/><path d="M141.575 161.375l57.707-21.186 57.781-21.187" stroke="#4285F4" stroke-width="2" stroke-linecap="round"/><path d="M167.717 126.557c4.371-1.63 6.593-6.445 4.963-10.741-1.629-4.371-6.444-6.593-10.741-4.963-4.37 1.629-6.593 6.444-4.963 10.741 1.63 4.296 6.445 6.519 10.741 4.963z" fill="#FBBC04"/><path d="M174.461 134.339l-6.741 2.519c-1.852.667-3.926-.296-4.593-2.148-.667-1.852.296-3.927 2.148-4.593l6.742-2.519c1.851-.667 3.926.296 4.592 2.148.667 1.852-.222 3.927-2.148 4.593z" fill="#4285F4"/><path d="M144.827 133.367c2.667 5.038 8.445 7.779 14.075 6.519.889-.222 1.407-1.185 1.111-2.074l-3.334-9.038c-.296-.814-1.185-1.185-2-.888l-9.037 3.333c-.815.296-1.26 1.333-.815 2.148z" fill="#34A853"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
index 2141cd97..c4d55121 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation.html
@@ -7,8 +7,8 @@
     <style>
       body {
         margin: 0;
+        min-width: 512px;
         padding: 0;
-        width: 512px;
       }
       @media (prefers-color-scheme: dark) {
         body {
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.html b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.html
index 3cc7d304..9eda3a29 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.html
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.html
@@ -1,10 +1,33 @@
 <style include="signin-dialog-shared">
   :host {
+    --avatar-size: 100px;
+    --banner-height: 244px;
+    --footer-margin: 40px;
+    --text-font-size: 1.16em;
     color: var(--cr-primary-text-color);
     display: block;
   }
 
-  cr-button {
+  @media (prefers-color-scheme: dark) {
+    /* Old design */
+    #illustration {
+      background-image:
+          url(./images/sync_confirmation_illustration_dark.svg);
+    }
+
+    /* New design */
+    #syncPromoBanner {
+      background-image:
+          url(images/sync_confirmation_refreshed_illustration_dark.svg);
+    }
+  }
+
+  .secondary {
+    color: var(--cr-secondary-text-color);
+  }
+
+  /* Old design */
+  .action-container cr-button {
     padding-inline-end: 16px;
     padding-inline-start: 16px;
   }
@@ -32,13 +55,6 @@
     width: 100%;
   }
 
-  @media (prefers-color-scheme: dark) {
-    #illustration {
-      background-image:
-          url(./images/sync_confirmation_illustration_dark.svg);
-    }
-  }
-
   #illustration-container > img {
     border-radius: 50%;
     height: 68px;
@@ -70,10 +86,6 @@
     padding: 0 24px;
   }
 
-  .secondary {
-    color: var(--cr-secondary-text-color);
-  }
-
   @media (prefers-color-scheme: light) {
     #grey-banner {
       background: var(--paper-grey-50);
@@ -83,20 +95,107 @@
     }
   }
 
-  #footer {
+  #old-footer {
     margin-bottom: 0;
     padding-top: 12px;
   }
 
-  #settingsButton {
+  .action-container #settingsButton {
     left: 16px;
     position: absolute;
   }
 
-  :host-context([dir='rtl']) #settingsButton {
+  :host-context([dir='rtl']) .action-container #settingsButton {
     left: auto;
     right: 16px;
   }
+
+  /* New design */
+  #headerContainer {
+    background-color: var(--theme-frame-color);
+    height: var(--banner-height);
+    position: relative;
+    width: 100%;
+  }
+
+  #syncPromoBanner {
+    background-image: url(images/sync_confirmation_refreshed_illustration.svg);
+    background-position: center;
+    background-repeat: no-repeat;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    width: 100%;
+    z-index: 0;
+  }
+
+  #avatar {
+    border: 2px solid var(--md-background-color);
+    border-radius: 50%;
+    bottom: calc(var(--avatar-size)/-2);
+    height: var(--avatar-size);
+    left: 0;
+    margin: auto;
+    position: absolute;
+    right: 0;
+    width: var(--avatar-size);
+    z-index: 1;
+  }
+
+  #contentContainer {
+    margin-bottom: calc(48px + var(--footer-margin));
+    margin-top: 104px;
+    text-align: center;
+  }
+
+  #contentContainer h2 {
+    font-size: 1.85em;
+    font-weight: normal;
+  }
+
+  #contentContainer h3 {
+    font-size: var(--text-font-size);
+    font-weight: normal;
+  }
+
+  #confirmationDescription {
+    font-size: 1em;
+    margin-top: 16px;
+  }
+
+  #footer {
+    bottom: 0;
+    display: flex;
+    font-size: var(--text-font-size);
+    margin-bottom: var(--footer-margin);
+    position: absolute;
+    width: 100%;
+  }
+
+  #actionContainer {
+    display: flex;
+    flex-flow: row-reverse;
+    justify-content: flex-start;
+    width: 100%;
+  }
+
+  #actionContainer cr-button {
+    min-width: 111px;
+  }
+
+  #actionContainer #settingsButton {
+    font-weight: normal;
+    margin-inline-end: auto;
+    margin-inline-start: var(--footer-margin);
+  }
+
+  #actionContainer #confirmButton {
+    margin-inline-end: var(--footer-margin);
+  }
+
+  #actionContainer #notNowButton {
+    font-weight: normal;
+  }
 </style>
 
 <!--
@@ -106,35 +205,71 @@
   which user clicks to indicate consent.
 -->
 
-<div id="illustration-container">
-  <div id="grey-banner"></div>
-  <div id="illustration"></div>
-  <img src="[[accountImageSrc_]]">
-</div>
-<div id="content-container">
-  <h1 id="syncConfirmationHeading" class="heading" consent-description>
-    $i18n{syncConfirmationTitle}
-  </h1>
-  <div class="message-container">
-    <div consent-description>$i18n{syncConfirmationSyncInfoTitle}</div>
-    <div class="secondary" consent-description>
+<template is="dom-if" if="[[!isProfileCreationFlow_]]">
+  <div id="illustration-container">
+    <div id="grey-banner"></div>
+    <div id="illustration"></div>
+    <img src="[[accountImageSrc_]]">
+  </div>
+  <div id="content-container">
+    <h1 id="syncConfirmationHeading" class="heading" consent-description>
+      $i18n{syncConfirmationTitle}
+    </h1>
+    <div class="message-container">
+      <div consent-description>$i18n{syncConfirmationSyncInfoTitle}</div>
+      <div class="secondary" consent-description>
+        $i18n{syncConfirmationSyncInfoDesc}
+      </div>
+    </div>
+    <div id="old-footer" class="message-container secondary">
+      $i18n{syncConfirmationSettingsInfo}
+    </div>
+    <div class="action-container">
+      <cr-button class="action-button" id="confirmButton"
+          on-click="onConfirm_" consent-confirmation autofocus>
+        $i18n{syncConfirmationConfirmLabel}
+      </cr-button>
+      <cr-button id="cancelButton" on-click="onUndo_">
+        $i18n{syncConfirmationUndoLabel}
+      </cr-button>
+      <cr-button id="settingsButton" on-click="onGoToSettings_"
+          consent-confirmation>
+        $i18n{syncConfirmationSettingsLabel}
+      </cr-button>
+    </div>
+  </div>
+</template>
+
+<template is="dom-if" if="[[isProfileCreationFlow_]]">
+  <div id="headerContainer" style$="--theme-frame-color:[[highlightColor_]]">
+    <div id="syncPromoBanner"></div>
+    <img id="avatar" alt="" src="[[accountImageSrc_]]">
+  </div>
+
+  <div id="contentContainer">
+    <h2 consent-description id="syncConfirmationHeading">
+      $i18n{syncConfirmationTitle}
+    </h2>
+    <h3 class="secondary" consent-description>
+      $i18n{syncConfirmationSyncInfoTitle}
+    </h3>
+    <div id="confirmationDescription" class="secondary" consent-description>
       $i18n{syncConfirmationSyncInfoDesc}
     </div>
   </div>
-  <div id="footer" class="message-container secondary">
-    $i18n{syncConfirmationSettingsInfo}
+  <div id="footer">
+    <div id="actionContainer">
+      <cr-button class="action-button" id="confirmButton"
+          on-click="onConfirm_" consent-confirmation autofocus>
+        $i18n{syncConfirmationConfirmLabel}
+      </cr-button>
+      <cr-button id="notNowButton" on-click="onUndo_">
+        $i18n{syncConfirmationUndoLabel}
+      </cr-button>
+      <cr-button id="settingsButton" on-click="onGoToSettings_"
+          consent-confirmation>
+        $i18n{syncConfirmationSettingsLabel}
+      </cr-button>
+    </div>
   </div>
-  <div class="action-container">
-    <cr-button class="action-button" id="confirmButton"
-        on-click="onConfirm_" consent-confirmation autofocus>
-      $i18n{syncConfirmationConfirmLabel}
-    </cr-button>
-    <cr-button id="cancelButton" on-click="onUndo_">
-      $i18n{syncConfirmationUndoLabel}
-    </cr-button>
-    <cr-button id="settingsButton" on-click="onGoToSettings_"
-        consent-confirmation>
-      $i18n{syncConfirmationSettingsLabel}
-    </cr-button>
-  </div>
-</div>
+</template>
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.js b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.js
index 83d2ccc..2dc96fe 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.js
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.js
@@ -31,6 +31,26 @@
         return loadTimeData.getString('accountPictureUrl');
       },
     },
+
+    /** @private */
+    isProfileCreationFlow_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('isProfileCreationFlow');
+      }
+    },
+
+    /** @private */
+    highlightColor_: {
+      type: String,
+      value() {
+        if (!loadTimeData.valueExists('highlightColor')) {
+          return '';
+        }
+
+        return loadTimeData.getString('highlightColor');
+      }
+    },
   },
 
   /** @private {?SyncConfirmationBrowserProxy} */
@@ -85,7 +105,7 @@
         Array.from(this.shadowRoot.querySelectorAll('[consent-description]'))
             .filter(element => element.clientWidth * element.clientHeight > 0)
             .map(element => element.innerHTML.trim());
-    assert(consentDescription);
+    assert(consentDescription.length);
     return consentDescription;
   },
 
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index e3d79cc..8e22a9f 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -170,8 +170,8 @@
     signin::IdentityManager* identity_manager =
         IdentityManagerFactory::GetForProfile(profile);
     if (!profile->IsOffTheRecord() && identity_manager) {
-      token_fetcher_ =
-          std::make_unique<SafeBrowsingTokenFetcher>(identity_manager);
+      token_fetcher_ = std::make_unique<SafeBrowsingPrimaryAccountTokenFetcher>(
+          identity_manager);
     }
   }
 }
@@ -461,7 +461,6 @@
   if (is_enhanced_protection_ && token_fetcher_ &&
       base::FeatureList::IsEnabled(kDownloadRequestWithToken)) {
     token_fetcher_->Start(
-        signin::ConsentLevel::kNotRequired,
         base::BindOnce(&CheckClientDownloadRequestBase::OnGotAccessToken,
                        GetWeakPtr()));
     return;
@@ -471,9 +470,8 @@
 }
 
 void CheckClientDownloadRequestBase::OnGotAccessToken(
-    base::Optional<signin::AccessTokenInfo> access_token_info) {
-  if (access_token_info.has_value())
-    access_token_ = access_token_info.value().token;
+    const std::string& access_token) {
+  access_token_ = access_token;
   SendRequest();
 }
 
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
index 71b3a0e1..6d039065 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.h
@@ -26,7 +26,7 @@
 #include "chrome/services/file_util/public/cpp/sandboxed_rar_analyzer.h"
 #include "chrome/services/file_util/public/cpp/sandboxed_zip_analyzer.h"
 #include "components/history/core/browser/history_service.h"
-#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
+#include "components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h"
 #include "components/safe_browsing/core/db/database_manager.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/gurl.h"
@@ -153,8 +153,7 @@
       DownloadCheckResultReason reason) const = 0;
 
   // Called when |token_fetcher_| has finished fetching the access token.
-  void OnGotAccessToken(
-      base::Optional<signin::AccessTokenInfo> access_token_info);
+  void OnGotAccessToken(const std::string& access_token);
 
   // Called at the request start to determine if we should bailout due to the
   // file being whitelisted by policy
diff --git a/chrome/browser/sharesheet/sharesheet_action_cache.cc b/chrome/browser/sharesheet/sharesheet_action_cache.cc
index b75e1cc0..b2c80e8 100644
--- a/chrome/browser/sharesheet/sharesheet_action_cache.cc
+++ b/chrome/browser/sharesheet/sharesheet_action_cache.cc
@@ -38,7 +38,6 @@
     AddShareAction(std::make_unique<ExampleAction>());
     AddShareAction(std::make_unique<ExampleAction>());
     AddShareAction(std::make_unique<ExampleAction>());
-    AddShareAction(std::make_unique<ExampleAction>());
   }
 #endif
 }
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index 03ab3d0d..7db0265 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -88,6 +88,11 @@
   return chrome::FindBrowserWithWebContents(web_contents) == nullptr;
 }
 
+bool GuestOptionAvailable() {
+  return Profile::IsEphemeralGuestProfileEnabled() &&
+         !ProfileManager::GuestProfileExists();
+}
+
 }  // namespace
 
 ScopedDiceWebSigninInterceptionBubbleHandle::
@@ -239,7 +244,8 @@
       Delegate::BubbleParameters bubble_parameters{
           SigninInterceptionType::kProfileSwitch, *account_info,
           GetPrimaryAccountInfo(identity_manager_),
-          entry->GetProfileThemeColors().profile_highlight_color};
+          entry->GetProfileThemeColors().profile_highlight_color,
+          /*show_guest_option=*/false};
       interception_bubble_handle_ = delegate_->ShowSigninInterceptionBubble(
           web_contents, bubble_parameters,
           base::BindOnce(&DiceWebSigninInterceptor::OnProfileSwitchChoice,
@@ -401,7 +407,8 @@
   SkColor profile_color = GenerateNewProfileColor(entry).color;
   Delegate::BubbleParameters bubble_parameters{
       *interception_type, info, GetPrimaryAccountInfo(identity_manager_),
-      GetAutogeneratedThemeColors(profile_color).frame_color};
+      GetAutogeneratedThemeColors(profile_color).frame_color,
+      GuestOptionAvailable()};
   interception_bubble_handle_ = delegate_->ShowSigninInterceptionBubble(
       web_contents(), bubble_parameters,
       base::BindOnce(&DiceWebSigninInterceptor::OnProfileCreationChoice,
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.h b/chrome/browser/signin/dice_web_signin_interceptor.h
index 42967f5..7c29c17c 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.h
+++ b/chrome/browser/signin/dice_web_signin_interceptor.h
@@ -150,6 +150,7 @@
       AccountInfo intercepted_account;
       AccountInfo primary_account;
       SkColor profile_highlight_color;
+      bool show_guest_option;
     };
 
     virtual ~Delegate() = default;
diff --git a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
index 6548970..5ea4988 100644
--- a/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
+++ b/chrome/browser/signin/services/android/java/src/org/chromium/chrome/browser/signin/services/ProfileDataCacheRenderTest.java
@@ -141,13 +141,14 @@
         when(mIdentityManagerNativeMock
                         .findExtendedAccountInfoForAccountWithRefreshTokenByEmailAddress(
                                 anyLong(), eq(accountEmail)))
-                .thenReturn(new AccountInfo(
-                        new CoreAccountId("gaia-id-test"), accountEmail, "gaia-id-test", null));
+                .thenReturn(new AccountInfo(new CoreAccountId("gaia-id-test"), accountEmail,
+                        "gaia-id-test", "full name", "given name", null));
 
         mAccountManagerTestRule.addAccount(
                 new ProfileDataSource.ProfileData(accountEmail, null, "Full Name", "Given Name"));
-        mIdentityManager.onExtendedAccountInfoUpdated(new AccountInfo(
-                new CoreAccountId("gaia-id-test"), accountEmail, "gaia-id-test", createAvatar()));
+        mIdentityManager.onExtendedAccountInfoUpdated(
+                new AccountInfo(new CoreAccountId("gaia-id-test"), accountEmail, "gaia-id-test",
+                        "full name", "given name", createAvatar()));
         TestThreadUtils.runOnUiThreadBlocking(() -> { checkImageIsScaled(accountEmail); });
         mRenderTestRule.render(mImageView, "profile_data_cache_avatar" + mImageSize);
     }
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc
index 06fc0ecc..0ea9976 100644
--- a/chrome/browser/storage_access_api/api_browsertest.cc
+++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -63,8 +63,6 @@
     // HTTPS server only serves a valid cert for localhost, so this is needed
     // to load pages from other hosts without an error.
     command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
-                                    "CookieStoreDocument");
   }
 
   GURL GetURL(const std::string& host) {
diff --git a/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.cc b/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.cc
index 62c520c7..9ac064f 100644
--- a/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.cc
@@ -101,6 +101,10 @@
       drive_service_->AddObserver(this);
     }
   }
+  if (base::FeatureList::IsEnabled(
+          app_list_features::kEnableLauncherSearchNormalization)) {
+    normalizer_.emplace("drive_zero_state_provider", profile);
+  }
 }
 
 DriveZeroStateProvider::~DriveZeroStateProvider() {
@@ -214,6 +218,12 @@
   }
 
   cache_results_.reset();
+
+  if (normalizer_.has_value()) {
+    normalizer_->Record(provider_results);
+    normalizer_->NormalizeResults(&provider_results);
+  }
+
   SwapResults(&provider_results);
 
   LogStatus(Status::kOk);
diff --git a/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.h b/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.h
index 3246e2b..c5e38810 100644
--- a/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.h
+++ b/chrome/browser/ui/app_list/search/files/drive_zero_state_provider.h
@@ -9,6 +9,7 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/sequence_checker.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string16.h"
@@ -16,6 +17,7 @@
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/ui/app_list/search/files/file_result.h"
 #include "chrome/browser/ui/app_list/search/files/item_suggest_cache.h"
+#include "chrome/browser/ui/app_list/search/score_normalizer/score_normalizer.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 #include "chromeos/components/drivefs/mojom/drivefs.mojom.h"
 
@@ -69,6 +71,9 @@
   // Whether the suggested files experiment is enabled.
   const bool suggested_files_enabled_;
 
+  // The normalizer normalizes the relevance scores of Results
+  base::Optional<ScoreNormalizer> normalizer_;
+
   // Whether we have sent at least one request to ItemSuggest to warm up the
   // results cache.
   bool have_warmed_up_cache_ = false;
diff --git a/chrome/browser/ui/app_list/search/omnibox_provider.cc b/chrome/browser/ui/app_list/search/omnibox_provider.cc
index b19857e..f9c5351d 100644
--- a/chrome/browser/ui/app_list/search/omnibox_provider.cc
+++ b/chrome/browser/ui/app_list/search/omnibox_provider.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/app_list/search/omnibox_provider.h"
 
+#include "ash/public/cpp/app_list/app_list_features.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
@@ -37,6 +38,10 @@
           std::make_unique<ChromeAutocompleteProviderClient>(profile),
           AutocompleteClassifier::DefaultOmniboxProviders())) {
   controller_->AddObserver(this);
+  if (base::FeatureList::IsEnabled(
+          app_list_features::kEnableLauncherSearchNormalization)) {
+    normalizer_.emplace("omnibox_provider", profile);
+  }
 }
 
 OmniboxProvider::~OmniboxProvider() {}
@@ -87,6 +92,11 @@
         is_zero_state_input_));
   }
 
+  if (normalizer_.has_value()) {
+    normalizer_->Record(new_results);
+    normalizer_->NormalizeResults(&new_results);
+  }
+
   SwapResults(&new_results);
 }
 
diff --git a/chrome/browser/ui/app_list/search/omnibox_provider.h b/chrome/browser/ui/app_list/search/omnibox_provider.h
index c431e99..7576208 100644
--- a/chrome/browser/ui/app_list/search/omnibox_provider.h
+++ b/chrome/browser/ui/app_list/search/omnibox_provider.h
@@ -8,6 +8,8 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "base/optional.h"
+#include "chrome/browser/ui/app_list/search/score_normalizer/score_normalizer.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 #include "components/omnibox/browser/autocomplete_controller.h"
 
@@ -50,6 +52,9 @@
   // eliminates the results as they come in.
   std::unique_ptr<AutocompleteController> controller_;
 
+  // The normalizer normalizes the relevance scores of Results
+  base::Optional<ScoreNormalizer> normalizer_;
+
   DISALLOW_COPY_AND_ASSIGN(OmniboxProvider);
 };
 
diff --git a/chrome/browser/ui/app_list/search/zero_state_file_provider.cc b/chrome/browser/ui/app_list/search/zero_state_file_provider.cc
index 2978f12..919bef9 100644
--- a/chrome/browser/ui/app_list/search/zero_state_file_provider.cc
+++ b/chrome/browser/ui/app_list/search/zero_state_file_provider.cc
@@ -79,6 +79,11 @@
         profile->GetPath().AppendASCII("zero_state_local_files.pb"), config,
         chromeos::ProfileHelper::IsEphemeralUserProfile(profile));
   }
+
+  if (base::FeatureList::IsEnabled(
+          app_list_features::kEnableLauncherSearchNormalization)) {
+    normalizer_.emplace("zero_state_file_provider", profile);
+  }
 }
 
 ZeroStateFileProvider::~ZeroStateFileProvider() = default;
@@ -118,6 +123,11 @@
     }
   }
 
+  if (normalizer_.has_value()) {
+    normalizer_->Record(new_results);
+    normalizer_->NormalizeResults(&new_results);
+  }
+
   UMA_HISTOGRAM_TIMES("Apps.AppList.ZeroStateFileProvider.Latency",
                       base::TimeTicks::Now() - query_start_time_);
   SwapResults(&new_results);
diff --git a/chrome/browser/ui/app_list/search/zero_state_file_provider.h b/chrome/browser/ui/app_list/search/zero_state_file_provider.h
index 771be5a..7dfb815 100644
--- a/chrome/browser/ui/app_list/search/zero_state_file_provider.h
+++ b/chrome/browser/ui/app_list/search/zero_state_file_provider.h
@@ -13,12 +13,14 @@
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/scoped_observer.h"
 #include "base/sequenced_task_runner.h"
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/file_manager/file_tasks_notifier.h"
 #include "chrome/browser/chromeos/file_manager/file_tasks_observer.h"
+#include "chrome/browser/ui/app_list/search/score_normalizer/score_normalizer.h"
 #include "chrome/browser/ui/app_list/search/search_provider.h"
 
 class Profile;
@@ -62,6 +64,9 @@
   // empty query.
   std::unique_ptr<RecurrenceRanker> files_ranker_;
 
+  // The normalizer normalizes the relevance scores of Results
+  base::Optional<ScoreNormalizer> normalizer_;
+
   base::TimeTicks query_start_time_;
 
   ScopedObserver<file_manager::file_tasks::FileTasksNotifier,
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
index ff324f88b..c50ce2b 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.cc
@@ -14,11 +14,13 @@
 #include "base/strings/string16.h"
 #include "base/time/time.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/about_flags.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sharesheet/sharesheet_metrics.h"
 #include "chrome/browser/sharesheet/sharesheet_service_delegate.h"
 #include "chrome/browser/ui/ash/sharesheet/sharesheet_expand_button.h"
 #include "chrome/browser/ui/ash/sharesheet/sharesheet_target_button.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "extensions/browser/app_window/app_window.h"
@@ -165,6 +167,24 @@
   title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   title->SetProperty(views::kMarginsKey, gfx::Insets(kSpacing));
 
+  // Add content preview text descriptor.
+  if (base::FeatureList::IsEnabled(features::kSharesheetContentPreviews) &&
+      (intent_->file_urls.has_value())) {
+    // Remove the margin under the title for file_title
+    title->SetProperty(views::kMarginsKey,
+                       gfx::Insets(kSpacing, kSpacing, 0, kSpacing));
+    // This displays text data only for the first file_url provided.
+    auto* file_title = main_view_->AddChildView(std::make_unique<views::Label>(
+        base::ASCIIToUTF16(
+            (intent_->file_urls.value().front().ExtractFileName())),
+        ash::CONTEXT_SHARESHEET_BUBBLE_BODY_SECONDARY));
+    file_title->SetLineHeight(kTitleLineHeight);
+    file_title->SetEnabledColor(kShareTargetTitleColor);
+    file_title->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+    file_title->SetProperty(views::kMarginsKey,
+                            gfx::Insets(3, kSpacing, kSpacing, kSpacing));
+  }
+
   if (targets.empty()) {
     auto* image =
         main_view_->AddChildView(std::make_unique<views::ImageView>());
@@ -210,6 +230,11 @@
     expand_button_separator_->SetVisible(false);
   }
   UpdateAnchorPosition();
+
+  // Expanding the sharesheet is needed for content previews
+  if (base::FeatureList::IsEnabled(features::kSharesheetContentPreviews)) {
+    ResizeBubble(kDefaultBubbleWidth, GetBubbleHeight());
+  }
 }
 
 std::unique_ptr<views::View> SharesheetBubbleView::MakeScrollableTargetView(
@@ -510,8 +535,7 @@
 
 void SharesheetBubbleView::ExpandButtonPressed() {
   show_expanded_view_ = !show_expanded_view_;
-  ResizeBubble(kDefaultBubbleWidth, show_expanded_view_ ? kExpandedBubbleHeight
-                                                        : kDefaultBubbleHeight);
+  ResizeBubble(kDefaultBubbleWidth, GetBubbleHeight());
 
   // Scrollview has separators that overlaps with |expand_button_separator_|
   // to create a double line when both are visible, so when scrollview is
@@ -580,7 +604,7 @@
 
 void SharesheetBubbleView::SetToDefaultBubbleSizing() {
   width_ = kDefaultBubbleWidth;
-  height_ = kDefaultBubbleHeight;
+  height_ = GetBubbleHeight();
 }
 
 void SharesheetBubbleView::ShowWidgetWithAnimateFadeIn() {
@@ -641,6 +665,16 @@
   delegate_->OnBubbleClosed(active_target_);
 }
 
+int SharesheetBubbleView::GetBubbleHeight() {
+  int height =
+      show_expanded_view_ ? kExpandedBubbleHeight : kDefaultBubbleHeight;
+
+  if (base::FeatureList::IsEnabled(features::kSharesheetContentPreviews)) {
+    height += kTitleLineHeight + 3;
+  }
+  return height;
+}
+
 void SharesheetBubbleView::RecordFormFactorMetric() {
   auto form_factor =
       ash::TabletMode::Get()->InTabletMode()
diff --git a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h
index b522443..5f0fea2 100644
--- a/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h
+++ b/chrome/browser/ui/ash/sharesheet/sharesheet_bubble_view.h
@@ -73,6 +73,7 @@
   void ShowWidgetWithAnimateFadeIn();
   void CloseWidgetWithAnimateFadeOut(views::Widget::ClosedReason closed_reason);
   void CloseWidgetWithReason(views::Widget::ClosedReason closed_reason);
+  int GetBubbleHeight();
   void RecordFormFactorMetric();
 
   // Owns this class.
diff --git a/chrome/browser/ui/extensions/extensions_container.h b/chrome/browser/ui/extensions/extensions_container.h
index b353378..f00cfe1 100644
--- a/chrome/browser/ui/extensions/extensions_container.h
+++ b/chrome/browser/ui/extensions/extensions_container.h
@@ -75,6 +75,8 @@
   // Same as above, but uses PostTask() in all cases.
   virtual void ShowToolbarActionBubbleAsync(
       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) = 0;
+
+  virtual void ToggleExtensionsMenu() = 0;
 };
 
 #endif  // CHROME_BROWSER_UI_EXTENSIONS_EXTENSIONS_CONTAINER_H_
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
index 16d4c3b..77768513 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.cc
@@ -606,6 +606,12 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(bubble)));
 }
 
+void ToolbarActionsBar::ToggleExtensionsMenu() {
+  // This is only implemented by |ExtensionsToolbarContainer|.
+  // TODO(crbug.com/943702): Remove this entire class.
+  NOTREACHED();
+}
+
 bool ToolbarActionsBar::CloseOverflowMenuIfOpen() {
   return delegate_->CloseOverflowMenuIfOpen();
 }
diff --git a/chrome/browser/ui/toolbar/toolbar_actions_bar.h b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
index 4d61c0b..ccf320e 100644
--- a/chrome/browser/ui/toolbar/toolbar_actions_bar.h
+++ b/chrome/browser/ui/toolbar/toolbar_actions_bar.h
@@ -248,6 +248,7 @@
       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
   void ShowToolbarActionBubbleAsync(
       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
+  void ToggleExtensionsMenu() override;
 
  private:
   // Returns the insets by which the icon area bounds (See GetIconAreaRect())
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
index bd3edb5..065b27f7 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
@@ -296,6 +296,10 @@
 
     // Removing the last extension. All actions now have the same state.
     RemoveExtension(method, extensions()[1]->id());
+
+    // Container should remain visible during the removal animation.
+    EXPECT_TRUE(GetExtensionsToolbarContainer()->IsDrawn());
+    views::test::WaitForAnimatingLayoutManager(GetExtensionsToolbarContainer());
     EXPECT_EQ(expected_visibility, GetExtensionsToolbarContainer()->IsDrawn());
   }
 
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_button.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_button.cc
index 4ac306b..b4f3658 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_button.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_button.cc
@@ -29,7 +29,7 @@
   std::unique_ptr<views::MenuButtonController> menu_button_controller =
       std::make_unique<views::MenuButtonController>(
           this,
-          base::BindRepeating(&ExtensionsToolbarButton::ButtonPressed,
+          base::BindRepeating(&ExtensionsToolbarButton::ToggleExtensionsMenu,
                               base::Unretained(this)),
           std::make_unique<views::Button::DefaultButtonControllerDelegate>(
               this));
@@ -95,6 +95,24 @@
 void ExtensionsToolbarButton::OnWidgetDestroying(views::Widget* widget) {
   widget->RemoveObserver(this);
   pressed_lock_.reset();
+  extensions_container_->OnMenuClosed();
+}
+
+void ExtensionsToolbarButton::ToggleExtensionsMenu() {
+  if (ExtensionsMenuView::IsShowing()) {
+    ExtensionsMenuView::Hide();
+    return;
+  }
+  pressed_lock_ = menu_button_controller_->TakeLock();
+  extensions_container_->OnMenuOpening();
+  base::RecordAction(base::UserMetricsAction("Extensions.Toolbar.MenuOpened"));
+  ExtensionsMenuView::ShowBubble(this, browser_, extensions_container_,
+                                 extensions_container_->CanShowIconInToolbar())
+      ->AddObserver(this);
+}
+
+bool ExtensionsToolbarButton::IsExtensionsMenuShowing() const {
+  return pressed_lock_.get();
 }
 
 int ExtensionsToolbarButton::GetIconSize() const {
@@ -102,15 +120,3 @@
   return (touch_ui && !browser_->app_controller()) ? kDefaultTouchableIconSize
                                                    : kDefaultIconSize;
 }
-
-void ExtensionsToolbarButton::ButtonPressed() {
-  if (ExtensionsMenuView::IsShowing()) {
-    ExtensionsMenuView::Hide();
-    return;
-  }
-  pressed_lock_ = menu_button_controller_->TakeLock();
-  base::RecordAction(base::UserMetricsAction("Extensions.Toolbar.MenuOpened"));
-  ExtensionsMenuView::ShowBubble(this, browser_, extensions_container_,
-                                 extensions_container_->CanShowIconInToolbar())
-      ->AddObserver(this);
-}
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_button.h b/chrome/browser/ui/views/extensions/extensions_toolbar_button.h
index dfe81b2..3351f7e 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_button.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_button.h
@@ -26,6 +26,12 @@
   ExtensionsToolbarButton& operator=(const ExtensionsToolbarButton&) = delete;
   ~ExtensionsToolbarButton() override;
 
+  // Activate the Extensions menu. If the ExtensionsToolbarContainer is in
+  // kAutoHide mode this will cause it to show.
+  void ToggleExtensionsMenu();
+
+  bool IsExtensionsMenuShowing() const;
+
   // ToolbarButton:
   gfx::Size CalculatePreferredSize() const override;
   gfx::Size GetMinimumSize() const override;
@@ -39,8 +45,6 @@
  private:
   int GetIconSize() const;
 
-  void ButtonPressed();
-
   // A lock to keep the button pressed when a popup is visible.
   std::unique_ptr<views::MenuButtonController::PressedLock> pressed_lock_;
 
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
index ccfdddf..9826b68 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.cc
@@ -23,6 +23,20 @@
 #include "ui/views/layout/flex_layout_types.h"
 #include "ui/views/view_class_properties.h"
 
+namespace {
+
+base::OnceClosure& GetOnVisibleCallbackForTesting() {
+  static base::NoDestructor<base::OnceClosure> callback;
+  return *callback;
+}
+
+}  // namespace
+
+void ExtensionsToolbarContainer::SetOnVisibleCallbackForTesting(
+    base::OnceClosure callback) {
+  GetOnVisibleCallbackForTesting() = std::move(callback);
+}
+
 struct ExtensionsToolbarContainer::DropInfo {
   DropInfo(ToolbarActionsModel::ActionId action_id, size_t index);
 
@@ -64,8 +78,9 @@
                                       views::FlexSpecification());
       break;
     case DisplayMode::kCompact:
-      // In compact mode, the menu icon can be hidden but has the highest
-      // priority.
+    case DisplayMode::kAutoHide:
+      // In compact/auto hide mode, the menu icon can be hidden but has the
+      // highest priority.
       extensions_button_->SetProperty(
           views::kFlexBehaviorKey, hide_icon_flex_specification.WithOrder(1));
       break;
@@ -174,8 +189,9 @@
                                  views::FlexSpecification());
         break;
       case DisplayMode::kCompact:
-        // In compact mode, the icon can still drop out, but receives precedence
-        // over other actions.
+      case DisplayMode::kAutoHide:
+        // In compact/auto hide mode, the icon can still drop out, but receives
+        // precedence over other actions.
         action_view->SetProperty(
             views::kFlexBehaviorKey,
             views::FlexSpecification(
@@ -291,6 +307,7 @@
   ToolbarActionViewController* const popped_out_action = popped_out_action_;
   popped_out_action_ = nullptr;
   UpdateIconVisibility(popped_out_action->GetId());
+  UpdateContainerVisibilityAfterAnimation();
 }
 
 void ExtensionsToolbarContainer::SetPopupOwner(
@@ -299,12 +316,21 @@
   // never unsetting one when one wasn't set.
   DCHECK((popup_owner_ != nullptr) ^ (popup_owner != nullptr));
   popup_owner_ = popup_owner;
+
+  // Container should become visible if |popup_owner_| and may lose visibility
+  // if not |popup_owner_|. Visibility must be maintained during layout
+  // animations.
+  if (popup_owner_)
+    UpdateContainerVisibility();
+  else
+    UpdateContainerVisibilityAfterAnimation();
 }
 
 void ExtensionsToolbarContainer::HideActivePopup() {
   if (popup_owner_)
     popup_owner_->HidePopup();
   DCHECK(!popup_owner_);
+  UpdateContainerVisibilityAfterAnimation();
 }
 
 bool ExtensionsToolbarContainer::CloseOverflowMenuIfOpen() {
@@ -324,6 +350,7 @@
   popped_out_action_ = action;
   UpdateIconVisibility(action->GetId());
   animating_layout_manager()->PostOrQueueAction(std::move(closure));
+  UpdateContainerVisibility();
 }
 
 bool ExtensionsToolbarContainer::ShowToolbarActionPopupForAPICall(
@@ -360,6 +387,10 @@
   ShowToolbarActionBubble(std::move(bubble));
 }
 
+void ExtensionsToolbarContainer::ToggleExtensionsMenu() {
+  extensions_button_->ToggleExtensionsMenu();
+}
+
 void ExtensionsToolbarContainer::OnTabStripModelChanged(
     TabStripModel* tab_strip_model,
     const TabStripModelChange& change,
@@ -400,7 +431,7 @@
   RemoveChildViewT(GetViewForId(action_id));
   icons_.erase(action_id);
 
-  UpdateContainerVisibility();
+  UpdateContainerVisibilityAfterAnimation();
 }
 
 void ExtensionsToolbarContainer::OnToolbarActionMoved(
@@ -666,7 +697,56 @@
 }
 
 void ExtensionsToolbarContainer::UpdateContainerVisibility() {
-  // The container (and extensions-menu button) should be visible if we have at
-  // least one extension.
-  SetVisible(!actions_.empty());
+  bool was_visible = GetVisible();
+  SetVisible(ShouldContainerBeVisible());
+
+  // Layout animation does not handle host view visibility changing; requires
+  // resetting.
+  if (was_visible != GetVisible())
+    animating_layout_manager()->ResetLayout();
+
+  if (!was_visible && GetVisible() && GetOnVisibleCallbackForTesting())
+    std::move(GetOnVisibleCallbackForTesting()).Run();
+}
+
+bool ExtensionsToolbarContainer::ShouldContainerBeVisible() const {
+  // The container (and extensions-menu button) should not be visible if we have
+  // no extensions.
+  if (actions_.empty())
+    return false;
+
+  // All other display modes are constantly visible.
+  if (display_mode_ != DisplayMode::kAutoHide)
+    return true;
+
+  if (animating_layout_manager()->is_animating())
+    return true;
+
+  // Is menu showing.
+  if (extensions_button_->IsExtensionsMenuShowing())
+    return true;
+
+  // Is extension pop out is showing.
+  if (popped_out_action_)
+    return true;
+
+  // Is extension pop up showing.
+  if (popup_owner_)
+    return true;
+
+  return false;
+}
+
+void ExtensionsToolbarContainer::UpdateContainerVisibilityAfterAnimation() {
+  animating_layout_manager()->PostOrQueueAction(
+      base::BindOnce(&ExtensionsToolbarContainer::UpdateContainerVisibility,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ExtensionsToolbarContainer::OnMenuOpening() {
+  UpdateContainerVisibility();
+}
+
+void ExtensionsToolbarContainer::OnMenuClosed() {
+  UpdateContainerVisibility();
 }
diff --git a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
index abdbb5a..c2f0bc5 100644
--- a/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
+++ b/chrome/browser/ui/views/extensions/extensions_toolbar_container.h
@@ -11,6 +11,7 @@
 #include <string>
 #include <vector>
 
+#include "base/callback_forward.h"
 #include "base/optional.h"
 #include "base/scoped_observation.h"
 #include "chrome/browser/ui/extensions/extensions_container.h"
@@ -50,11 +51,21 @@
     // always enough space to show at least two icons.
     kNormal,
     // In compact mode, one or both of the menu icon and popped-out action may
-    // be hidden. Compact mode is used in smaller windows (e.g. webapps) where
+    // be hidden if the available space does not allow for them. Compact mode is
+    // used in smaller windows (e.g. web apps) where
     // there may not be enough space to display the buttons.
     kCompact,
+    // In auto hide mode the menu icon is hidden until
+    // extensions_button()->ToggleExtensionsMenu() is called by the embedder.
+    // This
+    // is used for windows that want to minimize the number of visible icons in
+    // their
+    // toolbar (e.g. web apps).
+    kAutoHide,
   };
 
+  static void SetOnVisibleCallbackForTesting(base::OnceClosure callback);
+
   explicit ExtensionsToolbarContainer(
       Browser* browser,
       DisplayMode display_mode = DisplayMode::kNormal);
@@ -80,6 +91,12 @@
   void ShowWidgetForExtension(views::Widget* widget,
                               const std::string& extension_id);
 
+  // Event handler for when the extensions menu is opened.
+  void OnMenuOpening();
+
+  // Event handler for when the extensions menu is closed.
+  void OnMenuClosed();
+
   // Gets the widget that anchors to the extension (or is about to anchor to the
   // extension, pending pop-out).
   views::Widget* GetAnchoredWidgetForExtensionForTesting(
@@ -123,6 +140,7 @@
       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
   void ShowToolbarActionBubbleAsync(
       std::unique_ptr<ToolbarActionsBarBubbleDelegate> bubble) override;
+  void ToggleExtensionsMenu() override;
 
   // ToolbarActionView::Delegate:
   content::WebContents* GetCurrentWebContents() override;
@@ -187,10 +205,17 @@
   void SetExtensionIconVisibility(ToolbarActionsModel::ActionId id,
                                   bool visible);
 
-  // Calls SetVisible to make sure that the container is showing only when there
-  // are extensions available.
+  // Calls SetVisible() with ShouldContainerBeVisible().
   void UpdateContainerVisibility();
 
+  // Returns whether the contianer should be showing, e.g. not if there are no
+  // extensions installed, nor if the container is inactive in kAutoHide mode.
+  bool ShouldContainerBeVisible() const;
+
+  // Queues up a call to UpdateContainerVisibility() for when the current layout
+  // animation ends.
+  void UpdateContainerVisibilityAfterAnimation();
+
   // TabStripModelObserver:
   void OnTabStripModelChanged(
       TabStripModel* tab_strip_model,
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
index 2e8eaeb..1ddaed15a 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h"
+#include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
 #include "chrome/browser/web_applications/components/web_application_info.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -168,7 +169,7 @@
 
   // Add taller children to the web app frame toolbar RHS.
   const int container_height = web_app_frame_toolbar_->height();
-  web_app_frame_toolbar_->GetRightContainerForTesting()->AddChildView(
+  web_app_frame_toolbar_->get_right_container_for_testing()->AddChildView(
       new views::StaticSizedView(gfx::Size(1, title_bar_height * 2)));
   opaque_browser_frame_view_->Layout();
 
diff --git a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.cc b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.cc
index 940637d2..bc71c8f 100644
--- a/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.cc
+++ b/chrome/browser/ui/views/profiles/dice_web_signin_interception_bubble_view.cc
@@ -155,11 +155,10 @@
   std::unique_ptr<views::WebView> web_view =
       std::make_unique<views::WebView>(profile);
   web_view->LoadInitialURL(GURL(chrome::kChromeUIDiceWebSigninInterceptURL));
-  web_view->SetPreferredSize(
-      gfx::Size(kInterceptionBubbleWidth,
-                DiceWebSigninInterceptUI::ShouldShowGuestOption()
-                    ? kInterceptionBubbleWithGuestHeight
-                    : kInterceptionBubbleWithoutGuestHeight));
+  web_view->SetPreferredSize(gfx::Size(
+      kInterceptionBubbleWidth, bubble_parameters.show_guest_option
+                                    ? kInterceptionBubbleWithGuestHeight
+                                    : kInterceptionBubbleWithoutGuestHeight));
   DiceWebSigninInterceptUI* web_ui = web_view->GetWebContents()
                                          ->GetWebUI()
                                          ->GetController()
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 6d109c4..ae0f53c 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -63,6 +63,7 @@
 #include "ui/native_theme/native_theme.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/metadata/metadata_impl_macros.h"
 
 namespace {
 
@@ -744,3 +745,7 @@
   }
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
+BEGIN_METADATA(ProfileMenuView, ProfileMenuViewBase)
+ADD_READONLY_PROPERTY_METADATA(gfx::ImageSkia, SyncIcon)
+END_METADATA
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.h b/chrome/browser/ui/views/profiles/profile_menu_view.h
index 279a495..e626eae9 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.h
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.h
@@ -33,6 +33,8 @@
 // It displays a list of profiles and allows users to switch between profiles.
 class ProfileMenuView : public ProfileMenuViewBase {
  public:
+  METADATA_HEADER(ProfileMenuView);
+
   ProfileMenuView(views::Button* anchor_button, Browser* browser);
   ~ProfileMenuView() override;
 
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index 9a92c63..db21b6f 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -134,6 +134,25 @@
   return signin_url;
 }
 
+class ProfilePickerWidget : public views::Widget {
+ public:
+  explicit ProfilePickerWidget(ProfilePickerView* profile_picker_view)
+      : profile_picker_view_(profile_picker_view) {
+    views::Widget::InitParams params;
+    params.delegate = profile_picker_view_;
+    Init(std::move(params));
+  }
+  ~ProfilePickerWidget() override = default;
+
+  // views::Widget:
+  const ui::ThemeProvider* GetThemeProvider() const override {
+    return profile_picker_view_->GetThemeProviderForProfileBeingCreated();
+  }
+
+ private:
+  ProfilePickerView* const profile_picker_view_;
+};
+
 }  // namespace
 
 // static
@@ -232,14 +251,22 @@
   }
 }
 
+const ui::ThemeProvider*
+ProfilePickerView::GetThemeProviderForProfileBeingCreated() const {
+  if (!signed_in_profile_being_created_)
+    return nullptr;
+  return &ThemeService::GetThemeProviderForProfile(
+      signed_in_profile_being_created_);
+}
+
 ProfilePickerView::ProfilePickerView()
     : keep_alive_(KeepAliveOrigin::USER_MANAGER_VIEW,
                   KeepAliveRestartOption::DISABLED),
       extended_account_info_timeout_(kExtendedAccountInfoTimeout) {
+  // Setup the WidgetDelegate.
   SetHasWindowSizeControls(true);
-  SetButtons(ui::DIALOG_BUTTON_NONE);
   SetTitle(IDS_PRODUCT_NAME);
-  set_use_custom_frame(false);
+
   ConfigureAccelerators();
   // TODO(crbug.com/1063856): Add |RecordDialogCreation|.
 }
@@ -309,7 +336,8 @@
   extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
       system_profile_contents_.get());
 
-  CreateDialogWidget(this, nullptr, nullptr);
+  // The widget is owned by the native widget.
+  new ProfilePickerWidget(this);
 
 #if defined(OS_WIN)
   // Set the app id for the user manager to the app id of its parent.
@@ -429,7 +457,23 @@
 
   SyncConfirmationUI* sync_confirmation_ui = static_cast<SyncConfirmationUI*>(
       new_profile_contents_->GetWebUI()->GetController());
-  sync_confirmation_ui->InitializeMessageHandlerForCreationFlow();
+  sync_confirmation_ui->InitializeMessageHandlerForCreationFlow(profile_color_);
+}
+
+void ProfilePickerView::WindowClosing() {
+  // Now that the window is closed, we can allow a new one to be opened.
+  // (WindowClosing comes in asynchronously from the call to Close() and we
+  // may have already opened a new instance).
+  if (g_profile_picker_view == this)
+    g_profile_picker_view = nullptr;
+}
+
+views::ClientView* ProfilePickerView::CreateClientView(views::Widget* widget) {
+  return new views::ClientView(widget, TransferOwnershipOfContentsView());
+}
+
+views::View* ProfilePickerView::GetContentsView() {
+  return this;
 }
 
 gfx::Size ProfilePickerView::CalculatePreferredSize() const {
@@ -443,14 +487,6 @@
   return preferred_size;
 }
 
-void ProfilePickerView::WindowClosing() {
-  // Now that the window is closed, we can allow a new one to be opened.
-  // (WindowClosing comes in asynchronously from the call to Close() and we
-  // may have already opened a new instance).
-  if (g_profile_picker_view == this)
-    g_profile_picker_view = nullptr;
-}
-
 gfx::Size ProfilePickerView::GetMinimumSize() const {
   // On small screens, the preferred size may be smaller than the picker
   // minimum size. In that case there will be scrollbars on the picker.
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.h b/chrome/browser/ui/views/profiles/profile_picker_view.h
index effd6b9..91bb348 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.h
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.h
@@ -15,7 +15,8 @@
 #include "content/public/browser/web_contents_delegate.h"
 #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h"
 #include "ui/views/controls/webview/webview.h"
-#include "ui/views/window/dialog_delegate.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget_delegate.h"
 
 struct AccountInfo;
 class Browser;
@@ -31,12 +32,14 @@
 }  // namespace content
 
 // Dialog widget that contains the Desktop Profile picker webui.
-class ProfilePickerView : public views::DialogDelegateView,
+class ProfilePickerView : public views::WidgetDelegateView,
                           public content::WebContentsDelegate,
                           public signin::IdentityManager::Observer {
  public:
   using BrowserOpenedCallback = base::OnceCallback<void(Browser*)>;
 
+  const ui::ThemeProvider* GetThemeProviderForProfileBeingCreated() const;
+
  private:
   friend class ProfilePicker;
 
@@ -73,11 +76,13 @@
   // Switches the layout to the sync confirmation screen.
   void SwitchToSyncConfirmation();
 
-  // views::DialogDelegateView:
-  gfx::Size CalculatePreferredSize() const override;
+  // views::WidgetDelegate:
   void WindowClosing() override;
+  views::ClientView* CreateClientView(views::Widget* widget) override;
+  views::View* GetContentsView() override;
 
-  // views::View;
+  // views::View:
+  gfx::Size CalculatePreferredSize() const override;
   gfx::Size GetMinimumSize() const override;
   bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
index 42c77e4..6ef2ccf 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_icon_container_view.h
@@ -57,6 +57,11 @@
     return static_cast<views::AnimatingLayoutManager*>(GetLayoutManager());
   }
 
+  const views::AnimatingLayoutManager* animating_layout_manager() const {
+    return static_cast<const views::AnimatingLayoutManager*>(
+        GetLayoutManager());
+  }
+
   // Provides access to the flex layout in the animating layout manager.
   views::FlexLayout* target_layout_manager() {
     return static_cast<views::FlexLayout*>(
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
index 2b281481..4589dba 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
@@ -5,12 +5,17 @@
 #include <cmath>
 
 #include "base/optional.h"
+#include "base/path_service.h"
 #include "base/run_loop.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "chrome/browser/extensions/chrome_test_extension_loader.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/extensions/extensions_menu_view.h"
+#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
+#include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -18,6 +23,11 @@
 #include "chrome/browser/ui/views/page_action/page_action_icon_controller.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_test_helper.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h"
+#include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_navigation_button_container.h"
+#include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h"
+#include "chrome/browser/ui/web_applications/web_app_menu_model.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -26,6 +36,7 @@
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "content/public/test/theme_change_waiter.h"
+#include "extensions/test/test_extension_dir.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/geometry/size.h"
@@ -50,6 +61,22 @@
   return visible;
 }
 
+void LoadTestPopUpExtension(Profile* profile) {
+  extensions::TestExtensionDir test_extension_dir;
+  test_extension_dir.WriteManifest(
+      R"({
+          "name": "Pop up extension",
+          "version": "1.0",
+          "manifest_version": 2,
+          "browser_action": {
+            "default_popup": "popup.html"
+          }
+         })");
+  test_extension_dir.WriteFile(FILE_PATH_LITERAL("popup.html"), "");
+  extensions::ChromeTestExtensionLoader(profile).LoadExtension(
+      test_extension_dir.UnpackedPath());
+}
+
 }  // namespace
 
 class WebAppFrameToolbarBrowserTest : public InProcessBrowserTest {
@@ -79,8 +106,8 @@
   const GURL app_url("https://test.org");
   helper()->InstallAndLaunchWebApp(browser(), app_url);
 
-  views::View* const toolbar_left_container =
-      helper()->web_app_frame_toolbar()->GetLeftContainerForTesting();
+  WebAppNavigationButtonContainer* const toolbar_left_container =
+      helper()->web_app_frame_toolbar()->get_left_container_for_testing();
   EXPECT_EQ(toolbar_left_container->parent(),
             helper()->web_app_frame_toolbar());
 
@@ -92,8 +119,8 @@
   EXPECT_EQ(window_title->parent(), helper()->frame_view());
 #endif
 
-  views::View* const toolbar_right_container =
-      helper()->web_app_frame_toolbar()->GetRightContainerForTesting();
+  WebAppToolbarButtonContainer* const toolbar_right_container =
+      helper()->web_app_frame_toolbar()->get_right_container_for_testing();
   EXPECT_EQ(toolbar_right_container->parent(),
             helper()->web_app_frame_toolbar());
 
@@ -226,10 +253,10 @@
   const GURL app_url("https://test.org");
   helper()->InstallAndLaunchWebApp(browser(), app_url);
 
-  views::View* const toolbar_left_container =
-      helper()->web_app_frame_toolbar()->GetLeftContainerForTesting();
-  views::View* const toolbar_right_container =
-      helper()->web_app_frame_toolbar()->GetRightContainerForTesting();
+  WebAppNavigationButtonContainer* const toolbar_left_container =
+      helper()->web_app_frame_toolbar()->get_left_container_for_testing();
+  WebAppToolbarButtonContainer* const toolbar_right_container =
+      helper()->web_app_frame_toolbar()->get_right_container_for_testing();
 
   auto* const window_title = static_cast<views::Label*>(
       helper()->frame_view()->GetViewByID(VIEW_ID_WINDOW_TITLE));
@@ -271,3 +298,85 @@
       helper()->frame_view()->GetTooltipHandlerForPoint(window_title->origin()),
       window_title);
 }
+
+class WebAppFrameToolbarBrowserTest_ElidedExtensionsMenu
+    : public WebAppFrameToolbarBrowserTest {
+ public:
+  WebAppFrameToolbarBrowserTest_ElidedExtensionsMenu() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kDesktopPWAsElidedExtensionsMenu);
+  }
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_ElidedExtensionsMenu,
+                       Test) {
+  LoadTestPopUpExtension(browser()->profile());
+  helper()->InstallAndLaunchWebApp(browser(), GURL("https://test.org"));
+
+  WebAppToolbarButtonContainer* toolbar_button_container =
+      helper()->web_app_frame_toolbar()->get_right_container_for_testing();
+
+  // There should be no visible Extensions icon.
+  EXPECT_FALSE(toolbar_button_container->extensions_container()->GetVisible());
+
+  // There should be a menu entry for opening the Extensions menu.
+  auto app_menu_model = std::make_unique<WebAppMenuModel>(
+      /*provider=*/nullptr, helper()->app_browser());
+  app_menu_model->Init();
+  ui::MenuModel* model = app_menu_model.get();
+  int index = -1;
+  const bool found = app_menu_model->GetModelAndIndexForCommandId(
+      WebAppMenuModel::kExtensionsMenuCommandId, &model, &index);
+  EXPECT_TRUE(found);
+  EXPECT_TRUE(model->IsEnabledAt(index));
+
+  app_menu_model->ExecuteCommand(WebAppMenuModel::kExtensionsMenuCommandId,
+                                 /*event_flags=*/0);
+
+  // Extensions icon and menu should be visible.
+  EXPECT_TRUE(toolbar_button_container->extensions_container()->GetVisible());
+  EXPECT_TRUE(ExtensionsMenuView::IsShowing());
+}
+
+class WebAppFrameToolbarBrowserTest_NoElidedExtensionsMenu
+    : public WebAppFrameToolbarBrowserTest {
+ public:
+  WebAppFrameToolbarBrowserTest_NoElidedExtensionsMenu() {
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kDesktopPWAsElidedExtensionsMenu);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_NoElidedExtensionsMenu,
+                       Test) {
+  helper()->InstallAndLaunchWebApp(browser(), GURL("https://test.org"));
+
+  WebAppToolbarButtonContainer* toolbar_button_container =
+      helper()->web_app_frame_toolbar()->get_right_container_for_testing();
+
+  // Extensions toolbar should be hidden while there are no Extensions
+  // installed.
+  EXPECT_FALSE(toolbar_button_container->extensions_container()->GetVisible());
+
+  // Install Extension and wait for Extensions toolbar to appear.
+  base::RunLoop run_loop;
+  ExtensionsToolbarContainer::SetOnVisibleCallbackForTesting(
+      run_loop.QuitClosure());
+  LoadTestPopUpExtension(browser()->profile());
+  run_loop.Run();
+  EXPECT_TRUE(toolbar_button_container->extensions_container()->GetVisible());
+
+  // There should be no menu entry for opening the Extensions menu.
+  auto app_menu_model = std::make_unique<WebAppMenuModel>(
+      /*provider=*/nullptr, helper()->app_browser());
+  app_menu_model->Init();
+  ui::MenuModel* model = app_menu_model.get();
+  int index = -1;
+  const bool found = app_menu_model->GetModelAndIndexForCommandId(
+      WebAppMenuModel::kExtensionsMenuCommandId, &model, &index);
+  EXPECT_FALSE(found);
+}
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
index ca8d227..f21d4fa 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.cc
@@ -223,14 +223,6 @@
   return left_container_ ? left_container_->reload_button() : nullptr;
 }
 
-views::View* WebAppFrameToolbarView::GetLeftContainerForTesting() {
-  return left_container_;
-}
-
-views::View* WebAppFrameToolbarView::GetRightContainerForTesting() {
-  return right_container_;
-}
-
 PageActionIconController*
 WebAppFrameToolbarView::GetPageActionIconControllerForTesting() {
   return right_container_->page_action_icon_controller();
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h
index 8aecaa07..ae4108b 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_view.h
@@ -70,8 +70,12 @@
   ToolbarButton* GetBackButton() override;
   ReloadButton* GetReloadButton() override;
 
-  views::View* GetLeftContainerForTesting();
-  views::View* GetRightContainerForTesting();
+  WebAppNavigationButtonContainer* get_left_container_for_testing() {
+    return left_container_;
+  }
+  WebAppToolbarButtonContainer* get_right_container_for_testing() {
+    return right_container_;
+  }
   PageActionIconController* GetPageActionIconControllerForTesting();
 
  protected:
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h
index 9bee202..b36a13a 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h
@@ -8,6 +8,7 @@
 #include "base/strings/string16.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
+#include "chrome/browser/ui/web_applications/web_app_menu_model.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/gfx/color_palette.h"
 
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
index df3a23b..8dcbd3c 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/view_ids.h"
+#include "chrome/browser/ui/views/extensions/extensions_toolbar_button.h"
 #include "chrome/browser/ui/views/extensions/extensions_toolbar_container.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/toolbar_button_provider.h"
@@ -19,6 +20,7 @@
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_menu_button.h"
 #include "chrome/browser/ui/views/web_apps/frame_toolbar/web_app_origin_text.h"
 #include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
+#include "chrome/common/chrome_features.h"
 #include "ui/base/hit_test.h"
 #include "ui/views/layout/flex_layout.h"
 #include "ui/views/window/hit_test_utils.h"
@@ -104,11 +106,16 @@
     // for example, the menu button or other toolbar buttons, and pinned
     // extensions should hide before other toolbar buttons.
     constexpr int kLowPriorityFlexOrder = 2;
+
     if (base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu)) {
+      auto display_mode =
+          base::FeatureList::IsEnabled(
+              features::kDesktopPWAsElidedExtensionsMenu)
+              ? ExtensionsToolbarContainer::DisplayMode::kAutoHide
+              : ExtensionsToolbarContainer::DisplayMode::kCompact;
       extensions_container_ =
           AddChildView(std::make_unique<ExtensionsToolbarContainer>(
-              browser_view_->browser(),
-              ExtensionsToolbarContainer::DisplayMode::kCompact));
+              browser_view_->browser(), display_mode));
       extensions_container_->SetProperty(
           views::kFlexBehaviorKey,
           views::FlexSpecification(
@@ -349,6 +356,7 @@
   return browser_view_->GetActiveWebContents();
 }
 
+// views::WidgetObserver:
 void WebAppToolbarButtonContainer::OnWidgetVisibilityChanged(
     views::Widget* widget,
     bool visible) {
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h
index d7e6df4a..1f15cb8 100644
--- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h
+++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_toolbar_button_container.h
@@ -12,6 +12,7 @@
 #include "chrome/browser/ui/views/page_action/page_action_icon_container.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
+#include "chrome/browser/ui/web_applications/web_app_menu_model.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
index 2bac29f2..e0a6e95 100644
--- a/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_integration_browsertest.cc
@@ -260,7 +260,7 @@
     extensions::ScopedTestDialogAutoConfirm auto_confirm(
         extensions::ScopedTestDialogAutoConfirm::ACCEPT);
     auto app_menu_model =
-        std::make_unique<WebAppMenuModel>(nullptr, app_browser_);
+        std::make_unique<WebAppMenuModel>(/*provider=*/nullptr, app_browser_);
     app_menu_model->Init();
     ui::MenuModel* model = app_menu_model.get();
     int index = -1;
diff --git a/chrome/browser/ui/web_applications/web_app_browsertest.cc b/chrome/browser/ui/web_applications/web_app_browsertest.cc
index 8084c50e..0efdc889 100644
--- a/chrome/browser/ui/web_applications/web_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_app_browsertest.cc
@@ -698,7 +698,8 @@
   Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id);
 
   app_browser->tab_strip_model()->CloseAllTabs();
-  auto app_menu_model = std::make_unique<WebAppMenuModel>(nullptr, app_browser);
+  auto app_menu_model = std::make_unique<WebAppMenuModel>(
+      /*provider=*/nullptr, app_browser);
   app_menu_model->Init();
 }
 
@@ -708,7 +709,8 @@
   const AppId app_id = InstallPWA(app_url);
   Browser* const app_browser = LaunchWebAppBrowserAndWait(app_id);
 
-  auto app_menu_model = std::make_unique<WebAppMenuModel>(nullptr, app_browser);
+  auto app_menu_model = std::make_unique<WebAppMenuModel>(
+      /*provider=*/nullptr, app_browser);
   app_menu_model->Init();
   ui::MenuModel* model = app_menu_model.get();
   int index = -1;
diff --git a/chrome/browser/ui/web_applications/web_app_menu_model.cc b/chrome/browser/ui/web_applications/web_app_menu_model.cc
index 979a42c..0e00a55 100644
--- a/chrome/browser/ui/web_applications/web_app_menu_model.cc
+++ b/chrome/browser/ui/web_applications/web_app_menu_model.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/web_applications/web_app_menu_model.h"
 
+#include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/chromeos_buildflags.h"
@@ -12,7 +13,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/extensions/extensions_container.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/web_applications/app_browser_controller.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
@@ -28,6 +32,7 @@
 #endif
 
 constexpr int WebAppMenuModel::kUninstallAppCommandId;
+constexpr int WebAppMenuModel::kExtensionsMenuCommandId;
 
 WebAppMenuModel::WebAppMenuModel(ui::AcceleratorProvider* provider,
                                  Browser* browser)
@@ -36,17 +41,30 @@
 WebAppMenuModel::~WebAppMenuModel() {}
 
 bool WebAppMenuModel::IsCommandIdEnabled(int command_id) const {
-  return command_id == kUninstallAppCommandId
-             ? browser()->app_controller()->CanUninstall()
-             : AppMenuModel::IsCommandIdEnabled(command_id);
+  switch (command_id) {
+    case kUninstallAppCommandId:
+      return browser()->app_controller()->CanUninstall();
+    case kExtensionsMenuCommandId:
+      return base::FeatureList::IsEnabled(features::kExtensionsToolbarMenu) &&
+             base::FeatureList::IsEnabled(
+                 features::kDesktopPWAsElidedExtensionsMenu);
+    default:
+      return AppMenuModel::IsCommandIdEnabled(command_id);
+  }
 }
 
 void WebAppMenuModel::ExecuteCommand(int command_id, int event_flags) {
-  if (command_id == kUninstallAppCommandId) {
-    LogMenuAction(MENU_ACTION_UNINSTALL_APP);
-    browser()->app_controller()->Uninstall();
-  } else {
-    AppMenuModel::ExecuteCommand(command_id, event_flags);
+  switch (command_id) {
+    case kUninstallAppCommandId:
+      LogMenuAction(MENU_ACTION_UNINSTALL_APP);
+      browser()->app_controller()->Uninstall();
+      break;
+    case kExtensionsMenuCommandId:
+      browser()->window()->GetExtensionsContainer()->ToggleExtensionsMenu();
+      break;
+    default:
+      AppMenuModel::ExecuteCommand(command_id, event_flags);
+      break;
   }
 }
 
@@ -70,6 +88,9 @@
                    browser()->location_bar_model()->GetVectorIcon()));
 
   AddSeparator(ui::NORMAL_SEPARATOR);
+
+  if (IsCommandIdEnabled(kExtensionsMenuCommandId))
+    AddItemWithStringId(kExtensionsMenuCommandId, IDS_SHOW_EXTENSIONS);
   AddItemWithStringId(IDC_COPY_URL, IDS_COPY_URL);
   AddItemWithStringId(IDC_OPEN_IN_CHROME, IDS_OPEN_IN_CHROME);
 
diff --git a/chrome/browser/ui/web_applications/web_app_menu_model.h b/chrome/browser/ui/web_applications/web_app_menu_model.h
index 39ffe97..feba25f 100644
--- a/chrome/browser/ui/web_applications/web_app_menu_model.h
+++ b/chrome/browser/ui/web_applications/web_app_menu_model.h
@@ -14,6 +14,7 @@
 class WebAppMenuModel : public AppMenuModel {
  public:
   static constexpr int kUninstallAppCommandId = 1;
+  static constexpr int kExtensionsMenuCommandId = 2;
 
   WebAppMenuModel(ui::AcceleratorProvider* provider, Browser* browser);
   WebAppMenuModel(const WebAppMenuModel&) = delete;
diff --git a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
index 6cb4b41..54a4949 100644
--- a/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
+++ b/chrome/browser/ui/webui/chrome_url_data_manager_browsertest.cc
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
+#include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webui/welcome/helpers.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
@@ -143,7 +146,14 @@
     : public InProcessBrowserTest,
       public testing::WithParamInterface<const char*> {
  public:
-  ChromeURLDataManagerWebUITrustedTypesTest() = default;
+  ChromeURLDataManagerWebUITrustedTypesTest() {
+    std::vector<base::Feature> enabled_features;
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+    if (GetParam() == std::string("chrome://welcome"))
+      enabled_features.push_back(welcome::kForceEnabled);
+#endif
+    feature_list_.InitWithFeatures(enabled_features, {});
+  }
 
   void CheckTrustedTypesViolation(base::StringPiece url) {
     std::string message_filter1 = "*This document requires*assignment*";
@@ -156,16 +166,26 @@
 
     ASSERT_TRUE(embedded_test_server()->Start());
     ui_test_utils::NavigateToURL(browser(), GURL(url));
-    // We don't ASSERT_TRUE here because some WebUI pages are by design not
-    // PAGE_TYPE_NORMAL (e.g. chrome://interstitials/ssl).
-    content::WaitForLoadStop(content);
+
+    if (url == "chrome://network-error" || url == "chrome://dino") {
+      // We don't ASSERT_TRUE here because some WebUI pages are
+      // PAGE_TYPE_ERROR by design.
+      content::WaitForLoadStop(content);
+    } else {
+      ASSERT_TRUE(content::WaitForLoadStop(content));
+    }
+
     EXPECT_TRUE(console_observer.messages().empty());
   }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 // Verify that there's no Trusted Types violation in chrome://chrome-urls
 IN_PROC_BROWSER_TEST_P(ChromeURLDataManagerWebUITrustedTypesTest,
                        NoTrustedTypesViolation) {
+  LOG(INFO) << "Navigating to " << GetParam();
   CheckTrustedTypesViolation(GetParam());
 }
 
@@ -176,23 +196,18 @@
     // TODO(crbug.com/1114074): DCHECK failure when opening
     // chrome://appcache-internals.
     // "chrome://appcache-internals",
-    "chrome://apps",
     "chrome://autofill-internals",
     "chrome://blob-internals",
     "chrome://bluetooth-internals",
     "chrome://bookmarks",
-    "chrome://browser-switch",
     "chrome://chrome-urls",
     "chrome://components",
-    "chrome://conflicts",
     "chrome://connection-help",
     "chrome://connection-monitoring-detected",
     "chrome://conversion-internals",
     "chrome://crashes",
     "chrome://credits",
     "chrome://device-log",
-    // TODO(crbug.com/1114062): Crash when closing chrome://devices.
-    // "chrome://devices",
     "chrome://dino",
     // TODO(crbug.com/1113446): Test failure due to excessive output.
     // "chrome://discards",
@@ -215,21 +230,18 @@
     "chrome://invalidations",
     "chrome://local-state",
     "chrome://management",
-    "chrome://md-user-manager",
     "chrome://media-engagement",
     "chrome://media-feeds",
     "chrome://media-history",
     "chrome://media-internals",
     "chrome://media-router-internals",
     "chrome://memory-internals",
-    "chrome://nacl",
     "chrome://net-export",
     "chrome://net-internals",
     "chrome://network-error",
     "chrome://network-errors",
     "chrome://new-tab-page",
     "chrome://newtab",
-    "chrome://notifications-internals",
     "chrome://ntp-tiles-internals",
     "chrome://omnibox",
     "chrome://password-manager-internals",
@@ -241,16 +253,13 @@
     "chrome://quota-internals",
     "chrome://reset-password",
     "chrome://safe-browsing",
-    "chrome://sandbox",
     "chrome://serviceworker-internals",
     "chrome://settings",
     // TODO(crbug.com/1115600): DCHECK failure when opening
     // chrome://signin-dice-web-intercept.
     // "chrome://signin-dice-web-intercept",
-    "chrome://signin-email-confirmation",
     "chrome://signin-internals",
     "chrome://site-engagement",
-    "chrome://snippets-internals",
     "chrome://suggestions",
     // TODO(crbug.com/1099564): Navigating to chrome://sync-confirmation and
     // quickly navigating away cause DCHECK failure.
@@ -270,12 +279,12 @@
     "chrome://version",
     "chrome://webrtc-internals",
     "chrome://webrtc-logs",
-    "chrome://welcome",
 #if defined(OS_ANDROID)
     "chrome://explore-sites-internals",
     "chrome://internals/notifications",
     "chrome://internals/query-tiles",
     "chrome://offline-internals",
+    "chrome://snippets-internals",
     "chrome://webapks",
 #endif
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -295,7 +304,6 @@
     "chrome://crostini-installer",
     "chrome://cryptohome",
     "chrome://drive-internals",
-    "chrome://first-run",
     "chrome://help-app",
     "chrome://internet-config-dialog",
     "chrome://internet-detail-dialog",
@@ -314,11 +322,22 @@
     "chrome://smb-share-dialog",
     "chrome://supervised-user-internals",
     "chrome://sys-internals",
-    // TODO(crbug.com/1115643): DCHECK failure when opening
-    // chrome-untrusted://crosh.
-    // "chrome-untrusted://crosh",
     "chrome-untrusted://terminal",
 #endif
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+    "chrome://apps",
+    "chrome://browser-switch",
+    "chrome://md-user-manager",
+    "chrome://signin-email-confirmation",
+    "chrome://welcome",
+#endif
+#if !defined(OS_MAC)
+    "chrome://sandbox",
+    "chrome://nacl",
+#endif
+#if defined(OS_WIN)
+    "chrome://conflicts",
+#endif
 };
 
 INSTANTIATE_TEST_SUITE_P(,
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index ac1ba79..fb85417 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -48,8 +48,8 @@
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chromium_strings.h"
-#include "chrome/grit/component_extension_resources.h"
 #include "chrome/grit/generated_resources.h"
+#include "chrome/grit/pdf_resources.h"
 #include "chrome/grit/print_preview_resources.h"
 #include "chrome/grit/print_preview_resources_map.h"
 #include "chromeos/constants/chromeos_features.h"
@@ -415,11 +415,14 @@
       {"pdf/browser_api.js", IDR_PDF_BROWSER_API_JS},
       {"pdf/constants.js", IDR_PDF_CONSTANTS_JS},
       {"pdf/controller.js", IDR_PDF_CONTROLLER_JS},
-      {"pdf/elements/icons.js", IDR_PDF_ICONS_JS},
-      {"pdf/elements/shared-vars.js", IDR_PDF_SHARED_VARS_JS},
-      {"pdf/elements/viewer-error-screen.js", IDR_PDF_VIEWER_ERROR_SCREEN_JS},
-      {"pdf/elements/viewer-zoom-button.js", IDR_PDF_VIEWER_ZOOM_BUTTON_JS},
-      {"pdf/elements/viewer-zoom-toolbar.js", IDR_PDF_VIEWER_ZOOM_SELECTOR_JS},
+      {"pdf/elements/icons.js", IDR_PDF_ELEMENTS_ICONS_JS},
+      {"pdf/elements/shared-vars.js", IDR_PDF_ELEMENTS_SHARED_VARS_JS},
+      {"pdf/elements/viewer-error-screen.js",
+       IDR_PDF_ELEMENTS_VIEWER_ERROR_SCREEN_JS},
+      {"pdf/elements/viewer-zoom-button.js",
+       IDR_PDF_ELEMENTS_VIEWER_ZOOM_BUTTON_JS},
+      {"pdf/elements/viewer-zoom-toolbar.js",
+       IDR_PDF_ELEMENTS_VIEWER_ZOOM_TOOLBAR_JS},
       {"pdf/gesture_detector.js", IDR_PDF_GESTURE_DETECTOR_JS},
       {"pdf/index.css", IDR_PDF_INDEX_CSS},
       {"pdf/main.js", IDR_PDF_MAIN_JS},
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
index 756ee38d..a7d96dc 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
@@ -36,6 +36,8 @@
 namespace settings {
 namespace {
 
+using FeatureState = multidevice_setup::mojom::FeatureState;
+
 const std::vector<SearchConcept>& GetMultiDeviceOptedInSearchConcepts() {
   static const base::NoDestructor<std::vector<SearchConcept>> tags(
       {{IDS_OS_SETTINGS_TAG_MULTIDEVICE_FORGET,
@@ -551,14 +553,11 @@
         host_status_with_device) {
   SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
   updater.RemoveSearchTags(GetMultiDeviceOptedOutSearchConcepts());
-  updater.RemoveSearchTags(GetMultiDeviceOptedInPhoneHubSearchConcepts());
   updater.RemoveSearchTags(GetMultiDeviceOptedInWifiSyncSearchConcepts());
   updater.RemoveSearchTags(GetMultiDeviceOptedInSearchConcepts());
 
   if (IsOptedIn(host_status_with_device.first)) {
     updater.AddSearchTags(GetMultiDeviceOptedInSearchConcepts());
-    if (features::IsPhoneHubEnabled())
-      updater.AddSearchTags(GetMultiDeviceOptedInPhoneHubSearchConcepts());
     if (features::IsWifiSyncAndroidEnabled())
       updater.AddSearchTags(GetMultiDeviceOptedInWifiSyncSearchConcepts());
   } else {
@@ -571,11 +570,14 @@
         feature_states_map) {
   SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
   updater.RemoveSearchTags(GetSmartLockOptionsSearchConcepts());
+  updater.RemoveSearchTags(GetMultiDeviceOptedInPhoneHubSearchConcepts());
 
   if (feature_states_map.at(multidevice_setup::mojom::Feature::kSmartLock) ==
       multidevice_setup::mojom::FeatureState::kEnabledByUser) {
     updater.AddSearchTags(GetSmartLockOptionsSearchConcepts());
   }
+  if (IsPhoneHubSupported())
+    updater.AddSearchTags(GetMultiDeviceOptedInPhoneHubSearchConcepts());
 }
 
 void MultiDeviceSection::OnNearbySharingEnabledChanged() {
@@ -590,5 +592,12 @@
   }
 }
 
+bool MultiDeviceSection::IsPhoneHubSupported() {
+  const FeatureState feature_state = multidevice_setup_client_->GetFeatureState(
+      multidevice_setup::mojom::Feature::kPhoneHub);
+  return feature_state != FeatureState::kNotSupportedByPhone &&
+         feature_state != FeatureState::kNotSupportedByChromebook;
+}
+
 }  // namespace settings
 }  // namespace chromeos
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
index f420b8a9..3663219 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h
@@ -68,6 +68,8 @@
   // Nearby Share enabled pref change observer.
   void OnNearbySharingEnabledChanged();
 
+  bool IsPhoneHubSupported();
+
   multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
   phonehub::PhoneHubManager* phone_hub_manager_;
   android_sms::AndroidSmsService* android_sms_service_;
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index 707c936..d4aa5a0 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -817,22 +817,26 @@
   web_contents->GetDelegate()->ActivateContents(web_contents);
 }
 
-void PeopleHandler::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  // After a primary account was set, the Sync setup will start soon. Grab a
-  // SetupInProgressHandle right now to avoid a temporary "missing Sync
-  // confirmation" error in the avatar menu. See crbug.com/928696.
-  syncer::SyncService* service = GetSyncService();
-  if (service && !sync_blocker_)
-    sync_blocker_ = service->GetSetupInProgressHandle();
-
-  UpdateSyncStatus();
-}
-
-void PeopleHandler::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
-  sync_blocker_.reset();
-  UpdateSyncStatus();
+void PeopleHandler::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event) {
+  switch (event.GetEventTypeFor(signin::ConsentLevel::kSync)) {
+    case signin::PrimaryAccountChangeEvent::Type::kSet: {
+      // After a primary account was set, the Sync setup will start soon. Grab a
+      // SetupInProgressHandle right now to avoid a temporary "missing Sync
+      // confirmation" error in the avatar menu. See crbug.com/928696.
+      syncer::SyncService* service = GetSyncService();
+      if (service && !sync_blocker_)
+        sync_blocker_ = service->GetSetupInProgressHandle();
+      UpdateSyncStatus();
+      return;
+    }
+    case signin::PrimaryAccountChangeEvent::Type::kCleared:
+      sync_blocker_.reset();
+      UpdateSyncStatus();
+      return;
+    case signin::PrimaryAccountChangeEvent::Type::kNone:
+      return;
+  }
 }
 
 void PeopleHandler::OnStateChanged(syncer::SyncService* sync) {
diff --git a/chrome/browser/ui/webui/settings/people_handler.h b/chrome/browser/ui/webui/settings/people_handler.h
index fdc26132..e189aec8 100644
--- a/chrome/browser/ui/webui/settings/people_handler.h
+++ b/chrome/browser/ui/webui/settings/people_handler.h
@@ -126,10 +126,8 @@
   void FocusUI() override;
 
   // IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override;
   void OnExtendedAccountInfoUpdated(const AccountInfo& info) override;
   void OnExtendedAccountInfoRemoved(const AccountInfo& info) override;
 
diff --git a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
index 18421be..66553cd 100644
--- a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
+++ b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_handler.cc
@@ -181,6 +181,8 @@
                           l10n_util::GetStringUTF8(confirmButtonStringID));
   parameters.SetStringKey("cancelButtonLabel",
                           l10n_util::GetStringUTF8(cancelButtonStringID));
+  parameters.SetBoolKey("showGuestOption",
+                        bubble_parameters_.show_guest_option);
   parameters.SetKey("interceptedAccount",
                     GetAccountInfoValue(intercepted_account()));
   parameters.SetStringKey("headerBackgroundColor",
diff --git a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.cc b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.cc
index f8e2153..09d0d15 100644
--- a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.cc
+++ b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.cc
@@ -33,7 +33,6 @@
   source->AddResourcePath("signin_vars_css.js", IDR_SIGNIN_VARS_CSS_JS);
   source->AddLocalizedString("guestLink",
                              IDS_SIGNIN_DICE_WEB_INTERCEPT_BUBBLE_GUEST_LINK);
-  source->AddBoolean("shouldShowGuest", ShouldShowGuestOption());
   source->UseStringsJs();
 
   // Resources for testing.
@@ -49,12 +48,6 @@
 
 DiceWebSigninInterceptUI::~DiceWebSigninInterceptUI() = default;
 
-// static
-bool DiceWebSigninInterceptUI::ShouldShowGuestOption() {
-  return Profile::IsEphemeralGuestProfileEnabled() &&
-         !ProfileManager::GuestProfileExists();
-}
-
 void DiceWebSigninInterceptUI::Initialize(
     const DiceWebSigninInterceptor::Delegate::BubbleParameters&
         bubble_parameters,
diff --git a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.h b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.h
index c2aafa7d..f937ed0 100644
--- a/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.h
+++ b/chrome/browser/ui/webui/signin/dice_web_signin_intercept_ui.h
@@ -28,8 +28,6 @@
           bubble_parameters,
       base::OnceCallback<void(SigninInterceptionUserChoice)> callback);
 
-  static bool ShouldShowGuestOption();
-
  private:
   WEB_UI_CONTROLLER_TYPE_DECL();
 };
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 c0099ea..d82d5a5 100644
--- a/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
+++ b/chrome/browser/ui/webui/signin/login_ui_test_utils.cc
@@ -83,9 +83,14 @@
     FAIL() << "Sign in observer timed out!";
   }
 
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override {
-    DVLOG(1) << "Google signin succeeded.";
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override {
+    if (event.GetEventTypeFor(signin::ConsentLevel::kSync) !=
+        signin::PrimaryAccountChangeEvent::Type::kSet) {
+      return;
+    }
+
+    DVLOG(1) << "Sign in finished: Sync primary account was set.";
     signed_in_ = true;
     QuitLoopRunner();
   }
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
index 79134b5..f21f622f 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler.cc
@@ -163,18 +163,16 @@
     return;
   }
 
-  std::string picture_url_to_load;
   GURL picture_gurl(picture_url);
-  if (picture_gurl.is_valid()) {
-    picture_url_to_load =
-        signin::GetAvatarImageURLWithOptions(picture_gurl, kProfileImageSize,
-                                             false /* no_silhouette */)
-            .spec();
-  } else {
-    // Use the placeholder avatar icon until the account picture URL is fetched.
-    picture_url_to_load = profiles::GetPlaceholderAvatarIconUrl();
+  if (!picture_gurl.is_valid()) {
+    // As long as the provided gaia picture is not valid, stick to the default
+    // avatar provided in the load-time data.
+    return;
   }
-  base::Value picture_url_value(picture_url_to_load);
+
+  GURL picture_gurl_with_options = signin::GetAvatarImageURLWithOptions(
+      picture_gurl, kProfileImageSize, false /* no_silhouette */);
+  base::Value picture_url_value(picture_gurl_with_options.spec());
 
   AllowJavascript();
   FireWebUIListener("account-image-changed", picture_url_value);
@@ -229,7 +227,6 @@
   }
 
   if (!primary_account_info->IsValid()) {
-    SetUserImageURL(kNoPictureURLFound);
     identity_manager_->AddObserver(this);
   } else {
     SetUserImageURL(primary_account_info->picture_url);
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
index 2c334c7..ea69b790 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_handler_unittest.cc
@@ -170,16 +170,14 @@
     base::Optional<AccountInfo> primary_account =
         identity_manager->FindExtendedAccountInfoForAccountWithRefreshToken(
             identity_manager->GetPrimaryAccountInfo());
+    EXPECT_TRUE(primary_account);
 
-    std::string original_picture_url =
-        primary_account ? primary_account->picture_url : std::string();
+    std::string gaia_picture_url = primary_account->picture_url;
     std::string expected_picture_url =
-        original_picture_url.empty()
-            ? profiles::GetPlaceholderAvatarIconUrl()
-            : signin::GetAvatarImageURLWithOptions(GURL(original_picture_url),
-                                                   kExpectedProfileImageSize,
-                                                   false /* no_silhouette */)
-                  .spec();
+        signin::GetAvatarImageURLWithOptions(GURL(gaia_picture_url),
+                                             kExpectedProfileImageSize,
+                                             false /* no_silhouette */)
+            .spec();
     std::string passed_picture_url;
     ASSERT_TRUE(call_data.arg2()->GetAsString(&passed_picture_url));
     EXPECT_EQ(expected_picture_url, passed_picture_url);
@@ -233,16 +231,16 @@
   args.Set(0, std::make_unique<base::Value>(kDefaultDialogHeight));
   handler()->HandleInitializedWithSize(&args);
 
-  ASSERT_EQ(1U, web_ui()->call_data().size());
-  ExpectAccountImageChanged(*web_ui()->call_data()[0]);
+  // No callback called when there's no account image available.
+  ASSERT_EQ(0U, web_ui()->call_data().size());
 
   identity_test_env()->SimulateSuccessfulFetchOfAccountInfo(
       account_info_.account_id, account_info_.email, account_info_.gaia, "",
       "full_name", "given_name", "locale",
       "http://picture.example.com/picture.jpg");
 
-  ASSERT_EQ(2U, web_ui()->call_data().size());
-  ExpectAccountImageChanged(*web_ui()->call_data()[1]);
+  ASSERT_EQ(1U, web_ui()->call_data().size());
+  ExpectAccountImageChanged(*web_ui()->call_data()[0]);
 }
 
 TEST_F(SyncConfirmationHandlerTest,
@@ -250,7 +248,7 @@
   base::ListValue args;
   args.Set(0, std::make_unique<base::Value>(kDefaultDialogHeight));
   handler()->HandleInitializedWithSize(&args);
-  EXPECT_EQ(1U, web_ui()->call_data().size());
+  EXPECT_EQ(0U, web_ui()->call_data().size());
 
   AccountInfo account_info =
       identity_test_env()->MakeAccountAvailable("bar@example.com");
@@ -261,7 +259,7 @@
 
   // Updating the account info of a secondary account should not update the
   // image of the sync confirmation dialog.
-  EXPECT_EQ(1U, web_ui()->call_data().size());
+  EXPECT_EQ(0U, web_ui()->call_data().size());
 
   identity_test_env()->SimulateSuccessfulFetchOfAccountInfo(
       account_info_.account_id, account_info_.email, account_info_.gaia, "",
@@ -270,8 +268,8 @@
 
   // Updating the account info of the primary account should update the
   // image of the sync confirmation dialog.
-  ASSERT_EQ(2U, web_ui()->call_data().size());
-  ExpectAccountImageChanged(*web_ui()->call_data()[1]);
+  ASSERT_EQ(1U, web_ui()->call_data().size());
+  ExpectAccountImageChanged(*web_ui()->call_data()[0]);
 }
 
 TEST_F(SyncConfirmationHandlerTest, TestHandleUndo) {
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
index b4e0b72..0562c53a 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/signin/profile_colors_util.h"
 #include "chrome/browser/ui/webui/signin/sync_confirmation_handler.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/browser_resources.h"
@@ -25,25 +26,32 @@
 #include "services/network/public/mojom/content_security_policy.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/color_utils.h"
 #include "ui/resources/grit/webui_generated_resources.h"
 #include "ui/resources/grit/webui_resources.h"
 
 SyncConfirmationUI::SyncConfirmationUI(content::WebUI* web_ui)
     : SigninWebDialogUI(web_ui), profile_(Profile::FromWebUI(web_ui)) {
-  Initialize();
+  // Initializing the WebUIDataSource in the constructor is needed for polymer
+  // tests.
+  Initialize(/*profile_creation_flow_color=*/base::nullopt);
 }
 
-SyncConfirmationUI::~SyncConfirmationUI() {}
+SyncConfirmationUI::~SyncConfirmationUI() = default;
 
 void SyncConfirmationUI::InitializeMessageHandlerWithBrowser(Browser* browser) {
   InitializeMessageHandler(browser);
 }
 
-void SyncConfirmationUI::InitializeMessageHandlerForCreationFlow() {
+void SyncConfirmationUI::InitializeMessageHandlerForCreationFlow(
+    SkColor profile_color) {
+  // Redo the initialization with `profile_color`.
+  Initialize(profile_color);
   InitializeMessageHandler(/*browser=*/nullptr);
 }
 
-void SyncConfirmationUI::Initialize() {
+void SyncConfirmationUI::Initialize(
+    base::Optional<SkColor> profile_creation_flow_color) {
   const bool is_sync_allowed =
       ProfileSyncServiceFactory::IsSyncAllowed(profile_);
 
@@ -58,7 +66,7 @@
   source->AddResourcePath("sync_confirmation.js", IDR_SYNC_CONFIRMATION_JS);
 
   if (is_sync_allowed) {
-    InitializeForSyncConfirmation(source);
+    InitializeForSyncConfirmation(source, profile_creation_flow_color);
   } else {
     InitializeForSyncDisabled(source);
   }
@@ -79,24 +87,15 @@
 }
 
 void SyncConfirmationUI::InitializeForSyncConfirmation(
-    content::WebUIDataSource* source) {
+    content::WebUIDataSource* source,
+    base::Optional<SkColor> profile_creation_flow_color) {
+  // Resources for testing.
   source->AddResourcePath("test_loader.js", IDR_WEBUI_JS_TEST_LOADER_JS);
   source->AddResourcePath("test_loader.html", IDR_WEBUI_HTML_TEST_LOADER_HTML);
   source->OverrideContentSecurityPolicy(
       network::mojom::CSPDirectiveName::ScriptSrc,
       "script-src chrome://resources chrome://test 'self';");
 
-  source->SetDefaultResource(IDR_SYNC_CONFIRMATION_HTML);
-  source->AddResourcePath("sync_confirmation_app.js",
-                          IDR_SYNC_CONFIRMATION_APP_JS);
-
-  source->AddResourcePath(
-      "images/sync_confirmation_illustration.svg",
-      IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_ILLUSTRATION_SVG);
-  source->AddResourcePath(
-      "images/sync_confirmation_illustration_dark.svg",
-      IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_ILLUSTRATION_DARK_SVG);
-
   AddStringResource(source, "syncConfirmationTitle",
                     IDS_SYNC_CONFIRMATION_TITLE);
   AddStringResource(source, "syncConfirmationSyncInfoTitle",
@@ -109,24 +108,48 @@
                     IDS_SYNC_CONFIRMATION_SETTINGS_BUTTON_LABEL);
   AddStringResource(source, "syncConfirmationConfirmLabel",
                     IDS_SYNC_CONFIRMATION_CONFIRM_BUTTON_LABEL);
-  AddStringResource(source, "syncConfirmationUndoLabel", IDS_CANCEL);
 
-  constexpr int kAccountPictureSize = 68;
-  std::string custom_picture_url = profiles::GetPlaceholderAvatarIconUrl();
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(profile_);
-  base::Optional<AccountInfo> primary_account_info =
-      identity_manager->FindExtendedAccountInfoForAccountWithRefreshToken(
-          identity_manager->GetPrimaryAccountInfo());
-  GURL account_picture_url(
-      primary_account_info ? primary_account_info->picture_url : std::string());
-  if (account_picture_url.is_valid()) {
-    custom_picture_url =
-        signin::GetAvatarImageURLWithOptions(
-            account_picture_url, kAccountPictureSize, false /* no_silhouette */)
-            .spec();
+  const int kAccountPictureSize = 128;
+  std::string avatar_picture_url;
+  if (profile_creation_flow_color.has_value()) {
+    SkColor fill_color = *profile_creation_flow_color;
+    gfx::Image avatar_picture = profiles::GetPlaceholderAvatarIconWithColors(
+        /*fill_color=*/fill_color,
+        /*stroke_color=*/GetAvatarStrokeColor(fill_color), kAccountPictureSize);
+    avatar_picture_url = webui::GetBitmapDataUrl(avatar_picture.AsBitmap());
+  } else {
+    avatar_picture_url = profiles::GetPlaceholderAvatarIconUrl();
   }
-  source->AddString("accountPictureUrl", custom_picture_url);
+  source->AddString("accountPictureUrl", avatar_picture_url);
+
+  source->AddResourcePath("sync_confirmation_app.js",
+                          IDR_SYNC_CONFIRMATION_APP_JS);
+  source->SetDefaultResource(IDR_SYNC_CONFIRMATION_HTML);
+
+  source->AddBoolean("isProfileCreationFlow",
+                     profile_creation_flow_color.has_value());
+  if (!profile_creation_flow_color.has_value()) {
+    AddStringResource(source, "syncConfirmationUndoLabel", IDS_CANCEL);
+
+    source->AddResourcePath(
+        "images/sync_confirmation_illustration.svg",
+        IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_ILLUSTRATION_SVG);
+    source->AddResourcePath(
+        "images/sync_confirmation_illustration_dark.svg",
+        IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_ILLUSTRATION_DARK_SVG);
+    return;
+  }
+
+  AddStringResource(source, "syncConfirmationUndoLabel", IDS_NO_THANKS);
+  source->AddString("highlightColor", color_utils::SkColorToRgbaString(
+                                          *profile_creation_flow_color));
+
+  source->AddResourcePath(
+      "images/sync_confirmation_refreshed_illustration.svg",
+      IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_REFRESHED_ILLUSTRATION_SVG);
+  source->AddResourcePath(
+      "images/sync_confirmation_refreshed_illustration_dark.svg",
+      IDR_SYNC_CONFIRMATION_IMAGES_SYNC_CONFIRMATION_REFRESHED_ILLUSTRATION_DARK_SVG);
 }
 
 void SyncConfirmationUI::InitializeForSyncDisabled(
diff --git a/chrome/browser/ui/webui/signin/sync_confirmation_ui.h b/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
index 3dd46725..923a0ac 100644
--- a/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
+++ b/chrome/browser/ui/webui/signin/sync_confirmation_ui.h
@@ -37,12 +37,14 @@
 
   // Initializes the message handler for the profile creation flow (when there's
   // no browser available).
-  void InitializeMessageHandlerForCreationFlow();
+  void InitializeMessageHandlerForCreationFlow(SkColor profile_color);
 
  private:
-  void Initialize();
+  void Initialize(base::Optional<SkColor> profile_creation_flow_color);
   void InitializeMessageHandler(Browser* browser);
-  void InitializeForSyncConfirmation(content::WebUIDataSource* source);
+  void InitializeForSyncConfirmation(
+      content::WebUIDataSource* source,
+      base::Optional<SkColor> profile_creation_flow_color);
   void InitializeForSyncDisabled(content::WebUIDataSource* source);
 
   // Adds a string resource with the given GRD |ids| to the WebUI data |source|
diff --git a/chrome/browser/webapps/chrome_webapps_client.cc b/chrome/browser/webapps/chrome_webapps_client.cc
index 9c0b640b..038dc5fb 100644
--- a/chrome/browser/webapps/chrome_webapps_client.cc
+++ b/chrome/browser/webapps/chrome_webapps_client.cc
@@ -13,6 +13,9 @@
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/android/tab_web_contents_delegate_android.h"
 #include "chrome/browser/android/webapk/webapk_install_service.h"
+#include "chrome/browser/feature_engagement/tracker_factory.h"
+#include "components/feature_engagement/public/event_constants.h"
+#include "components/feature_engagement/public/tracker.h"
 #endif
 
 namespace webapps {
@@ -84,6 +87,15 @@
                     tab->web_contents()->GetDelegate())
                     ->CanShowAppBanners();
 }
+
+void ChromeWebappsClient::OnWebApkInstallInitiatedFromAppMenu(
+    content::WebContents* web_contents) {
+  DVLOG(2) << "Sending event: IPH used for Installing PWA";
+  feature_engagement::Tracker* tracker =
+      feature_engagement::TrackerFactory::GetForBrowserContext(
+          web_contents->GetBrowserContext());
+  tracker->NotifyEvent(feature_engagement::events::kPwaInstallMenuSelected);
+}
 #endif
 
 }  // namespace webapps
diff --git a/chrome/browser/webapps/chrome_webapps_client.h b/chrome/browser/webapps/chrome_webapps_client.h
index 0227c9b..b12377f 100644
--- a/chrome/browser/webapps/chrome_webapps_client.h
+++ b/chrome/browser/webapps/chrome_webapps_client.h
@@ -29,6 +29,8 @@
   bool IsInstallationInProgress(content::WebContents* web_contents,
                                 const GURL& manifest_url) override;
   bool CanShowAppBanners(content::WebContents* web_contents) override;
+  void OnWebApkInstallInitiatedFromAppMenu(
+      content::WebContents* web_contents) override;
 #endif
 
  private:
diff --git a/chrome/browser/webshare/BUILD.gn b/chrome/browser/webshare/BUILD.gn
index 72725dd9..c36e200 100644
--- a/chrome/browser/webshare/BUILD.gn
+++ b/chrome/browser/webshare/BUILD.gn
@@ -4,8 +4,8 @@
 
 import("//build/config/chromeos/ui_mode.gni")
 
-static_library("storage") {
-  if (is_chromeos || is_mac) {
+if (is_chromeos || is_mac) {
+  static_library("storage") {
     sources = [
       "prepare_directory_task.cc",
       "prepare_directory_task.h",
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 3f5ab7e..b968fa66 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-master-1610063718-75764fb6f7cc7a2d4d8119e0952784a5ac3fa72a.profdata
+chrome-linux-master-1610107094-a1b03a814715c7959032a16f0512a58b7ebd318d.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 498932ef..4f2aa6a 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1610052964-246b43420c64aac53ecf14d1b97c19dc58b69e86.profdata
+chrome-win32-master-1610096386-b7d9ff85a7e5a6b9d422dd8ae3e7ff1b6461dd2b.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 10b0b39b..b58e242 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1610052964-a33bb7f09ef1aa4be028cda8573fe902d004ec4a.profdata
+chrome-win64-master-1610074312-1e61cca385dcdccbd239aae3d699d6888b006a9e.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 58f9c54..6ad9c87 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -268,6 +268,10 @@
         "//extensions:extensions_resources",
       ]
     }
+    if (enable_plugins) {
+      sources += [ "$root_gen_dir/chrome/pdf_resources.pak" ]
+      deps += [ "//chrome/browser/resources/pdf:pdf_resources" ]
+    }
     if (enable_print_preview) {
       sources += [ "$root_gen_dir/chrome/print_preview_resources.pak" ]
       deps +=
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 5660057..982cd07 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -735,7 +735,13 @@
     base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kRemoveStatusBarInWebApps{
-    "RemoveStatusBarInWebApps", base::FEATURE_DISABLED_BY_DEFAULT};
+    "RemoveStatusBarInWebApps",
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Enables permanent removal of Legacy Supervised Users on startup.
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4d91fff3..0beadd9 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -5761,7 +5761,7 @@
       "//components/safe_browsing/content/triggers:ad_redirect_trigger",
       "//components/safe_browsing/core:ping_manager_unittest",
       "//components/safe_browsing/core/browser:safe_browsing_url_checker_unittest",
-      "//components/safe_browsing/core/browser:token_fetcher_unittest",
+      "//components/safe_browsing/core/browser/sync:unittests",
       "//components/safe_browsing/core/db:v4_test_util",
     ]
   } else if (safe_browsing_mode == 2 && is_android) {
diff --git a/chrome/test/data/webui/signin/dice_web_signin_intercept_test.js b/chrome/test/data/webui/signin/dice_web_signin_intercept_test.js
index fd8a52b..fcb41999 100644
--- a/chrome/test/data/webui/signin/dice_web_signin_intercept_test.js
+++ b/chrome/test/data/webui/signin/dice_web_signin_intercept_test.js
@@ -8,7 +8,7 @@
 import {AccountInfo, DiceWebSigninInterceptBrowserProxyImpl, InterceptionParameters} from 'chrome://signin-dice-web-intercept/dice_web_signin_intercept_browser_proxy.js';
 
 import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
-import {isChildVisible} from '../test_util.m.js';
+import {isChildVisible, waitAfterNextRender} from '../test_util.m.js';
 
 import {TestDiceWebSigninInterceptBrowserProxy} from './test_dice_web_signin_intercept_browser_proxy.js';
 
@@ -29,7 +29,7 @@
   /** @type {string} */
   const AVATAR_URL_2 = 'chrome://theme/IDR_PROFILE_AVATAR_2';
 
-  setup(function() {
+  setup(async function() {
     browserProxy = new TestDiceWebSigninInterceptBrowserProxy();
     browserProxy.setInterceptionParameters({
       headerText: 'header_text',
@@ -37,6 +37,7 @@
       bodyText: 'body_text',
       confirmButtonLabel: 'confirm_label',
       cancelButtonLabel: 'cancel_label',
+      showGuestOption: true,
       headerTextColor: 'rgba(255, 255, 255, 1)',
       headerBackgroundColor: 'rgba(255, 0, 0, 1)',
       interceptedAccount: {isManaged: false, pictureUrl: AVATAR_URL_1},
@@ -47,6 +48,7 @@
     app = /** @type {!DiceWebSigninInterceptAppElement} */ (
         document.createElement('dice-web-signin-intercept-app'));
     document.body.append(app);
+    await waitAfterNextRender(app);
     return browserProxy.whenCalled('pageLoaded');
   });
 
@@ -134,9 +136,11 @@
       bodyText: 'new_body_text',
       confirmButtonLabel: 'new_confirm_label',
       cancelButtonLabel: 'new_cancel_label',
+      showGuestOption: true,
       headerTextColor: 'rgba(255, 255, 255, 1)',
       headerBackgroundColor: 'rgba(255, 0, 0, 1)',
       interceptedAccount: {isManaged: false, pictureUrl: AVATAR_URL_1},
+      primaryAccount: {isManaged: false, pictureUrl: AVATAR_URL_2}
     });
     checkTextValues(
         'new_header_text', 'new_body_title', 'new_body_text',
@@ -154,9 +158,11 @@
       bodyText: 'body_text',
       confirmButtonLabel: 'confirm_label',
       cancelButtonLabel: 'cancel_label',
+      showGuestOption: true,
       headerTextColor: 'rgba(255, 255, 255, 1)',
       headerBackgroundColor: 'rgba(255, 0, 0, 1)',
       interceptedAccount: {isManaged: false, pictureUrl: AVATAR_URL_2},
+      primaryAccount: {isManaged: false, pictureUrl: AVATAR_URL_2}
     };
 
     // Update urls.
diff --git a/chrome/test/data/webui/signin/sync_confirmation_test.js b/chrome/test/data/webui/signin/sync_confirmation_test.js
index c21318ad..48d856e 100644
--- a/chrome/test/data/webui/signin/sync_confirmation_test.js
+++ b/chrome/test/data/webui/signin/sync_confirmation_test.js
@@ -23,7 +23,7 @@
   // Tests that no DCHECKS are thrown during initialization of the UI.
   test('LoadPage', function() {
     assertEquals(
-        'Turn on sync?', app.$.syncConfirmationHeading.textContent.trim());
+        'Turn on sync?', app.$$('#syncConfirmationHeading').textContent.trim());
   });
 });
 
@@ -48,6 +48,9 @@
     PolymerTest.clearBody();
     app = document.createElement('sync-confirmation-app');
     document.body.append(app);
+    // Wait for the app element to get attached to the document (which is when
+    // the account image gets requested).
+    return browserProxy.whenCalled('requestAccountImage');
   });
 
   const STANDARD_CONSENT_DESCRIPTION_TEXT = [
diff --git a/chrome/test/data/webui/signin/test_dice_web_signin_intercept_browser_proxy.js b/chrome/test/data/webui/signin/test_dice_web_signin_intercept_browser_proxy.js
index d85ab32..bb92c87 100644
--- a/chrome/test/data/webui/signin/test_dice_web_signin_intercept_browser_proxy.js
+++ b/chrome/test/data/webui/signin/test_dice_web_signin_intercept_browser_proxy.js
@@ -17,6 +17,7 @@
       bodyText: '',
       cancelButtonLabel: '',
       confirmButtonLabel: '',
+      showGuestOption: false,
       headerTextColor: '',
       headerBackgroundColor: '',
       interceptedAccount: {isManaged: false, pictureUrl: ''},
diff --git a/chromeos/components/account_manager/account_manager_facade_ash.cc b/chromeos/components/account_manager/account_manager_facade_ash.cc
index 7445979..65dd43a 100644
--- a/chromeos/components/account_manager/account_manager_facade_ash.cc
+++ b/chromeos/components/account_manager/account_manager_facade_ash.cc
@@ -18,4 +18,16 @@
   return account_manager_->IsInitialized();
 }
 
+void AccountManagerFacadeAsh::ShowAddAccountDialog(
+    const AccountAdditionSource& source,
+    base::OnceCallback<void(const AccountAdditionResult& result)> callback) {
+  // TODO(crbug.com/1140469): implement this.
+}
+
+void AccountManagerFacadeAsh::ShowReauthAccountDialog(
+    const AccountAdditionSource& source,
+    const std::string& email) {
+  // TODO(crbug.com/1140469): implement this.
+}
+
 }  // namespace chromeos
diff --git a/chromeos/components/account_manager/account_manager_facade_ash.h b/chromeos/components/account_manager/account_manager_facade_ash.h
index 0177c75..bf1ac59 100644
--- a/chromeos/components/account_manager/account_manager_facade_ash.h
+++ b/chromeos/components/account_manager/account_manager_facade_ash.h
@@ -23,6 +23,12 @@
 
   // AccountManagerFacade overrides:
   bool IsInitialized() override;
+  void ShowAddAccountDialog(
+      const AccountAdditionSource& source,
+      base::OnceCallback<void(const AccountAdditionResult& result)> callback)
+      override;
+  void ShowReauthAccountDialog(const AccountAdditionSource& source,
+                               const std::string& email) override;
 
  private:
   AccountManager* const account_manager_;
diff --git a/chromeos/components/camera_app_ui/resources/css/main.css b/chromeos/components/camera_app_ui/resources/css/main.css
index e7d3b8e..9fd93a0 100644
--- a/chromeos/components/camera_app_ui/resources/css/main.css
+++ b/chromeos/components/camera_app_ui/resources/css/main.css
@@ -1020,13 +1020,13 @@
   width: 50%;
 }
 
-#preview-grid-horizontal.animate,
-#preview-grid-horizontal.animate::before {
+#preview-grid-horizontal,
+#preview-grid-horizontal::before {
   transition: height 500ms, visibility 500ms;
 }
 
-#preview-grid-vertical.animate,
-#preview-grid-vertical.animate::before {
+#preview-grid-vertical,
+#preview-grid-vertical::before {
   transition: width 500ms, visibility 500ms;
 }
 
diff --git a/chromeos/components/camera_app_ui/resources/js/views/camera/options.js b/chromeos/components/camera_app_ui/resources/js/views/camera/options.js
index 6a181d5..20a54996 100644
--- a/chromeos/components/camera_app_ui/resources/js/views/camera/options.js
+++ b/chromeos/components/camera_app_ui/resources/js/views/camera/options.js
@@ -95,7 +95,6 @@
     this.audioTrack_ = null;
 
     [['#switch-device', () => this.switchDevice_()],
-     ['#toggle-grid', () => this.animatePreviewGrid_()],
      ['#open-settings', () => nav.open(ViewName.SETTINGS)],
     ]
         .forEach(
@@ -167,15 +166,6 @@
   }
 
   /**
-   * Animates the preview grid.
-   * @private
-   */
-  animatePreviewGrid_() {
-    Array.from(document.querySelector('#preview-grid').children)
-        .forEach((grid) => util.animateOnce(grid));
-  }
-
-  /**
    * Maps MediaTrackSettings.facingMode to CCA facing type.
    * @param {string|undefined} facing The target facingMode to map.
    * @return {!Facing} The mapped CCA facing.
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 76d5229..a07ada52 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-89-4367.0-1609757097-benchmark-89.0.4380.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-89-4367.0-1609757097-benchmark-89.0.4381.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index af6bf85..122eb2b 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-89-4367.0-1609764762-benchmark-89.0.4380.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-89-4367.0-1609764762-benchmark-89.0.4381.0-r1-redacted.afdo.xz
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 6b9c2d5..fb2d342 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -26,8 +26,6 @@
     "fake_assistant_manager_service_impl.h",
     "fake_assistant_settings_impl.cc",
     "fake_assistant_settings_impl.h",
-    "platform/audio_devices.cc",
-    "platform/audio_devices.h",
     "service.cc",
     "service.h",
     "service_context.h",
@@ -82,8 +80,8 @@
       "media_session/assistant_media_session.h",
       "platform/audio_device_owner.cc",
       "platform/audio_device_owner.h",
-      "platform/audio_input_host_impl.cc",
-      "platform/audio_input_host_impl.h",
+      "platform/audio_input_host.cc",
+      "platform/audio_input_host.h",
       "platform/audio_input_impl.cc",
       "platform/audio_input_impl.h",
       "platform/audio_input_provider_impl.cc",
@@ -176,7 +174,6 @@
   ]
 
   sources = [
-    "platform/audio_devices_unittest.cc",
     "service_unittest.cc",
     "test_support/fully_initialized_assistant_state.cc",
     "test_support/fully_initialized_assistant_state.h",
diff --git a/chromeos/services/assistant/assistant_manager_service_delegate_impl.cc b/chromeos/services/assistant/assistant_manager_service_delegate_impl.cc
index 6f6a5ec..a4a82b9 100644
--- a/chromeos/services/assistant/assistant_manager_service_delegate_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_delegate_impl.cc
@@ -9,7 +9,6 @@
 #include <utility>
 
 #include "ash/public/cpp/assistant/assistant_state_base.h"
-#include "chromeos/services/assistant/platform/audio_input_host_impl.h"
 #include "chromeos/services/assistant/platform_api_impl.h"
 #include "chromeos/services/assistant/service_context.h"
 #include "libassistant/shared/internal_api/assistant_manager_internal.h"
@@ -21,26 +20,21 @@
 AssistantManagerServiceDelegateImpl::AssistantManagerServiceDelegateImpl(
     mojo::PendingRemote<device::mojom::BatteryMonitor> battery_monitor,
     ServiceContext* context)
-    : battery_monitor_(std::move(battery_monitor)), context_(context) {}
+    : battery_monitor_(std::move(battery_monitor)),
+      context_(context) {}
 
 AssistantManagerServiceDelegateImpl::~AssistantManagerServiceDelegateImpl() =
     default;
 
-std::unique_ptr<AudioInputHost>
-AssistantManagerServiceDelegateImpl::CreateAudioInputHost() {
-  return std::make_unique<AudioInputHostImpl>(
-      context_->cras_audio_handler(), context_->power_manager_client(),
-      context_->assistant_state()->locale().value());
-}
-
 std::unique_ptr<CrosPlatformApi>
 AssistantManagerServiceDelegateImpl::CreatePlatformApi(
     AssistantMediaSession* media_session,
     scoped_refptr<base::SingleThreadTaskRunner> background_thread_task_runner) {
   return std::make_unique<PlatformApiImpl>(
       media_session, context_->power_manager_client(),
-      std::move(battery_monitor_), context_->main_task_runner(),
-      background_thread_task_runner);
+      context_->cras_audio_handler(), std::move(battery_monitor_),
+      context_->main_task_runner(), background_thread_task_runner,
+      context_->assistant_state()->locale().value());
 }
 
 std::unique_ptr<assistant_client::AssistantManager>
diff --git a/chromeos/services/assistant/assistant_manager_service_delegate_impl.h b/chromeos/services/assistant/assistant_manager_service_delegate_impl.h
index 82836437..1d0eb8586 100644
--- a/chromeos/services/assistant/assistant_manager_service_delegate_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_delegate_impl.h
@@ -25,15 +25,15 @@
       ServiceContext* context);
   ~AssistantManagerServiceDelegateImpl() override;
 
-  // AssistantManagerServiceDelegate implementation:
-  std::unique_ptr<AudioInputHost> CreateAudioInputHost() override;
   std::unique_ptr<CrosPlatformApi> CreatePlatformApi(
       AssistantMediaSession* media_session,
       scoped_refptr<base::SingleThreadTaskRunner> background_thread_task_runner)
       override;
+
   std::unique_ptr<assistant_client::AssistantManager> CreateAssistantManager(
       assistant_client::PlatformApi* platform_api,
       const std::string& lib_assistant_config) override;
+
   assistant_client::AssistantManagerInternal* UnwrapAssistantManagerInternal(
       assistant_client::AssistantManager* assistant_manager) override;
 
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index d4df871..413dd9d 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -40,7 +40,6 @@
 #include "chromeos/services/assistant/public/cpp/device_actions.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
 #include "chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h"
-#include "chromeos/services/assistant/public/cpp/migration/audio_input_host.h"
 #include "chromeos/services/assistant/public/cpp/migration/libassistant_v1_api.h"
 #include "chromeos/services/assistant/public/shared/utils.h"
 #include "chromeos/services/assistant/service_context.h"
@@ -199,10 +198,6 @@
   // To solve this chicken-and-egg problem, we need a separe Initialize() call.
   assistant_proxy_->Initialize(libassistant_service_host_.get());
 
-  audio_input_host_ = delegate_->CreateAudioInputHost();
-
-  platform_api_->InitializeAudioInputHost(*audio_input_host_);
-
   settings_delegate_ =
       std::make_unique<AssistantDeviceSettingsDelegate>(context);
 
@@ -366,7 +361,7 @@
 }
 
 void AssistantManagerServiceImpl::EnableHotword(bool enable) {
-  audio_input_host_->OnHotwordEnabled(enable);
+  platform_api_->OnHotwordEnabled(enable);
 }
 
 void AssistantManagerServiceImpl::SetArcPlayStoreEnabled(bool enable) {
@@ -454,14 +449,14 @@
   DCHECK(assistant_manager());
   DVLOG(1) << __func__;
 
-  audio_input_host_->SetMicState(true);
+  platform_api_->SetMicState(true);
   assistant_manager()->StartAssistantInteraction();
 }
 
 void AssistantManagerServiceImpl::StopActiveInteraction(
     bool cancel_conversation) {
   DVLOG(1) << __func__;
-  audio_input_host_->SetMicState(false);
+  platform_api_->SetMicState(false);
 
   if (!assistant_manager_internal()) {
     VLOG(1) << "Stopping interaction without assistant manager.";
@@ -583,7 +578,7 @@
       &AssistantManagerServiceImpl::OnConversationTurnStartedInternal,
       metadata);
 
-  audio_input_host_->OnConversationTurnStarted();
+  platform_api_->OnConversationTurnStarted();
 
   // Retrieve the cached interaction metadata associated with this conversation
   // turn or construct a new instance if there's no match in the cache.
@@ -612,10 +607,10 @@
   if (resolution != Resolution::NORMAL_WITH_FOLLOW_ON &&
       resolution != Resolution::CANCELLED &&
       resolution != Resolution::BARGE_IN) {
-    audio_input_host_->SetMicState(false);
+    platform_api_->SetMicState(false);
   }
 
-  audio_input_host_->OnConversationTurnFinished();
+  platform_api_->OnConversationTurnFinished();
 
   switch (resolution) {
     // Interaction ended normally.
@@ -1401,11 +1396,6 @@
   return api ? api->assistant_manager_internal() : nullptr;
 }
 
-void AssistantManagerServiceImpl::SetMicState(bool mic_open) {
-  DCHECK(audio_input_host_);
-  audio_input_host_->SetMicState(mic_open);
-}
-
 ServiceControllerProxy& AssistantManagerServiceImpl::service_controller() {
   return assistant_proxy_->service_controller();
 }
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.h b/chromeos/services/assistant/assistant_manager_service_impl.h
index cff59765..26a3d96 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.h
+++ b/chromeos/services/assistant/assistant_manager_service_impl.h
@@ -63,7 +63,6 @@
 class AssistantDeviceSettingsDelegate;
 class AssistantManagerServiceDelegate;
 class AssistantProxy;
-class AudioInputHost;
 class CrosPlatformApi;
 class ServiceContext;
 class ServiceControllerProxy;
@@ -221,7 +220,7 @@
 
   assistant_client::AssistantManager* assistant_manager();
   assistant_client::AssistantManagerInternal* assistant_manager_internal();
-  void SetMicState(bool mic_open);
+  CrosPlatformApi* platform_api() { return platform_api_.get(); }
 
   // assistant_client::MediaManager::Listener overrides:
   void OnPlaybackStateChange(
@@ -314,7 +313,6 @@
   std::unique_ptr<AssistantSettingsImpl> assistant_settings_;
 
   std::unique_ptr<AssistantProxy> assistant_proxy_;
-  std::unique_ptr<AudioInputHost> audio_input_host_;
 
   base::ObserverList<AssistantInteractionSubscriber> interaction_subscribers_;
   mojo::Remote<media_session::mojom::MediaController> media_controller_;
diff --git a/chromeos/services/assistant/assistant_settings_impl.cc b/chromeos/services/assistant/assistant_settings_impl.cc
index f064b87..e68eb8f1 100644
--- a/chromeos/services/assistant/assistant_settings_impl.cc
+++ b/chromeos/services/assistant/assistant_settings_impl.cc
@@ -13,6 +13,7 @@
 #include "chromeos/dbus/util/version_loader.h"
 #include "chromeos/services/assistant/assistant_manager_service_impl.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
+#include "chromeos/services/assistant/public/cpp/migration/cros_platform_api.h"
 #include "chromeos/services/assistant/public/proto/assistant_device_settings_ui.pb.h"
 #include "chromeos/services/assistant/public/proto/settings_ui.pb.h"
 #include "chromeos/services/assistant/service_context.h"
@@ -123,7 +124,7 @@
   DCHECK(main_task_runner()->RunsTasksInCurrentSequence());
   DCHECK(!speaker_id_enrollment_client_);
 
-  assistant_manager_service_->SetMicState(true);
+  assistant_manager_service_->platform_api()->SetMicState(true);
 
   if (!assistant_manager_service_->assistant_manager_internal())
     return;
@@ -152,7 +153,7 @@
   DCHECK(HasStarted(assistant_manager_service_));
   DCHECK(main_task_runner()->RunsTasksInCurrentSequence());
 
-  assistant_manager_service_->SetMicState(false);
+  assistant_manager_service_->platform_api()->SetMicState(false);
 
   if (!assistant_manager_service_->assistant_manager_internal())
     return;
diff --git a/chromeos/services/assistant/platform/audio_devices.cc b/chromeos/services/assistant/platform/audio_devices.cc
deleted file mode 100644
index 0fa34fd..0000000
--- a/chromeos/services/assistant/platform/audio_devices.cc
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright 2020 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 "chromeos/services/assistant/platform/audio_devices.h"
-
-#include "base/metrics/histogram_functions.h"
-#include "base/optional.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "base/system/sys_info.h"
-#include "chromeos/audio/audio_device.h"
-#include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/services/assistant/public/cpp/features.h"
-
-namespace chromeos {
-namespace assistant {
-
-namespace {
-
-constexpr const char kDefaultLocale[] = "en_us";
-
-// Hotword model is expected to have <language>_<region> format with lower
-// case, while the locale in pref is stored as <language>-<region> with region
-// code in capital letters. So we need to convert the pref locale to the
-// correct format.
-// Examples:
-//     "fr"     ->  "fr_fr"
-//     "nl-BE"  ->  "nl_be"
-base::Optional<std::string> ToHotwordModel(std::string pref_locale) {
-  std::vector<std::string> code_strings = base::SplitString(
-      pref_locale, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
-  if (code_strings.size() == 0) {
-    // Note: I am not sure this happens during real operations, but it
-    // definitely happens during the ChromeOS performance tests.
-    return base::nullopt;
-  }
-
-  DCHECK_LT(code_strings.size(), 3u);
-
-  // For locales with language code "en", use "en_all" hotword model.
-  if (code_strings[0] == "en")
-    return "en_all";
-
-  // If the language code and country code happen to be the same, e.g.
-  // France (FR) and French (fr), the locale will be stored as "fr" instead
-  // of "fr-FR" in the profile on Chrome OS.
-  if (code_strings.size() == 1)
-    return code_strings[0] + "_" + code_strings[0];
-
-  return code_strings[0] + "_" + base::ToLowerASCII(code_strings[1]);
-}
-
-const chromeos::AudioDevice* GetHighestPriorityDevice(
-    const chromeos::AudioDevice* left,
-    const chromeos::AudioDevice* right) {
-  if (!left)
-    return right;
-  if (!right)
-    return left;
-  return left->priority < right->priority ? right : left;
-}
-
-base::Optional<uint64_t> IdToOptional(const AudioDevice* device) {
-  if (!device)
-    return base::nullopt;
-  return device->id;
-}
-
-base::Optional<uint64_t> GetHotwordDeviceId(
-    const chromeos::AudioDeviceList& devices) {
-  const chromeos::AudioDevice* result = nullptr;
-
-  for (const chromeos::AudioDevice& device : devices) {
-    if (!device.is_input)
-      continue;
-
-    switch (device.type) {
-      case chromeos::AUDIO_TYPE_HOTWORD:
-        result = GetHighestPriorityDevice(result, &device);
-        break;
-      default:
-        // ignore other devices
-        break;
-    }
-  }
-
-  return IdToOptional(result);
-}
-
-base::Optional<uint64_t> GetPreferredDeviceId(
-    const chromeos::AudioDeviceList& devices) {
-  const chromeos::AudioDevice* result = nullptr;
-
-  for (const chromeos::AudioDevice& device : devices) {
-    if (!device.is_input)
-      continue;
-
-    switch (device.type) {
-      case chromeos::AUDIO_TYPE_USB:
-      case chromeos::AUDIO_TYPE_HEADPHONE:
-      case chromeos::AUDIO_TYPE_INTERNAL_MIC:
-      case chromeos::AUDIO_TYPE_FRONT_MIC:
-        result = GetHighestPriorityDevice(result, &device);
-        break;
-      default:
-        // ignore other devices
-        break;
-    }
-  }
-
-  return IdToOptional(result);
-}
-
-base::Optional<std::string> ToString(base::Optional<uint64_t> int_value) {
-  if (!int_value)
-    return base::nullopt;
-  return base::NumberToString(int_value.value());
-}
-
-}  // namespace
-
-// Observer that will report all changes to the audio devices.
-// It will automatically be triggered when it's created,
-// and will unsubscribe from |CrasAudioHandler| in its destructor.
-class AudioDevices::ScopedCrasAudioHandlerObserver
-    : private CrasAudioHandler::AudioObserver {
- public:
-  ScopedCrasAudioHandlerObserver(CrasAudioHandler* cras_audio_handler,
-                                 AudioDevices* parent)
-      : parent_(parent), cras_audio_handler_(cras_audio_handler) {
-    scoped_observer_.Observe(cras_audio_handler);
-    FetchAudioNodes();
-  }
-  ScopedCrasAudioHandlerObserver(const ScopedCrasAudioHandlerObserver&) =
-      delete;
-  ScopedCrasAudioHandlerObserver& operator=(
-      const ScopedCrasAudioHandlerObserver&) = delete;
-  ~ScopedCrasAudioHandlerObserver() override = default;
-
- private:
-  // CrasAudioHandler::AudioObserver implementation:
-  void OnAudioNodesChanged() override { FetchAudioNodes(); }
-
-  void FetchAudioNodes() {
-    if (!base::SysInfo::IsRunningOnChromeOS())
-      return;
-
-    chromeos::AudioDeviceList audio_devices;
-    cras_audio_handler_->GetAudioDevices(&audio_devices);
-    parent_->SetAudioDevices(audio_devices);
-  }
-
-  AudioDevices* const parent_;
-  // Owned by |AssistantManagerServiceImpl|.
-  CrasAudioHandler* const cras_audio_handler_;
-  base::ScopedObservation<CrasAudioHandler,
-                          CrasAudioHandler::AudioObserver,
-                          &CrasAudioHandler::AddAudioObserver,
-                          &CrasAudioHandler::RemoveAudioObserver>
-      scoped_observer_{this};
-};
-
-// Sends the new hotword model to |cras_audio_handler|. If that fails this class
-// will attempt to set the hotword model to |kDefaultLocale|.
-class AudioDevices::HotwordModelUpdater {
- public:
-  HotwordModelUpdater(CrasAudioHandler* cras_audio_handler,
-                      uint64_t hotword_device,
-                      const std::string& locale)
-      : cras_audio_handler_(cras_audio_handler),
-        hotword_device_(hotword_device),
-        locale_(locale) {
-    SendUpdate();
-  }
-
-  HotwordModelUpdater(const HotwordModelUpdater&) = delete;
-  HotwordModelUpdater& operator=(const HotwordModelUpdater&) = delete;
-  ~HotwordModelUpdater() = default;
-
- private:
-  void SendUpdate() {
-    std::string hotword_model =
-        ToHotwordModel(locale_).value_or(kDefaultLocale);
-
-    cras_audio_handler_->SetHotwordModel(
-        hotword_device_, hotword_model,
-        base::BindOnce(&HotwordModelUpdater::SetDspHotwordLocaleCallback,
-                       weak_factory_.GetWeakPtr(), hotword_model));
-  }
-
-  void SetDspHotwordLocaleCallback(std::string pref_locale, bool success) {
-    base::UmaHistogramBoolean("Assistant.SetDspHotwordLocale", success);
-    if (success) {
-      VLOG(2) << "Successfully changed audio hotword model";
-      return;
-    }
-
-    LOG(ERROR) << "Set " << pref_locale
-               << " hotword model failed, fallback to default locale.";
-    // Reset the locale to the default value if we failed to sync it to the
-    // locale stored in user's pref.
-    cras_audio_handler_->SetHotwordModel(
-        hotword_device_, /* hotword_model */ kDefaultLocale,
-        base::BindOnce([](bool success) {
-          if (!success)
-            LOG(ERROR) << "Reset to default hotword model failed.";
-        }));
-  }
-
-  CrasAudioHandler* const cras_audio_handler_;
-  uint64_t hotword_device_;
-  std::string locale_;
-
-  base::WeakPtrFactory<HotwordModelUpdater> weak_factory_{this};
-};
-
-AudioDevices::AudioDevices(CrasAudioHandler* cras_audio_handler,
-                           const std::string& locale)
-    : scoped_cras_audio_handler_observer_(
-          std::make_unique<ScopedCrasAudioHandlerObserver>(cras_audio_handler,
-                                                           this)),
-      cras_audio_handler_(cras_audio_handler),
-      locale_(locale) {}
-
-AudioDevices::~AudioDevices() = default;
-
-void AudioDevices::AddAndFireObserver(Observer* observer) {
-  DCHECK(observer);
-  observers_.AddObserver(observer);
-
-  observer->SetHotwordDeviceId(ToString(hotword_device_id_));
-  observer->SetDeviceId(ToString(device_id_));
-}
-
-void AudioDevices::RemoveObserver(Observer* observer) {
-  observers_.RemoveObserver(observer);
-}
-
-void AudioDevices::SetLocale(const std::string& locale) {
-  locale_ = locale;
-  UpdateHotwordModel();
-}
-
-void AudioDevices::SetAudioDevicesForTest(
-    const chromeos::AudioDeviceList& audio_devices) {
-  SetAudioDevices(audio_devices);
-}
-
-void AudioDevices::SetAudioDevices(const chromeos::AudioDeviceList& devices) {
-  UpdateHotwordDeviceId(devices);
-  UpdateDeviceId(devices);
-  UpdateHotwordModel();
-}
-
-void AudioDevices::UpdateHotwordDeviceId(
-    const chromeos::AudioDeviceList& devices) {
-  hotword_device_id_ = GetHotwordDeviceId(devices);
-
-  VLOG(2) << "Changed audio hotword input device to "
-          << ToString(hotword_device_id_).value_or("<none>");
-
-  for (auto& observer : observers_)
-    observer.SetHotwordDeviceId(ToString(hotword_device_id_));
-}
-
-void AudioDevices::UpdateDeviceId(const chromeos::AudioDeviceList& devices) {
-  device_id_ = GetPreferredDeviceId(devices);
-
-  VLOG(2) << "Changed audio input device to "
-          << ToString(device_id_).value_or("<none>");
-
-  for (auto& observer : observers_)
-    observer.SetDeviceId(ToString(device_id_));
-}
-
-void AudioDevices::UpdateHotwordModel() {
-  if (!hotword_device_id_)
-    return;
-
-  if (!features::IsDspHotwordEnabled())
-    return;
-
-  hotword_model_updater_ = std::make_unique<HotwordModelUpdater>(
-      cras_audio_handler_, hotword_device_id_.value(), locale_);
-}
-
-}  // namespace assistant
-}  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_devices.h b/chromeos/services/assistant/platform/audio_devices.h
deleted file mode 100644
index f0d70d5..0000000
--- a/chromeos/services/assistant/platform/audio_devices.h
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2020 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 CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICES_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICES_H_
-
-#include <cstdint>
-#include "base/component_export.h"
-#include "base/observer_list.h"
-#include "base/optional.h"
-#include "base/scoped_observation.h"
-#include "chromeos/audio/audio_device.h"
-
-namespace chromeos {
-class CrasAudioHandler;
-}  // namespace chromeos
-
-namespace chromeos {
-namespace assistant {
-
-// This class will monitor the available audio devices (through
-// |CrasAudioHandler|), and select the devices to use for audio input (both
-// regular input and hotword detection).
-// When the selected devices change, this class will:
-//     - Inform the observers.
-//     - Find the hotword model to use, and send it to
-//       CrasAudioHandler::SetHotwordModel().
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AudioDevices {
- public:
-  class Observer : public base::CheckedObserver {
-   public:
-    ~Observer() override = default;
-
-    // Set the input device to use for audio capture.
-    virtual void SetDeviceId(const base::Optional<std::string>& device_id) = 0;
-    // Set the input device to use for hardware based hotword detection.
-    virtual void SetHotwordDeviceId(
-        const base::Optional<std::string>& device_id) = 0;
-  };
-
-  AudioDevices(CrasAudioHandler* cras_audio_handler, const std::string& locale);
-  AudioDevices(const AudioDevices&) = delete;
-  AudioDevices& operator=(const AudioDevices&) = delete;
-  ~AudioDevices();
-
-  void AddAndFireObserver(Observer*);
-  void RemoveObserver(Observer*);
-
-  void SetLocale(const std::string& locale);
-
-  // Used during unittests to simulate an update to the list of available audio
-  // devices.
-  void SetAudioDevicesForTest(const chromeos::AudioDeviceList& audio_devices);
-
-  using ScopedObservation =
-      base::ScopedObservation<AudioDevices,
-                              Observer,
-                              &AudioDevices::AddAndFireObserver,
-                              &AudioDevices::RemoveObserver>;
-
- private:
-  class ScopedCrasAudioHandlerObserver;
-  class HotwordModelUpdater;
-
-  void SetAudioDevices(const chromeos::AudioDeviceList& audio_devices);
-  void UpdateHotwordDeviceId(const chromeos::AudioDeviceList& devices);
-  void UpdateDeviceId(const chromeos::AudioDeviceList& devices);
-  void UpdateHotwordModel();
-
-  // Observes changes to the available audio devices, and sends the list of
-  // devices to SetAudioDevices().
-  std::unique_ptr<ScopedCrasAudioHandlerObserver>
-      scoped_cras_audio_handler_observer_;
-
-  // Handles the asynchronous nature of sending a new hotword model to
-  // |cras_audio_handler_|.
-  std::unique_ptr<HotwordModelUpdater> hotword_model_updater_;
-
-  base::ObserverList<Observer> observers_;
-
-  // Owned by |AssistantManagerServiceImpl|.
-  CrasAudioHandler* const cras_audio_handler_;
-
-  std::string locale_;
-  base::Optional<uint64_t> hotword_device_id_;
-  base::Optional<uint64_t> device_id_;
-};
-
-}  // namespace assistant
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_DEVICES_H_
diff --git a/chromeos/services/assistant/platform/audio_devices_unittest.cc b/chromeos/services/assistant/platform/audio_devices_unittest.cc
deleted file mode 100644
index dd1cb9f..0000000
--- a/chromeos/services/assistant/platform/audio_devices_unittest.cc
+++ /dev/null
@@ -1,373 +0,0 @@
-// Copyright 2020 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 "chromeos/services/assistant/platform/audio_devices.h"
-
-#include "base/run_loop.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "chromeos/audio/audio_device.h"
-#include "chromeos/audio/cras_audio_handler.h"
-#include "chromeos/dbus/audio/fake_cras_audio_client.h"
-#include "chromeos/services/assistant/public/cpp/features.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace chromeos {
-namespace assistant {
-
-namespace {
-
-using ::testing::_;
-
-constexpr const char kDefaultLocale[] = "en_us";
-
-class FakeAudioDevicesObserver : public AudioDevices::Observer {
- public:
-  FakeAudioDevicesObserver() = default;
-  FakeAudioDevicesObserver(const FakeAudioDevicesObserver&) = delete;
-  FakeAudioDevicesObserver& operator=(const FakeAudioDevicesObserver&) = delete;
-  ~FakeAudioDevicesObserver() override = default;
-
-  // AudioDevices::Observer implementation
-  void SetDeviceId(const base::Optional<std::string>& device_id) override {
-    preferred_device_id_ = device_id;
-  }
-  void SetHotwordDeviceId(
-      const base::Optional<std::string>& device_id) override {
-    hotword_device_id_ = device_id;
-  }
-
-  std::string preferred_device_id() {
-    return preferred_device_id_.value_or("<none>");
-  }
-
-  std::string hotword_device_id() {
-    return hotword_device_id_.value_or("<none>");
-  }
-
- private:
-  base::Optional<std::string> preferred_device_id_;
-  base::Optional<std::string> hotword_device_id_;
-};
-
-class DeviceBuilder {
- public:
-  explicit DeviceBuilder(AudioDeviceType type) {
-    result_.type = type;
-    result_.is_input = true;
-  }
-  DeviceBuilder(const DeviceBuilder&) = delete;
-  DeviceBuilder& operator=(const DeviceBuilder&) = delete;
-  ~DeviceBuilder() = default;
-
-  DeviceBuilder& WithId(int id) {
-    result_.id = id;
-    return *this;
-  }
-
-  DeviceBuilder& WithPriority(int priority) {
-    result_.priority = priority;
-    return *this;
-  }
-
-  DeviceBuilder& WithIsInput(bool is_input) {
-    result_.is_input = is_input;
-    return *this;
-  }
-
-  AudioDevice Build() const { return result_; }
-
- private:
-  AudioDevice result_;
-};
-
-// Mock for |CrosAudioClient|. This inherits from |FakeCrasAudioClient| so we
-// only have to mock the methods we're interested in.
-// It will automatically be installed as the global singleton in its
-// constructor, and removed in the destructor.
-class ScopedCrasAudioClientMock : public FakeCrasAudioClient {
- public:
-  ScopedCrasAudioClientMock() = default;
-  ScopedCrasAudioClientMock(ScopedCrasAudioClientMock&) = delete;
-  ScopedCrasAudioClientMock& operator=(ScopedCrasAudioClientMock&) = delete;
-  ~ScopedCrasAudioClientMock() override = default;
-
-  MOCK_METHOD(void,
-              SetHotwordModel,
-              (uint64_t node_id,
-               const std::string& hotword_model,
-               VoidDBusMethodCallback callback));
-};
-
-class ScopedCrasAudioHandler {
- public:
-  ScopedCrasAudioHandler() { CrasAudioHandler::InitializeForTesting(); }
-  ScopedCrasAudioHandler(const ScopedCrasAudioHandler&) = delete;
-  ScopedCrasAudioHandler& operator=(const ScopedCrasAudioHandler&) = delete;
-  ~ScopedCrasAudioHandler() { CrasAudioHandler::Shutdown(); }
-
-  CrasAudioHandler* Get() { return CrasAudioHandler::Get(); }
-};
-
-}  // namespace
-
-class AssistantAudioDevicesTest : public testing::Test {
- public:
-  AssistantAudioDevicesTest()
-      : audio_devices_(cras_audio_handler_.Get(), "pref-locale") {
-    // Enable DSP feature flag.
-    scoped_feature_list_.InitAndEnableFeature(features::kEnableDspHotword);
-  }
-
-  AssistantAudioDevicesTest(const AssistantAudioDevicesTest&) = delete;
-  AssistantAudioDevicesTest& operator=(const AssistantAudioDevicesTest&) =
-      delete;
-  ~AssistantAudioDevicesTest() override = default;
-
-  AudioDevices& audio_devices() { return audio_devices_; }
-
-  ScopedCrasAudioClientMock& cras_audio_client_mock() {
-    return cras_audio_client_mock_;
-  }
-
-  void UpdateDeviceList(const AudioDeviceList& devices) {
-    audio_devices().SetAudioDevicesForTest(devices);
-  }
-
- private:
-  base::test::TaskEnvironment task_environment_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  testing::NiceMock<ScopedCrasAudioClientMock> cras_audio_client_mock_;
-  ScopedCrasAudioHandler cras_audio_handler_;
-  AudioDevices audio_devices_;
-};
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendHotwordDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(111).Build()});
-
-  EXPECT_EQ("111", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendUsbDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_USB).WithId(222).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("222", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendHeadphonesDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_HEADPHONE).WithId(333).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("333", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendInternalMicDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList(
-      {DeviceBuilder(AUDIO_TYPE_INTERNAL_MIC).WithId(444).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("444", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldSendFrontMicDeviceToObserver) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_FRONT_MIC).WithId(555).Build()});
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("555", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldUseHighestPriorityHotwordDevice) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(111).WithPriority(1).Build(),
-      DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(555).WithPriority(5).Build(),
-      DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(222).WithPriority(2).Build(),
-  });
-
-  EXPECT_EQ("555", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldIgnoreNonInputHotwordDevices) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(111).WithIsInput(false).Build(),
-      DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(222).WithIsInput(true).Build(),
-      DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(333).WithIsInput(false).Build(),
-  });
-
-  EXPECT_EQ("222", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldUseHighestPriorityDevice) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AUDIO_TYPE_USB).WithId(111).WithPriority(1).Build(),
-      DeviceBuilder(AUDIO_TYPE_USB).WithId(555).WithPriority(5).Build(),
-      DeviceBuilder(AUDIO_TYPE_USB).WithId(222).WithPriority(2).Build(),
-  });
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("555", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldIgnoreNonInputDevices) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AUDIO_TYPE_USB).WithId(111).WithIsInput(false).Build(),
-      DeviceBuilder(AUDIO_TYPE_USB).WithId(222).WithIsInput(true).Build(),
-      DeviceBuilder(AUDIO_TYPE_USB).WithId(333).WithIsInput(false).Build(),
-  });
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("222", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldIgnoreUnsupportedDeviceTypes) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AUDIO_TYPE_BLUETOOTH).WithId(2).Build(),
-      DeviceBuilder(AUDIO_TYPE_BLUETOOTH_NB_MIC).WithId(3).Build(),
-      DeviceBuilder(AUDIO_TYPE_HDMI).WithId(4).Build(),
-      DeviceBuilder(AUDIO_TYPE_INTERNAL_SPEAKER).WithId(5).Build(),
-      DeviceBuilder(AUDIO_TYPE_REAR_MIC).WithId(6).Build(),
-      DeviceBuilder(AUDIO_TYPE_KEYBOARD_MIC).WithId(7).Build(),
-      DeviceBuilder(AUDIO_TYPE_LINEOUT).WithId(8).Build(),
-      DeviceBuilder(AUDIO_TYPE_POST_MIX_LOOPBACK).WithId(9).Build(),
-      DeviceBuilder(AUDIO_TYPE_POST_DSP_LOOPBACK).WithId(10).Build(),
-      DeviceBuilder(AUDIO_TYPE_ALSA_LOOPBACK).WithId(11).Build(),
-      DeviceBuilder(AUDIO_TYPE_OTHER).WithId(12).Build(),
-  });
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldFireObserverWhenAdded) {
-  UpdateDeviceList({
-      DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(111).Build(),
-      DeviceBuilder(AUDIO_TYPE_USB).WithId(222).Build(),
-  });
-
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-
-  EXPECT_EQ("111", observer.hotword_device_id());
-  EXPECT_EQ("222", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldNotFireObserverAfterItsRemoved) {
-  FakeAudioDevicesObserver observer;
-  audio_devices().AddAndFireObserver(&observer);
-  audio_devices().RemoveObserver(&observer);
-
-  UpdateDeviceList({
-      DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(111).Build(),
-      DeviceBuilder(AUDIO_TYPE_USB).WithId(222).Build(),
-  });
-
-  EXPECT_EQ("<none>", observer.hotword_device_id());
-  EXPECT_EQ("<none>", observer.preferred_device_id());
-}
-
-TEST_F(AssistantAudioDevicesTest,
-       ShouldUpdateHotwordModelWhenHotwordDeviceIsAdded) {
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel);
-
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_HOTWORD).Build()});
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldFormatLocaleToHotwordModel) {
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(111).Build()});
-
-  // Normal case
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "nl_be", _));
-  audio_devices().SetLocale("nl-BE");
-  base::RunLoop().RunUntilIdle();
-
-  // Handle the case where country code and language code are the same
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "fr_fr", _));
-  audio_devices().SetLocale("fr");
-  base::RunLoop().RunUntilIdle();
-
-  // use "en_all" for all english locales
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "en_all", _));
-  audio_devices().SetLocale("en-US");
-  base::RunLoop().RunUntilIdle();
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldUseDefaultLocaleIfUserPrefIsRejected) {
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_HOTWORD).WithId(222).Build()});
-
-  EXPECT_CALL(cras_audio_client_mock(),
-              SetHotwordModel(_, "rejected_locale", _))
-      .WillOnce([](uint64_t node_id, const std::string&,
-                   VoidDBusMethodCallback callback) {
-        // Report failure to change the locale
-        std::move(callback).Run(/*success=*/false);
-      });
-
-  EXPECT_CALL(cras_audio_client_mock(),
-              SetHotwordModel(222, kDefaultLocale, _));
-
-  audio_devices().SetLocale("rejected-LOCALE");
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldUseDefaultLocaleIfUserPrefIsEmpty) {
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_HOTWORD).Build()});
-
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(_, kDefaultLocale, _));
-
-  audio_devices().SetLocale("");
-}
-
-TEST_F(AssistantAudioDevicesTest, ShouldDoNothingIfUserPrefIsAccepted) {
-  UpdateDeviceList({DeviceBuilder(AUDIO_TYPE_HOTWORD).Build()});
-
-  EXPECT_CALL(cras_audio_client_mock(),
-              SetHotwordModel(_, "accepted_locale", _))
-      .WillOnce([](uint64_t node_id, const std::string&,
-                   VoidDBusMethodCallback callback) {
-        // Accept the change to the locale.
-        std::move(callback).Run(/*success=*/true);
-      });
-
-  // Do not expect a second call if change of locale is accepted
-  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(_, kDefaultLocale, _))
-      .Times(0);
-
-  audio_devices().SetLocale("accepted-LOCALE");
-}
-
-}  // namespace assistant
-}  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_input_host.cc b/chromeos/services/assistant/platform/audio_input_host.cc
new file mode 100644
index 0000000..cb67acd
--- /dev/null
+++ b/chromeos/services/assistant/platform/audio_input_host.cc
@@ -0,0 +1,174 @@
+// Copyright 2020 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 "chromeos/services/assistant/platform/audio_input_host.h"
+
+#include "base/check.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/optional.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "chromeos/audio/cras_audio_handler.h"
+#include "chromeos/services/assistant/platform/audio_input_impl.h"
+#include "chromeos/services/assistant/public/cpp/features.h"
+
+namespace chromeos {
+namespace assistant {
+
+namespace {
+
+constexpr const char kDefaultLocale[] = "en_us";
+
+AudioInputImpl::LidState ConvertLidState(
+    chromeos::PowerManagerClient::LidState state) {
+  switch (state) {
+    case chromeos::PowerManagerClient::LidState::CLOSED:
+      return AudioInputImpl::LidState::kClosed;
+    case chromeos::PowerManagerClient::LidState::OPEN:
+      return AudioInputImpl::LidState::kOpen;
+    case chromeos::PowerManagerClient::LidState::NOT_PRESENT:
+      // If there is no lid, it can't be closed.
+      return AudioInputImpl::LidState::kOpen;
+  }
+}
+
+// Hotword model is expected to have <language>_<region> format with lower
+// case, while the locale in pref is stored as <language>-<region> with region
+// code in capital letters. So we need to convert the pref locale to the
+// correct format.
+// Examples:
+//     "fr"     ->  "fr_fr"
+//     "nl-BE"  ->  "nl_be"
+base::Optional<std::string> ToHotwordModel(std::string pref_locale) {
+  std::vector<std::string> code_strings = base::SplitString(
+      pref_locale, "-", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+  if (code_strings.size() == 0) {
+    // Note: I am not sure this happens during real operations, but it
+    // definitely happens during the ChromeOS performance tests.
+    return base::nullopt;
+  }
+
+  DCHECK_LT(code_strings.size(), 3u);
+
+  // For locales with language code "en", use "en_all" hotword model.
+  if (code_strings[0] == "en")
+    return "en_all";
+
+  // If the language code and country code happen to be the same, e.g.
+  // France (FR) and French (fr), the locale will be stored as "fr" instead
+  // of "fr-FR" in the profile on Chrome OS.
+  if (code_strings.size() == 1)
+    return code_strings[0] + "_" + code_strings[0];
+
+  return code_strings[0] + "_" + base::ToLowerASCII(code_strings[1]);
+}
+
+}  // namespace
+
+chromeos::assistant::AudioInputHost::AudioInputHost(
+    AudioInputImpl* audio_input,
+    CrasAudioHandler* cras_audio_handler,
+    chromeos::PowerManagerClient* power_manager_client)
+    : audio_input_(audio_input),
+      cras_audio_handler_(cras_audio_handler),
+      power_manager_client_(power_manager_client),
+      power_manager_client_observer_(this) {
+  DCHECK(audio_input_);
+  DCHECK(cras_audio_handler_);
+  DCHECK(power_manager_client_);
+
+  power_manager_client_observer_.Observe(power_manager_client);
+  power_manager_client->GetSwitchStates(base::BindOnce(
+      &AudioInputHost::OnInitialLidStateReceived, weak_factory_.GetWeakPtr()));
+}
+
+AudioInputHost::~AudioInputHost() = default;
+
+void AudioInputHost::SetMicState(bool mic_open) {
+  audio_input_->SetMicState(mic_open);
+}
+
+void AudioInputHost::SetDeviceId(const std::string& device_id) {
+  audio_input_->SetDeviceId(device_id);
+}
+
+void AudioInputHost::OnConversationTurnStarted() {
+  audio_input_->OnConversationTurnStarted();
+  // Inform power manager of a wake notification when Libassistant
+  // recognized hotword and started a conversation. We intentionally
+  // avoid using |NotifyUserActivity| because it is not suitable for
+  // this case according to the Platform team.
+  power_manager_client_->NotifyWakeNotification();
+}
+
+void AudioInputHost::OnConversationTurnFinished() {
+  audio_input_->OnConversationTurnFinished();
+}
+
+void AudioInputHost::OnHotwordEnabled(bool enable) {
+  audio_input_->OnHotwordEnabled(enable);
+}
+
+void AudioInputHost::SetHotwordDeviceId(const std::string& device_id) {
+  hotword_device_id_ = device_id;
+  audio_input_->SetHotwordDeviceId(device_id);
+}
+
+void AudioInputHost::SetDspHotwordLocale(std::string pref_locale) {
+  if (!features::IsDspHotwordEnabled())
+    return;
+
+  std::string hotword_model =
+      ToHotwordModel(pref_locale).value_or(kDefaultLocale);
+
+  cras_audio_handler_->SetHotwordModel(
+      GetDspNodeId(), hotword_model,
+      base::BindOnce(&AudioInputHost::SetDspHotwordLocaleCallback,
+                     weak_factory_.GetWeakPtr(), hotword_model));
+}
+
+void AudioInputHost::SetDspHotwordLocaleCallback(std::string pref_locale,
+                                                 bool success) {
+  base::UmaHistogramBoolean("Assistant.SetDspHotwordLocale", success);
+  if (success)
+    return;
+
+  LOG(ERROR) << "Set " << pref_locale
+             << " hotword model failed, fallback to default locale.";
+  // Reset the locale to the default value if we failed to sync it to the locale
+  // stored in user's pref.
+  cras_audio_handler_->SetHotwordModel(
+      GetDspNodeId(), /* hotword_model */ kDefaultLocale,
+      base::BindOnce([](bool success) {
+        if (!success)
+          LOG(ERROR) << "Reset to default hotword model failed.";
+      }));
+}
+
+uint64_t AudioInputHost::GetDspNodeId() const {
+  DCHECK(!hotword_device_id_.empty());
+  uint64_t result;
+  bool success = base::StringToUint64(hotword_device_id_, &result);
+  DCHECK(success) << "Invalid hotword device id '" << hotword_device_id_ << "'";
+  return result;
+}
+
+void AudioInputHost::LidEventReceived(
+    chromeos::PowerManagerClient::LidState state,
+    base::TimeTicks timestamp) {
+  // Lid switch event still gets fired during system suspend, which enables
+  // us to stop DSP recording correctly when user closes lid after the device
+  // goes to sleep.
+  audio_input_->OnLidStateChanged(ConvertLidState(state));
+}
+
+void AudioInputHost::OnInitialLidStateReceived(
+    base::Optional<chromeos::PowerManagerClient::SwitchStates> switch_states) {
+  if (switch_states.has_value())
+    audio_input_->OnLidStateChanged(ConvertLidState(switch_states->lid_state));
+}
+
+}  // namespace assistant
+}  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_input_host.h b/chromeos/services/assistant/platform/audio_input_host.h
new file mode 100644
index 0000000..2c0c710
--- /dev/null
+++ b/chromeos/services/assistant/platform/audio_input_host.h
@@ -0,0 +1,86 @@
+// Copyright 2020 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 CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_H_
+#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_H_
+
+#include <string>
+
+#include "base/component_export.h"
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "chromeos/dbus/power/power_manager_client.h"
+
+namespace chromeos {
+class CrasAudioHandler;
+}  // namespace chromeos
+
+namespace chromeos {
+namespace assistant {
+
+class AudioInputImpl;
+
+// Class that provides the bridge between the ChromeOS UI thread and the
+// Libassistant audio input class.
+// The goal is that |AudioInputImpl| no longer depends on any external events.
+// This will allow us to move it to the Libassistant mojom service (at which
+// point this class will talk to the Libassistant mojom service).
+class COMPONENT_EXPORT(ASSISTANT_SERVICE) AudioInputHost
+    : public chromeos::PowerManagerClient::Observer {
+ public:
+  AudioInputHost(AudioInputImpl* audio_input,
+                 CrasAudioHandler* cras_audio_handler,
+                 chromeos::PowerManagerClient* power_manager_client);
+  AudioInputHost(AudioInputHost&) = delete;
+  AudioInputHost& operator=(AudioInputHost&) = delete;
+  ~AudioInputHost() override;
+
+  // Called when the mic state associated with the interaction is changed.
+  void SetMicState(bool mic_open);
+
+  // Setting the input device to use for audio capture.
+  void SetDeviceId(const std::string& device_id);
+
+  // Called when hotword enabled status changed.
+  void OnHotwordEnabled(bool enable);
+
+  // Setting the hotword input device with hardware based hotword detection.
+  void SetHotwordDeviceId(const std::string& device_id);
+
+  // Setting the hotword locale for the input device with DSP support.
+  void SetDspHotwordLocale(std::string pref_locale);
+
+  void OnConversationTurnStarted();
+  void OnConversationTurnFinished();
+
+ private:
+  void SetDspHotwordLocaleCallback(std::string pref_locale, bool success);
+
+  uint64_t GetDspNodeId() const;
+
+  // chromeos::PowerManagerClient::Observer overrides:
+  void LidEventReceived(chromeos::PowerManagerClient::LidState state,
+                        base::TimeTicks timestamp) override;
+
+  void OnInitialLidStateReceived(
+      base::Optional<chromeos::PowerManagerClient::SwitchStates> switch_states);
+
+  // Owned by |PlatformApiImpl| which also owns |this|.
+  AudioInputImpl* const audio_input_;
+  CrasAudioHandler* const cras_audio_handler_;
+  chromeos::PowerManagerClient* const power_manager_client_;
+  base::ScopedObservation<chromeos::PowerManagerClient,
+                          chromeos::PowerManagerClient::Observer>
+      power_manager_client_observer_;
+
+  // Hotword input device used for hardware based hotword detection.
+  std::string hotword_device_id_;
+
+  base::WeakPtrFactory<AudioInputHost> weak_factory_{this};
+};
+
+}  // namespace assistant
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_H_
diff --git a/chromeos/services/assistant/platform/audio_input_host_impl.cc b/chromeos/services/assistant/platform/audio_input_host_impl.cc
deleted file mode 100644
index 4f6dcd6e..0000000
--- a/chromeos/services/assistant/platform/audio_input_host_impl.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2021 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 "chromeos/services/assistant/platform/audio_input_host_impl.h"
-
-#include "base/check.h"
-#include "base/optional.h"
-#include "chromeos/services/assistant/platform/audio_devices.h"
-#include "chromeos/services/assistant/platform/audio_input_impl.h"
-#include "chromeos/services/assistant/public/cpp/features.h"
-
-namespace chromeos {
-namespace assistant {
-
-namespace {
-
-AudioInputImpl::LidState ConvertLidState(
-    chromeos::PowerManagerClient::LidState state) {
-  switch (state) {
-    case chromeos::PowerManagerClient::LidState::CLOSED:
-      return AudioInputImpl::LidState::kClosed;
-    case chromeos::PowerManagerClient::LidState::OPEN:
-      return AudioInputImpl::LidState::kOpen;
-    case chromeos::PowerManagerClient::LidState::NOT_PRESENT:
-      // If there is no lid, it can't be closed.
-      return AudioInputImpl::LidState::kOpen;
-  }
-}
-
-}  // namespace
-
-chromeos::assistant::AudioInputHostImpl::AudioInputHostImpl(
-    CrasAudioHandler* cras_audio_handler,
-    chromeos::PowerManagerClient* power_manager_client,
-    const std::string& locale)
-    : power_manager_client_(power_manager_client),
-      power_manager_client_observer_(this),
-      audio_devices_(cras_audio_handler, locale) {
-  DCHECK(power_manager_client_);
-}
-
-AudioInputHostImpl::~AudioInputHostImpl() = default;
-
-void AudioInputHostImpl::Initialize(AudioInputImpl* audio_input) {
-  DCHECK(audio_input);
-  audio_input_ = audio_input;
-  audio_devices_observation_.Observe(&audio_devices_);
-  power_manager_client_observer_.Observe(power_manager_client_);
-  power_manager_client_->GetSwitchStates(
-      base::BindOnce(&AudioInputHostImpl::OnInitialLidStateReceived,
-                     weak_factory_.GetWeakPtr()));
-}
-
-void AudioInputHostImpl::SetMicState(bool mic_open) {
-  audio_input_->SetMicState(mic_open);
-}
-
-void AudioInputHostImpl::SetDeviceId(
-    const base::Optional<std::string>& device_id) {
-  audio_input_->SetDeviceId(device_id.value_or(""));
-}
-
-void AudioInputHostImpl::OnConversationTurnStarted() {
-  audio_input_->OnConversationTurnStarted();
-  // Inform power manager of a wake notification when Libassistant
-  // recognized hotword and started a conversation. We intentionally
-  // avoid using |NotifyUserActivity| because it is not suitable for
-  // this case according to the Platform team.
-  power_manager_client_->NotifyWakeNotification();
-}
-
-void AudioInputHostImpl::OnConversationTurnFinished() {
-  audio_input_->OnConversationTurnFinished();
-}
-
-void AudioInputHostImpl::OnHotwordEnabled(bool enable) {
-  audio_input_->OnHotwordEnabled(enable);
-}
-
-void AudioInputHostImpl::SetHotwordDeviceId(
-    const base::Optional<std::string>& device_id) {
-  audio_input_->SetHotwordDeviceId(device_id.value_or(""));
-}
-
-void AudioInputHostImpl::LidEventReceived(
-    chromeos::PowerManagerClient::LidState state,
-    base::TimeTicks timestamp) {
-  // Lid switch event still gets fired during system suspend, which enables
-  // us to stop DSP recording correctly when user closes lid after the device
-  // goes to sleep.
-  audio_input_->OnLidStateChanged(ConvertLidState(state));
-}
-
-void AudioInputHostImpl::OnInitialLidStateReceived(
-    base::Optional<chromeos::PowerManagerClient::SwitchStates> switch_states) {
-  if (switch_states.has_value())
-    audio_input_->OnLidStateChanged(ConvertLidState(switch_states->lid_state));
-}
-
-}  // namespace assistant
-}  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_input_host_impl.h b/chromeos/services/assistant/platform/audio_input_host_impl.h
deleted file mode 100644
index fc3583a..0000000
--- a/chromeos/services/assistant/platform/audio_input_host_impl.h
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright 2021 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 CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_IMPL_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_IMPL_H_
-
-#include "chromeos/services/assistant/public/cpp/migration/audio_input_host.h"
-
-#include <string>
-
-#include "base/component_export.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "base/scoped_observation.h"
-#include "base/time/time.h"
-#include "chromeos/dbus/power/power_manager_client.h"
-#include "chromeos/services/assistant/platform/audio_devices.h"
-
-namespace chromeos {
-namespace assistant {
-
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AudioInputHostImpl
-    : public AudioInputHost,
-      private chromeos::PowerManagerClient::Observer,
-      private AudioDevices::Observer {
- public:
-  AudioInputHostImpl(CrasAudioHandler* cras_audio_handler,
-                     chromeos::PowerManagerClient* power_manager_client,
-                     const std::string& locale);
-  AudioInputHostImpl(const AudioInputHostImpl&) = delete;
-  AudioInputHostImpl& operator=(const AudioInputHostImpl&) = delete;
-  ~AudioInputHostImpl() override;
-
-  // AudioInputHost implementation:
-  void Initialize(AudioInputImpl* audio_input) override;
-  void SetMicState(bool mic_open) override;
-  void OnHotwordEnabled(bool enable) override;
-  void OnConversationTurnStarted() override;
-  void OnConversationTurnFinished() override;
-
-  // AudioDevices::Observer implementation:
-  void SetDeviceId(const base::Optional<std::string>& device_id) override;
-  void SetHotwordDeviceId(
-      const base::Optional<std::string>& device_id) override;
-
- private:
-  // chromeos::PowerManagerClient::Observer overrides:
-  void LidEventReceived(chromeos::PowerManagerClient::LidState state,
-                        base::TimeTicks timestamp) override;
-
-  void OnInitialLidStateReceived(
-      base::Optional<chromeos::PowerManagerClient::SwitchStates> switch_states);
-
-  // Owned by |PlatformApiImpl| which also owns |this|.
-  AudioInputImpl* audio_input_ = nullptr;
-  chromeos::PowerManagerClient* const power_manager_client_;
-  base::ScopedObservation<chromeos::PowerManagerClient,
-                          chromeos::PowerManagerClient::Observer>
-      power_manager_client_observer_;
-
-  // Observes available audio devices and will set device-id/hotword-device-id
-  // accordingly.
-  AudioDevices audio_devices_;
-  AudioDevices::ScopedObservation audio_devices_observation_{this};
-
-  base::WeakPtrFactory<AudioInputHostImpl> weak_factory_{this};
-};
-
-}  // namespace assistant
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PLATFORM_AUDIO_INPUT_HOST_IMPL_H_
diff --git a/chromeos/services/assistant/platform/audio_input_impl.cc b/chromeos/services/assistant/platform/audio_input_impl.cc
index 1e14738b..66b944b 100644
--- a/chromeos/services/assistant/platform/audio_input_impl.cc
+++ b/chromeos/services/assistant/platform/audio_input_impl.cc
@@ -283,7 +283,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(observer_sequence_checker_);
   if (open_audio_stream_)
     VLOG(1) << open_audio_stream_->device_id() << " remove observer";
-
   bool have_no_observer = false;
   {
     base::AutoLock lock(lock_);
diff --git a/chromeos/services/assistant/platform/audio_input_impl_unittest.cc b/chromeos/services/assistant/platform/audio_input_impl_unittest.cc
index 28b6d5f..5d264cb 100644
--- a/chromeos/services/assistant/platform/audio_input_impl_unittest.cc
+++ b/chromeos/services/assistant/platform/audio_input_impl_unittest.cc
@@ -11,8 +11,9 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "chromeos/audio/cras_audio_handler.h"
+#include "chromeos/dbus/audio/fake_cras_audio_client.h"
 #include "chromeos/dbus/power/fake_power_manager_client.h"
-#include "chromeos/services/assistant/platform/audio_input_host_impl.h"
+#include "chromeos/services/assistant/platform/audio_input_host.h"
 #include "chromeos/services/assistant/platform/audio_stream_factory_delegate.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
 #include "chromeos/services/assistant/test_support/scoped_assistant_client.h"
@@ -47,14 +48,22 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedFakeAssistantClient);
 };
 
-class ScopedCrasAudioHandler {
+// Mock for |CrosAudioClient|. This inherits from |FakeCrasAudioClient| so we
+// only have to mock the methods we're interested in.
+// It will automatically be installed as the global singleton in its
+// constructor, and removed in the destructor.
+class ScopedCrasAudioClientMock : public FakeCrasAudioClient {
  public:
-  ScopedCrasAudioHandler() { CrasAudioHandler::InitializeForTesting(); }
-  ScopedCrasAudioHandler(const ScopedCrasAudioHandler&) = delete;
-  ScopedCrasAudioHandler& operator=(const ScopedCrasAudioHandler&) = delete;
-  ~ScopedCrasAudioHandler() { CrasAudioHandler::Shutdown(); }
+  ScopedCrasAudioClientMock() = default;
+  ScopedCrasAudioClientMock(ScopedCrasAudioClientMock&) = delete;
+  ScopedCrasAudioClientMock& operator=(ScopedCrasAudioClientMock&) = delete;
+  ~ScopedCrasAudioClientMock() override = default;
 
-  CrasAudioHandler* Get() { return CrasAudioHandler::Get(); }
+  MOCK_METHOD(void,
+              SetHotwordModel,
+              (uint64_t node_id,
+               const std::string& hotword_model,
+               VoidDBusMethodCallback callback));
 };
 
 }  // namespace
@@ -67,6 +76,7 @@
     scoped_feature_list_.InitAndEnableFeature(features::kEnableDspHotword);
 
     PowerManagerClient::InitializeFake();
+    CrasAudioHandler::InitializeForTesting();
 
     CreateNewAudioInputImpl();
   }
@@ -77,6 +87,7 @@
     // |audio_input_host_| uses the fake power manager client, so must be
     // destroyed before the power manager client.
     audio_input_host_.reset();
+    CrasAudioHandler::Shutdown();
     chromeos::PowerManagerClient::Shutdown();
   }
 
@@ -101,11 +112,9 @@
     audio_input_impl_ = std::make_unique<AudioInputImpl>(
         &audio_stream_factory_delegate_, "fake-device-id");
 
-    audio_input_host_ = std::make_unique<AudioInputHostImpl>(
-        cras_audio_handler_.Get(), FakePowerManagerClient::Get(),
-        "initial-locale");
-    audio_input_host_->Initialize(audio_input_impl_.get());
-    audio_input_host_->SetDeviceId("initial-device-id");
+    audio_input_host_ = std::make_unique<AudioInputHost>(
+        audio_input_impl_.get(), CrasAudioHandler::Get(),
+        FakePowerManagerClient::Get());
 
     audio_input_impl_->AddObserver(this);
 
@@ -123,6 +132,10 @@
 
   AudioInputHost& audio_input_host() { return *audio_input_host_; }
 
+  ScopedCrasAudioClientMock& cras_audio_client_mock() {
+    return cras_audio_client_mock_;
+  }
+
   // assistant_client::AudioInput::Observer overrides:
   void OnAudioBufferAvailable(const assistant_client::AudioBuffer& buffer,
                               int64_t timestamp) override {}
@@ -151,9 +164,9 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   ScopedFakeAssistantClient fake_assistant_client_;
   DefaultAudioStreamFactoryDelegate audio_stream_factory_delegate_;
-  ScopedCrasAudioHandler cras_audio_handler_;
+  ::testing::NiceMock<ScopedCrasAudioClientMock> cras_audio_client_mock_;
   std::unique_ptr<AudioInputImpl> audio_input_impl_;
-  std::unique_ptr<AudioInputHostImpl> audio_input_host_;
+  std::unique_ptr<AudioInputHost> audio_input_host_;
 
   DISALLOW_COPY_AND_ASSIGN(AudioInputImplTest);
 };
@@ -277,5 +290,81 @@
   EXPECT_FALSE(IsUsingDeadStreamDetection());
 }
 
+TEST_F(AudioInputImplTest, ShouldSendHotwordLocaleToCrasAudioClient) {
+  StopAudioRecording();
+
+  audio_input_host().SetHotwordDeviceId("111");
+
+  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel);
+  audio_input_host().SetDspHotwordLocale("bla");
+}
+
+TEST_F(AudioInputImplTest,
+       ShouldFormatHotwordLocaleAndSendItToCrasAudioClient) {
+  StopAudioRecording();
+  audio_input_host().SetHotwordDeviceId("111");
+
+  // Normal case
+  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "nl_be", _));
+  audio_input_host().SetDspHotwordLocale("nl-BE");
+
+  // Handle the case where country code and language code are the same
+  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "fr_fr", _));
+  audio_input_host().SetDspHotwordLocale("fr");
+
+  // use "en_all" for all english locales
+  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(111, "en_all", _));
+  audio_input_host().SetDspHotwordLocale("en-US");
+}
+
+TEST_F(AudioInputImplTest, ShouldUseDefaultLocaleIfUserPrefIsRejected) {
+  const std::string default_locale = "en_us";
+  StopAudioRecording();
+  audio_input_host().SetHotwordDeviceId("222");
+
+  EXPECT_CALL(cras_audio_client_mock(),
+              SetHotwordModel(222, "rejected_locale", _))
+      .WillOnce([](uint64_t node_id, const std::string&,
+                   VoidDBusMethodCallback callback) {
+        // Report failure to change the locale
+        std::move(callback).Run(/*success=*/false);
+      });
+
+  EXPECT_CALL(cras_audio_client_mock(),
+              SetHotwordModel(222, default_locale, _));
+
+  audio_input_host().SetDspHotwordLocale("rejected-LOCALE");
+}
+
+TEST_F(AudioInputImplTest, ShouldUseDefaultLocaleIfUserPrefIsEmpty) {
+  const std::string default_locale = "en_us";
+  StopAudioRecording();
+  audio_input_host().SetHotwordDeviceId("222");
+
+  EXPECT_CALL(cras_audio_client_mock(),
+              SetHotwordModel(222, default_locale, _));
+
+  audio_input_host().SetDspHotwordLocale("");
+}
+
+TEST_F(AudioInputImplTest, ShouldDoNothingIfUserPrefIsAccepted) {
+  const std::string default_locale = "en_us";
+  StopAudioRecording();
+  audio_input_host().SetHotwordDeviceId("222");
+
+  EXPECT_CALL(cras_audio_client_mock(),
+              SetHotwordModel(222, "accepted_locale", _))
+      .WillOnce([](uint64_t node_id, const std::string&,
+                   VoidDBusMethodCallback callback) {
+        // Accept the change to the locale.
+        std::move(callback).Run(/*success=*/true);
+      });
+
+  // Do not expect a second call if change of locale is accepted
+  EXPECT_CALL(cras_audio_client_mock(), SetHotwordModel(222, default_locale, _))
+      .Times(0);
+
+  audio_input_host().SetDspHotwordLocale("accepted-LOCALE");
+}
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform_api_impl.cc b/chromeos/services/assistant/platform_api_impl.cc
index dc59df3..63c38759 100644
--- a/chromeos/services/assistant/platform_api_impl.cc
+++ b/chromeos/services/assistant/platform_api_impl.cc
@@ -8,11 +8,11 @@
 #include <utility>
 #include <vector>
 
+#include "base/system/sys_info.h"
+#include "chromeos/audio/cras_audio_handler.h"
 #include "chromeos/services/assistant/media_session/assistant_media_session.h"
-#include "chromeos/services/assistant/platform/audio_devices.h"
 #include "chromeos/services/assistant/platform/power_manager_provider_impl.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
-#include "chromeos/services/assistant/public/cpp/migration/audio_input_host.h"
 #include "chromeos/services/assistant/utils.h"
 #include "libassistant/shared/public/assistant_export.h"
 #include "libassistant/shared/public/platform_api.h"
@@ -79,13 +79,20 @@
 PlatformApiImpl::PlatformApiImpl(
     AssistantMediaSession* media_session,
     PowerManagerClient* power_manager_client,
+    CrasAudioHandler* cras_audio_handler,
     mojo::PendingRemote<device::mojom::BatteryMonitor> battery_monitor,
     scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
-    scoped_refptr<base::SingleThreadTaskRunner> background_task_runner)
+    scoped_refptr<base::SingleThreadTaskRunner> background_task_runner,
+    std::string pref_locale)
     : audio_input_provider_(),
       audio_output_provider_(media_session,
                              background_task_runner,
-                             media::AudioDeviceDescription::kDefaultDeviceId) {
+                             media::AudioDeviceDescription::kDefaultDeviceId),
+      audio_input_host_(&audio_input_provider_.GetAudioInput(),
+                        cras_audio_handler,
+                        power_manager_client),
+      pref_locale_(pref_locale),
+      cras_audio_handler_(cras_audio_handler) {
   // Only enable native power features if they are supported by the UI.
   std::unique_ptr<PowerManagerProviderImpl> provider;
   if (features::IsPowerManagerEnabled()) {
@@ -94,9 +101,14 @@
   }
   system_provider_ = std::make_unique<SystemProviderImpl>(
       std::move(provider), std::move(battery_monitor));
+
+  cras_audio_handler_->AddAudioObserver(this);
+  OnAudioNodesChanged();
 }
 
-PlatformApiImpl::~PlatformApiImpl() = default;
+PlatformApiImpl::~PlatformApiImpl() {
+  cras_audio_handler_->RemoveAudioObserver(this);
+}
 
 AudioInputProviderImpl& PlatformApiImpl::GetAudioInputProvider() {
   return audio_input_provider_;
@@ -122,8 +134,64 @@
   return *system_provider_;
 }
 
-void PlatformApiImpl::InitializeAudioInputHost(AudioInputHost& host) {
-  host.Initialize(&audio_input_provider_.GetAudioInput());
+void PlatformApiImpl::OnAudioNodesChanged() {
+  if (!base::SysInfo::IsRunningOnChromeOS())
+    return;
+
+  chromeos::AudioDeviceList devices;
+  cras_audio_handler_->GetAudioDevices(&devices);
+
+  const chromeos::AudioDevice* input_device = nullptr;
+  const chromeos::AudioDevice* hotword_device = nullptr;
+
+  for (const chromeos::AudioDevice& device : devices) {
+    if (!device.is_input)
+      continue;
+
+    switch (device.type) {
+      case chromeos::AUDIO_TYPE_USB:
+      case chromeos::AUDIO_TYPE_HEADPHONE:
+      case chromeos::AUDIO_TYPE_INTERNAL_MIC:
+      case chromeos::AUDIO_TYPE_FRONT_MIC:
+        if (!input_device || input_device->priority < device.priority)
+          input_device = &device;
+        break;
+      case chromeos::AUDIO_TYPE_HOTWORD:
+        if (!hotword_device || hotword_device->priority < device.priority)
+          hotword_device = &device;
+        break;
+      default:
+        // ignore other devices
+        break;
+    }
+  }
+
+  audio_input_host_.SetDeviceId(
+      input_device ? base::NumberToString(input_device->id) : std::string());
+
+  if (hotword_device) {
+    audio_input_host_.SetHotwordDeviceId(
+        base::NumberToString(hotword_device->id));
+    audio_input_host_.SetDspHotwordLocale(pref_locale_);
+  } else {
+    audio_input_host_.SetHotwordDeviceId(std::string());
+  }
+}
+
+void PlatformApiImpl::SetMicState(bool mic_open) {
+  audio_input_host_.SetMicState(mic_open);
+}
+
+void PlatformApiImpl::OnConversationTurnStarted() {
+  audio_input_host_.OnConversationTurnStarted();
+}
+
+void PlatformApiImpl::OnConversationTurnFinished() {
+  audio_input_host_.OnConversationTurnFinished();
+}
+
+void PlatformApiImpl::OnHotwordEnabled(bool enable) {
+  audio_input_host_.OnHotwordEnabled(enable);
 }
 
 }  // namespace assistant
diff --git a/chromeos/services/assistant/platform_api_impl.h b/chromeos/services/assistant/platform_api_impl.h
index 782ddc8..ffcaeb1 100644
--- a/chromeos/services/assistant/platform_api_impl.h
+++ b/chromeos/services/assistant/platform_api_impl.h
@@ -10,6 +10,8 @@
 #include <utility>
 #include <vector>
 
+#include "chromeos/audio/cras_audio_handler.h"
+#include "chromeos/services/assistant/platform/audio_input_host.h"
 #include "chromeos/services/assistant/platform/audio_input_provider_impl.h"
 #include "chromeos/services/assistant/platform/audio_output_provider_impl.h"
 #include "chromeos/services/assistant/platform/file_provider_impl.h"
@@ -29,14 +31,17 @@
 class AssistantMediaSession;
 
 // Platform API required by the voice assistant.
-class PlatformApiImpl : public CrosPlatformApi {
+class PlatformApiImpl : public CrosPlatformApi,
+                        CrasAudioHandler::AudioObserver {
  public:
   PlatformApiImpl(
       AssistantMediaSession* media_session,
       PowerManagerClient* power_manager_client,
+      CrasAudioHandler* cras_audio_handler,
       mojo::PendingRemote<device::mojom::BatteryMonitor> battery_monitor,
       scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
-      scoped_refptr<base::SingleThreadTaskRunner> background_task_runner);
+      scoped_refptr<base::SingleThreadTaskRunner> background_task_runner,
+      std::string pref_locale);
   ~PlatformApiImpl() override;
 
   // assistant_client::PlatformApi overrides
@@ -47,7 +52,17 @@
   assistant_client::NetworkProvider& GetNetworkProvider() override;
   assistant_client::SystemProvider& GetSystemProvider() override;
 
-  void InitializeAudioInputHost(AudioInputHost& host) override;
+  // chromeos::CrasAudioHandler::AudioObserver overrides
+  void OnAudioNodesChanged() override;
+
+  // Called when the mic state associated with the interaction is changed.
+  void SetMicState(bool mic_open) override;
+
+  void OnConversationTurnStarted() override;
+  void OnConversationTurnFinished() override;
+
+  // Called when hotword enabled status changed.
+  void OnHotwordEnabled(bool enable) override;
 
  private:
   // ChromeOS does not use auth manager, so we don't yet need to implement a
@@ -87,7 +102,11 @@
   FakeAuthProvider auth_provider_;
   FileProviderImpl file_provider_;
   NetworkProviderImpl network_provider_;
+  AudioInputHost audio_input_host_;
   std::unique_ptr<SystemProviderImpl> system_provider_;
+  std::string pref_locale_;
+
+  CrasAudioHandler* const cras_audio_handler_;
 
   DISALLOW_COPY_AND_ASSIGN(PlatformApiImpl);
 };
diff --git a/chromeos/services/assistant/public/cpp/migration/BUILD.gn b/chromeos/services/assistant/public/cpp/migration/BUILD.gn
index 910c427..d901cdc 100644
--- a/chromeos/services/assistant/public/cpp/migration/BUILD.gn
+++ b/chromeos/services/assistant/public/cpp/migration/BUILD.gn
@@ -11,7 +11,6 @@
 
   sources = [
     "assistant_manager_service_delegate.h",
-    "audio_input_host.h",
     "cros_platform_api.h",
     "libassistant_v1_api.cc",
     "libassistant_v1_api.h",
diff --git a/chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h b/chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h
index f92b2f2..446616a 100644
--- a/chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h
+++ b/chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h
@@ -20,7 +20,6 @@
 namespace assistant {
 
 class AssistantMediaSession;
-class AudioInputHost;
 class CrosPlatformApi;
 
 // Interface class that provides factory methods for assistant internal
@@ -30,8 +29,6 @@
   AssistantManagerServiceDelegate() = default;
   virtual ~AssistantManagerServiceDelegate() = default;
 
-  virtual std::unique_ptr<AudioInputHost> CreateAudioInputHost() = 0;
-
   virtual std::unique_ptr<CrosPlatformApi> CreatePlatformApi(
       AssistantMediaSession* media_session,
       scoped_refptr<base::SingleThreadTaskRunner>
diff --git a/chromeos/services/assistant/public/cpp/migration/audio_input_host.h b/chromeos/services/assistant/public/cpp/migration/audio_input_host.h
deleted file mode 100644
index 2314831..0000000
--- a/chromeos/services/assistant/public/cpp/migration/audio_input_host.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2021 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 CHROMEOS_SERVICES_ASSISTANT_PUBLIC_CPP_MIGRATION_AUDIO_INPUT_HOST_H_
-#define CHROMEOS_SERVICES_ASSISTANT_PUBLIC_CPP_MIGRATION_AUDIO_INPUT_HOST_H_
-
-#include <string>
-
-#include "base/component_export.h"
-
-namespace chromeos {
-namespace assistant {
-
-class AudioInputImpl;
-
-// Class that provides the bridge between the ChromeOS UI thread and the
-// Libassistant audio input class.
-class COMPONENT_EXPORT(ASSISTANT_SERVICE) AudioInputHost {
- public:
-  virtual ~AudioInputHost() = default;
-
-  // Initialize this class by setting the backend that it's supposed to use.
-  // TODO(b/171748795): Should be gone when the Libassistant V2 migration is
-  // completed.
-  virtual void Initialize(AudioInputImpl* audio_input) = 0;
-
-  // Called when the mic state associated with the interaction is changed.
-  virtual void SetMicState(bool mic_open) = 0;
-
-  // Called when hotword enabled status changed.
-  virtual void OnHotwordEnabled(bool enable) = 0;
-
-  virtual void OnConversationTurnStarted() = 0;
-  virtual void OnConversationTurnFinished() = 0;
-};
-
-}  // namespace assistant
-}  // namespace chromeos
-
-#endif  // CHROMEOS_SERVICES_ASSISTANT_PUBLIC_CPP_MIGRATION_AUDIO_INPUT_HOST_H_
diff --git a/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h b/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
index 41f4ee12..409e7a10 100644
--- a/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
+++ b/chromeos/services/assistant/public/cpp/migration/cros_platform_api.h
@@ -19,8 +19,6 @@
 namespace chromeos {
 namespace assistant {
 
-class AudioInputHost;
-
 // Platform API required by the voice assistant, extended with some methods used
 // when ChromeOS needs to make changes to the platform state.
 // Note that this no longer inherits from |assistant_client::PlatformApi|,
@@ -31,10 +29,14 @@
   CrosPlatformApi() = default;
   virtual ~CrosPlatformApi() = default;
 
-  // Initialize the AudioInputHost.
-  // TODO(b/171748795): Should be gone when the Libassistant V2 migration is
-  // completed.
-  virtual void InitializeAudioInputHost(AudioInputHost&) = 0;
+  // Called when the mic state associated with the interaction is changed.
+  virtual void SetMicState(bool mic_open) = 0;
+
+  virtual void OnConversationTurnStarted() = 0;
+  virtual void OnConversationTurnFinished() = 0;
+
+  // Called when hotword enabled status changed.
+  virtual void OnHotwordEnabled(bool enable) = 0;
 
   // Returns the platform's audio input provider.
   virtual assistant_client::AudioInputProvider& GetAudioInputProvider() = 0;
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.cc b/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.cc
index 73c9b52..4e0a16e 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.cc
+++ b/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.cc
@@ -9,30 +9,10 @@
 #include "chromeos/assistant/internal/test_support/fake_assistant_manager.h"
 #include "chromeos/assistant/internal/test_support/fake_assistant_manager_internal.h"
 #include "chromeos/services/assistant//public/cpp/migration/fake_platform_api.h"
-#include "chromeos/services/assistant/public/cpp/migration/audio_input_host.h"
 
 namespace chromeos {
 namespace assistant {
 
-namespace {
-
-class FakeAudioInputHost : public AudioInputHost {
- public:
-  FakeAudioInputHost() = default;
-  FakeAudioInputHost(const FakeAudioInputHost&) = delete;
-  FakeAudioInputHost& operator=(const FakeAudioInputHost&) = delete;
-  ~FakeAudioInputHost() override = default;
-
-  // AudioInputHost implementation:
-  void Initialize(AudioInputImpl* audio_input) override {}
-  void SetMicState(bool mic_open) override {}
-  void OnHotwordEnabled(bool enable) override {}
-  void OnConversationTurnStarted() override {}
-  void OnConversationTurnFinished() override {}
-};
-
-}  // namespace
-
 FakeAssistantManagerServiceDelegate::FakeAssistantManagerServiceDelegate() {
   // We start by creating a pending assistant manager, as our unittests
   // might need to access the assistant manager before it is created through
@@ -43,11 +23,6 @@
 FakeAssistantManagerServiceDelegate::~FakeAssistantManagerServiceDelegate() =
     default;
 
-std::unique_ptr<AudioInputHost>
-FakeAssistantManagerServiceDelegate::CreateAudioInputHost() {
-  return std::make_unique<FakeAudioInputHost>();
-}
-
 std::unique_ptr<CrosPlatformApi>
 FakeAssistantManagerServiceDelegate::CreatePlatformApi(
     AssistantMediaSession* media_session,
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h b/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h
index 1adddb28..1ba87f1e 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h
+++ b/chromeos/services/assistant/public/cpp/migration/fake_assistant_manager_service_delegate.h
@@ -26,7 +26,6 @@
   FakeAssistantManager* assistant_manager();
 
   // AssistantManagerServiceDelegate:
-  std::unique_ptr<AudioInputHost> CreateAudioInputHost() override;
   std::unique_ptr<CrosPlatformApi> CreatePlatformApi(
       AssistantMediaSession* media_session,
       scoped_refptr<base::SingleThreadTaskRunner> background_thread_task_runner)
diff --git a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
index b730a4c..49fde32a 100644
--- a/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
+++ b/chromeos/services/assistant/public/cpp/migration/fake_platform_api.h
@@ -28,7 +28,10 @@
   assistant_client::FileProvider& GetFileProvider() override;
   assistant_client::NetworkProvider& GetNetworkProvider() override;
   assistant_client::SystemProvider& GetSystemProvider() override;
-  void InitializeAudioInputHost(AudioInputHost&) override {}
+  void SetMicState(bool mic_open) override {}
+  void OnHotwordEnabled(bool enable) override {}
+  void OnConversationTurnStarted() override {}
+  void OnConversationTurnFinished() override {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FakePlatformApi);
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index dc4704a..214ba25 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -274,21 +274,24 @@
   return base::EmptyString();
 }
 
-mojom::DeviceStatePropertiesPtr GetVpnState() {
-  auto result = mojom::DeviceStateProperties::New();
-  result->type = mojom::NetworkType::kVPN;
-
-  bool vpn_disabled = false;
+bool IsVpnProhibited() {
+  bool vpn_prohibited = false;
   if (NetworkHandler::IsInitialized()) {
     std::vector<std::string> prohibited_technologies =
         NetworkHandler::Get()
             ->prohibited_technologies_handler()
             ->GetCurrentlyProhibitedTechnologies();
-    vpn_disabled = base::Contains(prohibited_technologies, shill::kTypeVPN);
+    vpn_prohibited = base::Contains(prohibited_technologies, shill::kTypeVPN);
   }
+  return vpn_prohibited;
+}
 
-  result->device_state = vpn_disabled ? mojom::DeviceStateType::kProhibited
-                                      : mojom::DeviceStateType::kEnabled;
+mojom::DeviceStatePropertiesPtr GetVpnState() {
+  auto result = mojom::DeviceStateProperties::New();
+  result->type = mojom::NetworkType::kVPN;
+
+  result->device_state = IsVpnProhibited() ? mojom::DeviceStateType::kProhibited
+                                           : mojom::DeviceStateType::kEnabled;
   return result;
 }
 
@@ -1861,7 +1864,15 @@
   // Handle VPN state separately because VPN is not considered a device by shill
   // and thus will not be included in the |devices| list returned by network
   // state handler. In the UI code, it is treated as a "device" for consistency.
-  result.emplace_back(GetVpnState());
+  // In the UI code, knowing whether a device is prohibited or not is done by
+  // checking |device_state| field of the DeviceStateProperties of the
+  // corresponding device. A VPN device state is returned if built-in VPN
+  // services are prohibited by policy even if no VPN services exist in order to
+  // indicate that adding a VPN is prohibited in the UI.
+  if (network_state_handler_->FirstNetworkByType(NetworkTypePattern::VPN()) ||
+      IsVpnProhibited()) {
+    result.emplace_back(GetVpnState());
+  }
 
   std::move(callback).Run(std::move(result));
 }
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index 342fbc1..8d95848 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -25,6 +25,7 @@
 #include "chromeos/network/network_state_test_helper.h"
 #include "chromeos/network/network_type_pattern.h"
 #include "chromeos/network/onc/onc_utils.h"
+#include "chromeos/network/prohibited_technologies_handler.h"
 #include "chromeos/network/proxy/ui_proxy_config_service.h"
 #include "chromeos/services/network_config/public/cpp/cros_network_config_test_observer.h"
 #include "components/onc/onc_constants.h"
@@ -485,6 +486,16 @@
     run_loop.Run();
   }
 
+  bool ContainsVpnDeviceState(
+      std::vector<mojom::DeviceStatePropertiesPtr> devices) {
+    for (auto& device : devices) {
+      if (device->type == mojom::NetworkType::kVPN) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   NetworkStateTestHelper& helper() { return helper_; }
   CrosNetworkConfigTestObserver* observer() { return observer_.get(); }
   CrosNetworkConfig* cros_network_config() {
@@ -684,6 +695,32 @@
   EXPECT_EQ(mojom::DeviceStateType::kDisabled, devices[0]->device_state);
 }
 
+// Tests that no VPN device state is returned by GetDeviceStateList if no VPN
+// services exist and built-in VPN is not prohibited.
+TEST_F(CrosNetworkConfigTest, GetDeviceStateListNoVpnServices) {
+  helper().ClearServices();
+
+  std::vector<std::string> prohibited_technologies =
+      NetworkHandler::Get()
+          ->prohibited_technologies_handler()
+          ->GetCurrentlyProhibitedTechnologies();
+  ASSERT_FALSE(base::Contains(prohibited_technologies, shill::kTypeVPN));
+
+  EXPECT_FALSE(ContainsVpnDeviceState(GetDeviceStateList()));
+}
+
+// Tests that a VPN device state is returned by GetDeviceStateList if built-in
+// VPN is not prohibited even if no VPN services exist.
+TEST_F(CrosNetworkConfigTest, GetDeviceStateListNoVpnServicesAndVpnProhibited) {
+  helper().ClearServices();
+
+  NetworkHandler::Get()
+      ->prohibited_technologies_handler()
+      ->AddGloballyProhibitedTechnology(shill::kTypeVPN);
+
+  EXPECT_TRUE(ContainsVpnDeviceState(GetDeviceStateList()));
+}
+
 // Test a sampling of properties, ensuring that string property types are
 // translated as strings and not enum values (See ManagedProperties definition
 // in cros_network_config.mojom for details).
diff --git a/components/account_manager_core/BUILD.gn b/components/account_manager_core/BUILD.gn
index 665ab81..b16801c 100644
--- a/components/account_manager_core/BUILD.gn
+++ b/components/account_manager_core/BUILD.gn
@@ -20,6 +20,7 @@
   deps = [
     "//base",
     "//chromeos/crosapi/mojom",
+    "//google_apis:google_apis",
   ]
 
   defines = [ "IS_ACCOUNT_MANAGER_CORE_IMPL" ]
diff --git a/components/account_manager_core/DEPS b/components/account_manager_core/DEPS
index 1b72be6..1709b49 100644
--- a/components/account_manager_core/DEPS
+++ b/components/account_manager_core/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+chromeos/crosapi/mojom/account_manager.mojom.h",
+  "+google_apis/gaia/google_service_auth_error.h",
 ]
diff --git a/components/account_manager_core/account_manager_facade.cc b/components/account_manager_core/account_manager_facade.cc
index eb8eea7e..f154ff8 100644
--- a/components/account_manager_core/account_manager_facade.cc
+++ b/components/account_manager_core/account_manager_facade.cc
@@ -6,6 +6,17 @@
 
 namespace account_manager {
 
+AccountManagerFacade::AccountAdditionResult::AccountAdditionResult() = default;
+AccountManagerFacade::AccountAdditionResult::AccountAdditionResult(
+    Status status,
+    AccountKey account)
+    : status(status), account(account) {}
+AccountManagerFacade::AccountAdditionResult::AccountAdditionResult(
+    Status status,
+    GoogleServiceAuthError error)
+    : status(status), error(error) {}
+AccountManagerFacade::AccountAdditionResult::~AccountAdditionResult() = default;
+
 AccountManagerFacade::AccountManagerFacade() = default;
 AccountManagerFacade::~AccountManagerFacade() = default;
 
diff --git a/components/account_manager_core/account_manager_facade.h b/components/account_manager_core/account_manager_facade.h
index 4408ea0..ea0d886 100644
--- a/components/account_manager_core/account_manager_facade.h
+++ b/components/account_manager_core/account_manager_facade.h
@@ -5,7 +5,12 @@
 #ifndef COMPONENTS_ACCOUNT_MANAGER_CORE_ACCOUNT_MANAGER_FACADE_H_
 #define COMPONENTS_ACCOUNT_MANAGER_CORE_ACCOUNT_MANAGER_FACADE_H_
 
+#include <string>
+
+#include "base/callback.h"
 #include "base/component_export.h"
+#include "components/account_manager_core/account.h"
+#include "google_apis/gaia/google_service_auth_error.h"
 
 namespace account_manager {
 
@@ -42,6 +47,31 @@
     kMaxValue = kOnboarding
   };
 
+  // The result of account addition request.
+  struct AccountAdditionResult {
+    enum class Status : int {
+      // The account was added successfully.
+      kSuccess = 0,
+      // The dialog is already open.
+      kAlreadyInProgress = 1,
+      // User closed the dialog.
+      kCancelledByUser = 2,
+      // Network error.
+      kNetworkError = 3,
+    };
+
+    Status status;
+    // The account that was added.
+    base::Optional<AccountKey> account;
+    // The error is set only if `status` is set to `kNetworkError`.
+    base::Optional<GoogleServiceAuthError> error;
+
+    AccountAdditionResult();
+    AccountAdditionResult(Status status, AccountKey account);
+    AccountAdditionResult(Status status, GoogleServiceAuthError error);
+    ~AccountAdditionResult();
+  };
+
   AccountManagerFacade();
   AccountManagerFacade(const AccountManagerFacade&) = delete;
   AccountManagerFacade& operator=(const AccountManagerFacade&) = delete;
@@ -52,6 +82,18 @@
   // Note: For out-of-process implementations, it returns |false| if the IPC
   // pipe to |AccountManager| is disconnected.
   virtual bool IsInitialized() = 0;
+
+  // Launches account addition dialog and calls the `callback` with the result.
+  // If `result` is `kSuccess`, the added account will be passed to the
+  // callback. Otherwise `account` will be set to `base::nullopt`.
+  virtual void ShowAddAccountDialog(
+      const AccountAdditionSource& source,
+      base::OnceCallback<void(const AccountAdditionResult& result)>
+          callback) = 0;
+
+  // Launches account reauthentication dialog for provided `email`.
+  virtual void ShowReauthAccountDialog(const AccountAdditionSource& source,
+                                       const std::string& email) = 0;
 };
 
 }  // namespace account_manager
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 3115136..293c7dd 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -143,6 +143,7 @@
     "//ui/base:base",
     "//ui/base/clipboard",
     "//ui/base/ime",
+    "//ui/base/ime/chromeos:chromeos",
     "//ui/chromeos/strings",
     "//ui/display/manager",
     "//ui/events",
diff --git a/components/arc/ime/arc_ime_service.cc b/components/arc/ime/arc_ime_service.cc
index 7cf6412..4ded456 100644
--- a/components/arc/ime/arc_ime_service.cc
+++ b/components/arc/ime/arc_ime_service.cc
@@ -25,6 +25,8 @@
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
+#include "ui/base/ime/chromeos/extension_ime_util.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/base/ime/input_method.h"
 #include "ui/base/ime/input_method_delegate.h"
 #include "ui/base/ime/text_input_flags.h"
@@ -633,6 +635,18 @@
   if (!range.is_empty()) {
     base::UmaHistogramEnumeration("InputMethod.Assistive.Autocorrect.Count",
                                   TextInputClient::SubClass::kArcImeService);
+
+    auto* input_method_manager =
+        chromeos::input_method::InputMethodManager::Get();
+    if (input_method_manager &&
+        chromeos::extension_ime_util::IsExperimentalMultilingual(
+            input_method_manager->GetActiveIMEState()
+                ->GetCurrentInputMethod()
+                .id())) {
+      base::UmaHistogramEnumeration(
+          "InputMethod.MultilingualExperiment.Autocorrect.Count",
+          TextInputClient::SubClass::kArcImeService);
+    }
   }
   // TODO(https:://crbug.com/1091088): Implement this method.
   NOTIMPLEMENTED_LOG_ONCE();
diff --git a/components/autofill/ios/browser/js_suggestion_manager.h b/components/autofill/ios/browser/js_suggestion_manager.h
index 953742fe..425e4ae 100644
--- a/components/autofill/ios/browser/js_suggestion_manager.h
+++ b/components/autofill/ios/browser/js_suggestion_manager.h
@@ -5,70 +5,90 @@
 #ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_JS_SUGGESTION_MANAGER_H_
 #define COMPONENTS_AUTOFILL_IOS_BROWSER_JS_SUGGESTION_MANAGER_H_
 
-#import "ios/web/public/deprecated/crw_js_injection_receiver.h"
+#include "base/callback.h"
+#include "base/memory/weak_ptr.h"
+#import "ios/web/public/web_state_user_data.h"
+
+namespace base {
+class Value;
+}  // namespace base
 
 namespace web {
-class WebFramesManager;
-}  // namespace
+class WebFrame;
+class WebState;
+}  // namespace web
 
-// Loads the JavaScript file, suggestion_manager.js, which contains form parsing
-// and autofill functions.
-@interface JsSuggestionManager : NSObject
+namespace autofill {
 
-// Designated initializer. |receiver| should not be nil.
-- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver
-    NS_DESIGNATED_INITIALIZER;
+class JsSuggestionManager : public web::WebStateUserData<JsSuggestionManager> {
+ public:
+  ~JsSuggestionManager() override;
 
-- (instancetype)init NS_UNAVAILABLE;
+  static JsSuggestionManager* GetOrCreateForWebState(web::WebState* web_state);
 
-// Sets the WebFrames manager associated with the receiver.
-- (void)setWebFramesManager:(web::WebFramesManager*)framesManager;
+  // Focuses the next focusable element in tab order inside the web frame with
+  // frame id |frame_ID|. No action if there is no such element.
+  void SelectNextElementInFrameWithID(const std::string& frame_ID);
 
-// Focuses the next focusable element in tab order inside the web frame with
-// frame id |frameID|. No action if there is no such element.
-- (void)selectNextElementInFrameWithID:(NSString*)frameID;
+  // Focuses the next focusable element in tab order after the element specified
+  // by |form_name| and |field_name| in tab order inside the web frame with
+  // frame id |frame_ID|. No action if there is no such element.
+  void SelectNextElementInFrameWithID(const std::string& frame_ID,
+                                      const std::string& form_name,
+                                      const std::string& field_name);
 
-// Focuses the next focusable element in tab order after the element specified
-// by |formName| and |fieldName| in tab order inside the web frame with frame id
-// |frameID|. No action if there is no such element.
-- (void)selectNextElementInFrameWithID:(NSString*)frameID
-                             afterForm:(NSString*)formName
-                                 field:(NSString*)fieldName;
+  // Focuses the previous focusable element in tab order inside the web frame
+  // with frame id |frame_ID|. No action if there is no such element.
+  void SelectPreviousElementInFrameWithID(const std::string& frame_ID);
 
-// Focuses the previous focusable element in tab order inside the web frame with
-// frame id |frameID|. No action if there is no such element.
-- (void)selectPreviousElementInFrameWithID:(NSString*)frameID;
+  // Focuses the previous focusable element in tab order from the element
+  // specified by |form_name| and |field_name| in tab order inside the web frame
+  // with frame id |frame_ID|. No action if there is no such element.
+  void SelectPreviousElementInFrameWithID(const std::string& frame_ID,
+                                          const std::string& form_name,
+                                          const std::string& field_name);
 
-// Focuses the previous focusable element in tab order from the element
-// specified by |formName| and |fieldName| in tab order inside the web frame
-// with frame id |frameID|. No action if there is no such element.
-- (void)selectPreviousElementInFrameWithID:(NSString*)frameID
-                                beforeForm:(NSString*)formName
-                                     field:(NSString*)fieldName;
+  // Checks if the frame with frame id |frame_ID| contains a next and previous
+  // element. |completionHandler| is called with 2 bools, the first indicating
+  // if a previous element was found, and the second indicating if a next
+  // element was found. |completionHcompletion_handlerandler| cannot be nil.
+  void FetchPreviousAndNextElementsPresenceInFrameWithID(
+      const std::string& frame_ID,
+      base::OnceCallback<void(bool, bool)> completion_handler);
 
-// Checks if the frame with frame id |frameID| contains a next and previous
-// element. |completionHandler| is called with 2 BOOLs, the first indicating if
-// a previous element was found, and the second indicating if a next element was
-// found. |completionHandler| cannot be nil.
-- (void)fetchPreviousAndNextElementsPresenceInFrameWithID:(NSString*)frameID
-                                        completionHandler:(void (^)(BOOL, BOOL))
-                                                              completionHandler;
+  // Checks if the frame with frame id |frame_ID| contains a next and previous
+  // element starting from the field specified by |form_name| and |field_name|.
+  // |completionHandler| is called with 2 BOOLs, the first indicating if a
+  // previous element was found, and the second indicating if a next element was
+  // found. |completion_handler| cannot be nil.
+  void FetchPreviousAndNextElementsPresenceInFrameWithID(
+      const std::string& frame_ID,
+      const std::string& form_name,
+      const std::string& field_name,
+      base::OnceCallback<void(bool, bool)> completion_handler);
 
-// Checks if the frame with frame id |frameID| contains a next and previous
-// element starting from the field specified by |formName| and |fieldName|.
-// |completionHandler| is called with 2 BOOLs, the first indicating if a
-// previous element was found, and the second indicating if a next element was
-// found. |completionHandler| cannot be nil.
-- (void)fetchPreviousAndNextElementsPresenceInFrameWithID:(NSString*)frameID
-                                                  forForm:(NSString*)formName
-                                                    field:(NSString*)fieldName
-                                        completionHandler:(void (^)(BOOL, BOOL))
-                                                              completionHandler;
+  // Closes the keyboard and defocuses the active input element in the frame
+  // with frame id |frame_ID|.
+  void CloseKeyboardForFrameWithID(const std::string& frame_ID);
 
-// Closes the keyboard and defocuses the active input element in the frame with
-// frame id |frameID|.
-- (void)closeKeyboardForFrameWithID:(NSString*)frameID;
+ private:
+  explicit JsSuggestionManager(web::WebState* web_state);
 
-@end
+  void PreviousAndNextElementsPresenceResult(
+      base::OnceCallback<void(bool, bool)> completion_handler,
+      const base::Value* res);
+
+  web::WebFrame* GetFrameWithFrameID(const std::string& frame_ID);
+
+  web::WebState* web_state_;
+
+  base::WeakPtrFactory<JsSuggestionManager> weak_ptr_factory_;
+
+  friend class web::WebStateUserData<JsSuggestionManager>;
+
+  WEB_STATE_USER_DATA_KEY_DECL();
+};
+
+}  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_IOS_BROWSER_JS_SUGGESTION_MANAGER_H_
diff --git a/components/autofill/ios/browser/js_suggestion_manager.mm b/components/autofill/ios/browser/js_suggestion_manager.mm
index 41edcd0..2b207468 100644
--- a/components/autofill/ios/browser/js_suggestion_manager.mm
+++ b/components/autofill/ios/browser/js_suggestion_manager.mm
@@ -4,6 +4,7 @@
 
 #import "components/autofill/ios/browser/js_suggestion_manager.h"
 
+#import <Foundation/Foundation.h>
 #include <vector>
 
 #include "base/bind.h"
@@ -21,115 +22,125 @@
 #error "This file requires ARC support."
 #endif
 
-@implementation JsSuggestionManager {
-  // The injection receiver used to evaluate JavaScript.
-  __weak CRWJSInjectionReceiver* _receiver;
-  web::WebFramesManager* _webFramesManager;
-}
+namespace autofill {
 
-- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver {
-  DCHECK(receiver);
-  self = [super init];
-  if (self) {
-    _receiver = receiver;
+JsSuggestionManager::JsSuggestionManager(web::WebState* web_state)
+    : web_state_(web_state), weak_ptr_factory_(this) {}
+
+JsSuggestionManager::~JsSuggestionManager() = default;
+
+// static
+JsSuggestionManager* JsSuggestionManager::GetOrCreateForWebState(
+    web::WebState* web_state) {
+  JsSuggestionManager* helper = FromWebState(web_state);
+  if (!helper) {
+    CreateForWebState(web_state);
+    helper = FromWebState(web_state);
+    DCHECK(helper);
   }
-  return self;
+  return helper;
 }
 
-- (void)setWebFramesManager:(web::WebFramesManager*)framesManager {
-  _webFramesManager = framesManager;
+void JsSuggestionManager::SelectNextElementInFrameWithID(
+    const std::string& frame_ID) {
+  SelectNextElementInFrameWithID(frame_ID, "", "");
 }
 
-#pragma mark -
-#pragma mark ProtectedMethods
-
-- (void)selectNextElementInFrameWithID:(NSString*)frameID {
-  [self selectNextElementInFrameWithID:frameID afterForm:@"" field:@""];
-}
-
-- (void)selectNextElementInFrameWithID:(NSString*)frameID
-                             afterForm:(NSString*)formName
-                                 field:(NSString*)fieldName {
+void JsSuggestionManager::SelectNextElementInFrameWithID(
+    const std::string& frame_ID,
+    const std::string& form_name,
+    const std::string& field_name) {
   std::vector<base::Value> parameters;
-  parameters.push_back(base::Value(base::SysNSStringToUTF8(formName)));
-  parameters.push_back(base::Value(base::SysNSStringToUTF8(fieldName)));
-  autofill::ExecuteJavaScriptFunction(
-      "suggestion.selectNextElement", parameters,
-      [self frameWithFrameID:frameID], autofill::JavaScriptResultCallback());
+  parameters.push_back(base::Value(form_name));
+  parameters.push_back(base::Value(field_name));
+  autofill::ExecuteJavaScriptFunction("suggestion.selectNextElement",
+                                      parameters, GetFrameWithFrameID(frame_ID),
+                                      autofill::JavaScriptResultCallback());
 }
 
-- (void)selectPreviousElementInFrameWithID:(NSString*)frameID {
-  [self selectPreviousElementInFrameWithID:frameID beforeForm:@"" field:@""];
+void JsSuggestionManager::SelectPreviousElementInFrameWithID(
+    const std::string& frame_ID) {
+  SelectPreviousElementInFrameWithID(frame_ID, "", "");
 }
 
-- (void)selectPreviousElementInFrameWithID:(NSString*)frameID
-                                beforeForm:(NSString*)formName
-                                     field:(NSString*)fieldName {
+void JsSuggestionManager::SelectPreviousElementInFrameWithID(
+    const std::string& frame_ID,
+    const std::string& form_name,
+    const std::string& field_name) {
   std::vector<base::Value> parameters;
-  parameters.push_back(base::Value(base::SysNSStringToUTF8(formName)));
-  parameters.push_back(base::Value(base::SysNSStringToUTF8(fieldName)));
-  autofill::ExecuteJavaScriptFunction(
-      "suggestion.selectPreviousElement", parameters,
-      [self frameWithFrameID:frameID], autofill::JavaScriptResultCallback());
+  parameters.push_back(base::Value(form_name));
+  parameters.push_back(base::Value(field_name));
+  autofill::ExecuteJavaScriptFunction("suggestion.selectPreviousElement",
+                                      parameters, GetFrameWithFrameID(frame_ID),
+                                      autofill::JavaScriptResultCallback());
 }
 
-- (void)fetchPreviousAndNextElementsPresenceInFrameWithID:(NSString*)frameID
-                                        completionHandler:
-                                            (void (^)(BOOL,
-                                                      BOOL))completionHandler {
-  [self fetchPreviousAndNextElementsPresenceInFrameWithID:frameID
-                                                  forForm:@""
-                                                    field:@""
-                                        completionHandler:completionHandler];
+void JsSuggestionManager::FetchPreviousAndNextElementsPresenceInFrameWithID(
+    const std::string& frame_ID,
+    base::OnceCallback<void(bool, bool)> completion_handler) {
+  FetchPreviousAndNextElementsPresenceInFrameWithID(
+      frame_ID, "", "", std::move(completion_handler));
 }
 
-- (void)fetchPreviousAndNextElementsPresenceInFrameWithID:(NSString*)frameID
-                                                  forForm:(NSString*)formName
-                                                    field:(NSString*)fieldName
-                                        completionHandler:
-                                            (void (^)(BOOL,
-                                                      BOOL))completionHandler {
-  DCHECK(completionHandler);
+void JsSuggestionManager::FetchPreviousAndNextElementsPresenceInFrameWithID(
+    const std::string& frame_ID,
+    const std::string& form_name,
+    const std::string& field_name,
+    base::OnceCallback<void(bool, bool)> completion_handler) {
+  DCHECK(completion_handler);
   std::vector<base::Value> parameters;
-  parameters.push_back(base::Value(base::SysNSStringToUTF8(formName)));
-  parameters.push_back(base::Value(base::SysNSStringToUTF8(fieldName)));
+  parameters.push_back(base::Value(form_name));
+  parameters.push_back(base::Value(field_name));
   autofill::ExecuteJavaScriptFunction(
       "suggestion.hasPreviousNextElements", parameters,
-      [self frameWithFrameID:frameID],
-      autofill::CreateStringCallback(^(NSString* result) {
-        // The result maybe an empty string here due to 2 reasons:
-        // 1) When there is an exception running the JS
-        // 2) There is a race when the page is changing due to which
-        // JSSuggestionManager has not yet injected __gCrWeb.suggestion
-        // object Handle this case gracefully. If a page has overridden
-        // Array.toString, the string returned may not contain a ",",
-        // hence this is a defensive measure to early return.
-        NSArray* components = [result componentsSeparatedByString:@","];
-        if (components.count != 2) {
-          completionHandler(NO, NO);
-          return;
-        }
-
-        DCHECK([components[0] isEqualToString:@"true"] ||
-               [components[0] isEqualToString:@"false"]);
-        BOOL hasPreviousElement = [components[0] isEqualToString:@"true"];
-        DCHECK([components[1] isEqualToString:@"true"] ||
-               [components[1] isEqualToString:@"false"]);
-        BOOL hasNextElement = [components[1] isEqualToString:@"true"];
-        completionHandler(hasPreviousElement, hasNextElement);
-      }));
+      GetFrameWithFrameID(frame_ID),
+      base::BindOnce(
+          &JsSuggestionManager::PreviousAndNextElementsPresenceResult,
+          weak_ptr_factory_.GetWeakPtr(), std::move(completion_handler)));
 }
 
-- (void)closeKeyboardForFrameWithID:(NSString*)frameID {
+void JsSuggestionManager::PreviousAndNextElementsPresenceResult(
+    base::OnceCallback<void(bool, bool)> completion_handler,
+    const base::Value* res) {
+  NSString* result = nil;
+  if (res && res->is_string()) {
+    result = base::SysUTF8ToNSString(res->GetString());
+  }
+  // The result maybe an empty string here due to 2 reasons:
+  // 1) When there is an exception running the JS
+  // 2) There is a race when the page is changing due to which
+  // JSSuggestionManager has not yet injected __gCrWeb.suggestion
+  // object Handle this case gracefully. If a page has overridden
+  // Array.toString, the string returned may not contain a ",",
+  // hence this is a defensive measure to early return.
+  NSArray* components = [result componentsSeparatedByString:@","];
+  if (components.count != 2) {
+    std::move(completion_handler).Run(false, false);
+    return;
+  }
+
+  DCHECK([components[0] isEqualToString:@"true"] ||
+         [components[0] isEqualToString:@"false"]);
+  bool has_previous_element = [components[0] isEqualToString:@"true"];
+  DCHECK([components[1] isEqualToString:@"true"] ||
+         [components[1] isEqualToString:@"false"]);
+  bool has_next_element = [components[1] isEqualToString:@"true"];
+  std::move(completion_handler).Run(has_previous_element, has_next_element);
+}
+
+void JsSuggestionManager::CloseKeyboardForFrameWithID(
+    const std::string& frame_ID) {
   std::vector<base::Value> parameters;
-  autofill::ExecuteJavaScriptFunction(
-      "suggestion.blurActiveElement", parameters,
-      [self frameWithFrameID:frameID], autofill::JavaScriptResultCallback());
+  autofill::ExecuteJavaScriptFunction("suggestion.blurActiveElement",
+                                      parameters, GetFrameWithFrameID(frame_ID),
+                                      autofill::JavaScriptResultCallback());
 }
 
-- (web::WebFrame*)frameWithFrameID:(NSString*)frameID {
-  DCHECK(_webFramesManager);
-  return _webFramesManager->GetFrameWithId(base::SysNSStringToUTF8(frameID));
+web::WebFrame* JsSuggestionManager::GetFrameWithFrameID(
+    const std::string& frame_ID) {
+  return web_state_->GetWebFramesManager()->GetFrameWithId(frame_ID);
 }
 
-@end
+WEB_STATE_USER_DATA_KEY_IMPL(JsSuggestionManager)
+
+}  // namspace autofill
diff --git a/components/autofill_assistant/browser/model.proto b/components/autofill_assistant/browser/model.proto
index b5441d6..876ffe4 100644
--- a/components/autofill_assistant/browser/model.proto
+++ b/components/autofill_assistant/browser/model.proto
@@ -271,6 +271,12 @@
   // HIGHLIGHTED_ACTION.
   DONE_ACTION = 6;
 
+  // A "Send feedback" chip, which will show the feedback form when clicked.
+  //
+  // Note that when this is used inside a script, we will continue the script
+  // normally once the user has clicked the chip.
+  FEEDBACK_ACTION = 7;
+
   reserved 2;
 }
 
diff --git a/components/autofill_assistant/browser/service/lite_service_util.cc b/components/autofill_assistant/browser/service/lite_service_util.cc
index 3bd4846b..18aec34 100644
--- a/components/autofill_assistant/browser/service/lite_service_util.cc
+++ b/components/autofill_assistant/browser/service/lite_service_util.cc
@@ -117,6 +117,7 @@
     case HIGHLIGHTED_ACTION:
     case NORMAL_ACTION:
     case CANCEL_ACTION:
+    case FEEDBACK_ACTION:
       return ActionResponseType::UNKNOWN;
     case CLOSE_ACTION:
       return ActionResponseType::PROMPT_CLOSE;
diff --git a/components/performance_manager/public/mojom/web_memory.mojom b/components/performance_manager/public/mojom/web_memory.mojom
index 7b3888b..b3110b1 100644
--- a/components/performance_manager/public/mojom/web_memory.mojom
+++ b/components/performance_manager/public/mojom/web_memory.mojom
@@ -47,13 +47,21 @@
   string? id;
 };
 
+// The amount of memory used by a breakdown.
+struct WebMemoryUsage {
+  uint64 bytes;
+};
+
 // Describes a memory region and attributes it to a set of contexts.
 // Usually the set consists of a single context. If there are multiple
 // contexts then this means that the memory may be owned by any of them.
 struct WebMemoryBreakdownEntry {
-  uint64 bytes;
-  array<WebMemoryAttribution> attribution;
+  // The memory used in this breakdown. It is null for breakdowns that did not
+  // have a memory measurement (for example a frame that was added after the
+  // measurement started).
+  WebMemoryUsage? memory;
 
+  array<WebMemoryAttribution> attribution;
   // TODO(1085129): Add memory types once they are implemented.
 };
 
diff --git a/components/performance_manager/v8_memory/v8_memory_test_helpers.cc b/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
index d73875e..89eb97b 100644
--- a/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
+++ b/components/performance_manager/v8_memory/v8_memory_test_helpers.cc
@@ -19,6 +19,7 @@
 #include "components/performance_manager/graph/process_node_impl.h"
 #include "components/performance_manager/public/mojom/v8_contexts.mojom.h"
 #include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/v8_memory/v8_context_tracker.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
@@ -226,6 +227,7 @@
   GetGraphFeaturesHelper().EnableV8ContextTracker();
   Super::SetUp();
   process_ = CreateNode<ProcessNodeImpl>();
+  other_process_ = CreateNode<ProcessNodeImpl>();
   pages_.push_back(CreateNode<PageNodeImpl>());
 }
 
@@ -239,6 +241,7 @@
     Bytes memory_usage,
     FrameNodeImpl* parent,
     FrameNodeImpl* opener,
+    ProcessNodeImpl* process,
     base::Optional<std::string> id_attribute,
     base::Optional<std::string> src_attribute) {
   // If there's an opener, the new frame is also a new page.
@@ -252,14 +255,16 @@
   int frame_tree_node_id = GetNextUniqueId();
   int frame_routing_id = GetNextUniqueId();
   auto frame_token = blink::LocalFrameToken();
-  auto frame = CreateNode<FrameNodeImpl>(process_.get(), page, parent,
+  auto frame = CreateNode<FrameNodeImpl>(process, page, parent,
                                          frame_tree_node_id, frame_routing_id,
                                          frame_token, browsing_instance_id);
   if (url) {
     frame->OnNavigationCommitted(GURL(*url), /*same document*/ true);
   }
-  V8DetailedMemoryExecutionContextData::CreateForTesting(frame.get())
-      ->set_v8_bytes_used(memory_usage.bytes);
+  if (memory_usage) {
+    V8DetailedMemoryExecutionContextData::CreateForTesting(frame.get())
+        ->set_v8_bytes_used(memory_usage.value());
+  }
   frames_.push_back(std::move(frame));
   FrameNodeImpl* frame_impl = frames_.back().get();
 
@@ -284,9 +289,20 @@
     DCHECK(!id_attribute);
     DCHECK(!src_attribute);
   }
+
+  // If the frame is in the same process as its parent include the attribution
+  // in OnV8ContextCreated, otherwise it must be attached separately with
+  // OnRemoteIframeAttached.
   DCHECK(frame_impl->process_node());
-  frame_impl->process_node()->OnV8ContextCreated(std::move(description),
-                                                 std::move(attribution));
+  if (parent && parent->process_node() != frame_impl->process_node()) {
+    frame_impl->process_node()->OnV8ContextCreated(
+        std::move(description), mojom::IframeAttributionDataPtr());
+    V8ContextTracker::GetFromGraph(graph())->OnRemoteIframeAttachedForTesting(
+        frame_impl, parent, blink::RemoteFrameToken(), std::move(attribution));
+  } else {
+    frame_impl->process_node()->OnV8ContextCreated(std::move(description),
+                                                   std::move(attribution));
+  }
 
   return frame_impl;
 }
diff --git a/components/performance_manager/v8_memory/v8_memory_test_helpers.h b/components/performance_manager/v8_memory/v8_memory_test_helpers.h
index fdae0c8..90810172 100644
--- a/components/performance_manager/v8_memory/v8_memory_test_helpers.h
+++ b/components/performance_manager/v8_memory/v8_memory_test_helpers.h
@@ -225,10 +225,7 @@
   using Super = GraphTestHarness;
 
   // Wrapper for memory usage bytes to improve test readability.
-  struct Bytes {
-    uint64_t bytes;
-    bool operator==(const Bytes& other) const { return bytes == other.bytes; }
-  };
+  using Bytes = base::Optional<uint64_t>;
 
   WebMemoryTestHarness();
   ~WebMemoryTestHarness() override;
@@ -243,7 +240,8 @@
       base::Optional<std::string> id_attribute = base::nullopt,
       base::Optional<std::string> src_attribute = base::nullopt) {
     return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes, parent,
-                            /*opener=*/nullptr, id_attribute, src_attribute);
+                            /*opener=*/nullptr, process_.get(), id_attribute,
+                            src_attribute);
   }
 
   // Creates a frame node as if from window.open and adds it to the graph.
@@ -251,7 +249,9 @@
                                         Bytes bytes,
                                         FrameNodeImpl* opener) {
     return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes,
-                            /*parent=*/nullptr, opener);
+                            /*parent=*/nullptr, opener, process_.get(),
+                            /*id_attribute=*/base::nullopt,
+                            /*src_attribute=*/base::nullopt);
   }
 
   // Creates a frame node in a different browsing instance and adds it to the
@@ -263,7 +263,8 @@
       base::Optional<std::string> id_attribute = base::nullopt,
       base::Optional<std::string> src_attribute = base::nullopt) {
     return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId + 1, bytes, parent,
-                            /*opener=*/nullptr, id_attribute, src_attribute);
+                            /*opener=*/nullptr, process_.get(), id_attribute,
+                            src_attribute);
   }
 
   // Creates a frame node in a different browsing instance as if from
@@ -273,7 +274,21 @@
       Bytes bytes,
       FrameNodeImpl* opener) {
     return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId + 1, bytes,
-                            /*parent=*/nullptr, opener);
+                            /*parent=*/nullptr, opener, process_.get(),
+                            /*id_attribute=*/base::nullopt,
+                            /*src_attribute=*/base::nullopt);
+  }
+
+  // Creates a frame node in a different process and adds it to the graph.
+  FrameNodeImpl* AddCrossProcessFrameNode(
+      std::string url,
+      Bytes bytes,
+      FrameNodeImpl* parent,
+      base::Optional<std::string> id_attribute = base::nullopt,
+      base::Optional<std::string> src_attribute = base::nullopt) {
+    return AddFrameNodeImpl(url, kDefaultBrowsingInstanceId, bytes, parent,
+                            /*opener=*/nullptr, other_process_.get(),
+                            id_attribute, src_attribute);
   }
 
   ProcessNode* process_node() const { return process_.get(); }
@@ -282,16 +297,17 @@
   static constexpr int kDefaultBrowsingInstanceId = 0;
 
   // Creates and adds a new frame node to the graph.
-  FrameNodeImpl* AddFrameNodeImpl(
-      base::Optional<std::string> url,
-      int browsing_instance_id,
-      Bytes bytes,
-      FrameNodeImpl* parent = nullptr,
-      FrameNodeImpl* opener = nullptr,
-      base::Optional<std::string> id_attribute = base::nullopt,
-      base::Optional<std::string> src_attribute = base::nullopt);
+  FrameNodeImpl* AddFrameNodeImpl(base::Optional<std::string> url,
+                                  int browsing_instance_id,
+                                  Bytes bytes,
+                                  FrameNodeImpl* parent,
+                                  FrameNodeImpl* opener,
+                                  ProcessNodeImpl* process,
+                                  base::Optional<std::string> id_attribute,
+                                  base::Optional<std::string> src_attribute);
   int GetNextUniqueId();
   TestNodeWrapper<ProcessNodeImpl> process_;
+  TestNodeWrapper<ProcessNodeImpl> other_process_;
   std::vector<TestNodeWrapper<PageNodeImpl>> pages_;
   std::vector<TestNodeWrapper<FrameNodeImpl>> frames_;
   int next_unique_id_ = 0;
diff --git a/components/performance_manager/v8_memory/web_memory_aggregator.cc b/components/performance_manager/v8_memory/web_memory_aggregator.cc
index ebd5529..845cc36 100644
--- a/components/performance_manager/v8_memory/web_memory_aggregator.cc
+++ b/components/performance_manager/v8_memory/web_memory_aggregator.cc
@@ -141,13 +141,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   aggregation_result_ = mojom::WebMemoryMeasurement::New();
   VisitFrame(nullptr, aggregation_start_node_);
-
-  // Don't report breakdowns without any memory use.
-  base::EraseIf(aggregation_result_->breakdown,
-                [](const mojom::WebMemoryBreakdownEntryPtr& entry) {
-                  return entry->bytes == 0;
-                });
-
   return std::move(aggregation_result_);
 }
 
@@ -221,7 +214,18 @@
   DCHECK(aggregation_point);
   if (auto* frame_data =
           V8DetailedMemoryExecutionContextData::ForFrameNode(frame_node)) {
-    aggregation_point->bytes += frame_data->v8_bytes_used();
+    if (!aggregation_point->memory) {
+      aggregation_point->memory = mojom::WebMemoryUsage::New();
+    }
+
+    // Ensure this frame is actually in the same process as the requesting
+    // frame. If not it should be considered to have 0 bytes.
+    // (https://github.com/WICG/performance-measure-memory/issues/20).
+    uint64_t bytes_used = (frame_node->GetProcessNode() ==
+                           aggregation_start_node_->GetProcessNode())
+                              ? frame_data->v8_bytes_used()
+                              : 0;
+    aggregation_point->memory->bytes += bytes_used;
   }
 
   // Recurse into children and opened pages. This node's aggregation point
@@ -269,10 +273,16 @@
 
   // Follow parent and opener links to find the most general same-site node to
   // start the aggregation traversal from.
-  const FrameNode* start_node = requesting_node;
-  while (auto* parent_or_opener =
-             GetSameOriginParentOrOpener(start_node, requesting_origin)) {
-    start_node = parent_or_opener;
+  const FrameNode* start_node = nullptr;
+  for (auto* parent_or_opener = requesting_node; parent_or_opener;
+       parent_or_opener =
+           GetSameOriginParentOrOpener(parent_or_opener, requesting_origin)) {
+    // Only consider nodes in the same process as potential start nodes.
+    // (https://github.com/WICG/performance-measure-memory/issues/20).
+    if (parent_or_opener->GetProcessNode() ==
+        requesting_node->GetProcessNode()) {
+      start_node = parent_or_opener;
+    }
   }
 
   DCHECK(start_node);
diff --git a/components/performance_manager/v8_memory/web_memory_aggregator_unittest.cc b/components/performance_manager/v8_memory/web_memory_aggregator_unittest.cc
index c41f2c9..0590a640 100644
--- a/components/performance_manager/v8_memory/web_memory_aggregator_unittest.cc
+++ b/components/performance_manager/v8_memory/web_memory_aggregator_unittest.cc
@@ -30,7 +30,7 @@
 using WebMemoryAggregatorTest = WebMemoryTestHarness;
 
 struct ExpectedMemoryBreakdown {
-  uint64_t bytes = 0U;
+  WebMemoryTestHarness::Bytes bytes;
   AttributionScope scope = AttributionScope::kWindow;
   base::Optional<std::string> url;
   base::Optional<std::string> id;
@@ -38,7 +38,7 @@
 
   ExpectedMemoryBreakdown() = default;
   ExpectedMemoryBreakdown(
-      uint64_t expected_bytes,
+      WebMemoryTestHarness::Bytes expected_bytes,
       AttributionScope expected_scope,
       base::Optional<std::string> expected_url = base::nullopt,
       base::Optional<std::string> expected_id = base::nullopt,
@@ -59,7 +59,10 @@
   auto expected_measurement = mojom::WebMemoryMeasurement::New();
   for (const auto& breakdown : breakdowns) {
     auto expected_breakdown = mojom::WebMemoryBreakdownEntry::New();
-    expected_breakdown->bytes = breakdown.bytes;
+    if (breakdown.bytes) {
+      expected_breakdown->memory = mojom::WebMemoryUsage::New();
+      expected_breakdown->memory->bytes = breakdown.bytes.value();
+    }
 
     auto attribution = mojom::WebMemoryAttribution::New();
     attribution->scope = breakdown.scope;
@@ -129,15 +132,18 @@
     internal::CopyBreakdownAttribution(breakdown_with_url,
                                        breakdown_with_empty_url);
 
-    // All measurements should be created with 0 bytes.
+    // All measurements should be created without measurement results.
     auto expected_result = CreateExpectedMemoryMeasurement({
-        ExpectedMemoryBreakdown(0, AttributionScope::kCrossOriginAggregated,
+        ExpectedMemoryBreakdown(/*bytes=*/base::nullopt,
+                                AttributionScope::kCrossOriginAggregated,
                                 /*expected_url=*/base::nullopt,
                                 /*expected_id=*/base::nullopt,
                                 /*expected_src=*/base::nullopt),
-        ExpectedMemoryBreakdown(0, AttributionScope::kWindow,
+        ExpectedMemoryBreakdown(/*bytes=*/base::nullopt,
+                                AttributionScope::kWindow,
                                 "https://example.com", attribute, attribute),
-        ExpectedMemoryBreakdown(0, AttributionScope::kWindow,
+        ExpectedMemoryBreakdown(/*bytes=*/base::nullopt,
+                                AttributionScope::kWindow,
                                 /*expected_url=*/"", attribute, attribute),
     });
     EXPECT_EQ(MeasurementToJSON(measurement),
@@ -293,10 +299,21 @@
       AddFrameNode("https://example.com/iframe3", Bytes{6}, subframe3,
                    "example-id6", "https://example.com/iframe3");
 
-  // A frame with 0 bytes of memory use (eg. a frame that's added to the frame
-  // tree during the measurement) should not appear in the result.
+  // To test aggregation all the frames above are in the same process, even
+  // though in production frames with different origins will be in different
+  // processes whenever possible. Frames in a different process from the
+  // requesting frame should all have 0 bytes reported.
+  FrameNodeImpl* cross_process_frame =
+      AddCrossProcessFrameNode("https://example.com/cross_process", Bytes{100},
+                               subframe3, "cross-process-id1");
+  FrameNodeImpl* cross_process_frame2 =
+      AddCrossProcessFrameNode("https://foo.com/cross_process", Bytes{200},
+                               subframe3, "cross-process-id2");
+
+  // A frame without a memory measurement (eg. a frame that's added to the frame
+  // tree during the measurement) should not have a memory entry in the result.
   FrameNodeImpl* empty_frame =
-      AddFrameNode("https://example.com/empty_frame", Bytes{0}, subframe3);
+      AddFrameNode("https://example.com/empty_frame", base::nullopt, subframe3);
 
   EXPECT_EQ(internal::FindAggregationStartNode(main_frame), main_frame);
   WebMemoryAggregator aggregator(main_frame);
@@ -339,6 +356,16 @@
   EXPECT_EQ(internal::GetSameOriginParentOrOpener(
                 empty_frame, aggregator.requesting_origin()),
             subframe3);
+  EXPECT_EQ(aggregator.FindNodeAggregationType(cross_process_frame),
+            NodeAggregationType::kSameOriginAggregationPoint);
+  EXPECT_EQ(internal::GetSameOriginParentOrOpener(
+                cross_process_frame, aggregator.requesting_origin()),
+            subframe3);
+  EXPECT_EQ(aggregator.FindNodeAggregationType(cross_process_frame2),
+            NodeAggregationType::kCrossOriginAggregationPoint);
+  EXPECT_EQ(internal::GetSameOriginParentOrOpener(
+                cross_process_frame2, aggregator.requesting_origin()),
+            subframe3);
 
   auto expected_result = CreateExpectedMemoryMeasurement({
       ExpectedMemoryBreakdown(10, AttributionScope::kWindow,
@@ -358,6 +385,13 @@
       ExpectedMemoryBreakdown(6, AttributionScope::kWindow,
                               "https://example.com/iframe3", "example-id6",
                               "https://example.com/iframe3"),
+      ExpectedMemoryBreakdown(0, AttributionScope::kWindow,
+                              "https://example.com/cross_process",
+                              "cross-process-id1"),
+      ExpectedMemoryBreakdown(0, AttributionScope::kCrossOriginAggregated,
+                              base::nullopt, "cross-process-id2"),
+      ExpectedMemoryBreakdown(base::nullopt, AttributionScope::kWindow,
+                              "https://example.com/empty_frame"),
   });
   auto result = aggregator.AggregateMeasureMemoryResult();
   EXPECT_EQ(MeasurementToJSON(result), MeasurementToJSON(expected_result));
@@ -407,6 +441,29 @@
             MeasurementToJSON(main_frame_expected_result));
 }
 
+TEST_F(WebMemoryAggregatorTest, FindCrossProcessAggregationStartNode) {
+  FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{1});
+  FrameNodeImpl* cross_process_child = AddCrossProcessFrameNode(
+      "https://example.com/cross_process.html", Bytes{2}, main_frame);
+  FrameNodeImpl* same_process_child = AddFrameNode(
+      "https://example.com/same_process.html", Bytes{3}, cross_process_child);
+
+  auto origin = url::Origin::Create(GURL("https://example.com"));
+  ASSERT_EQ(internal::GetSameOriginParentOrOpener(cross_process_child, origin),
+            main_frame);
+  ASSERT_EQ(internal::GetSameOriginParentOrOpener(same_process_child, origin),
+            cross_process_child);
+
+  // |cross_process_child| has no ancestor in the same process as it.
+  EXPECT_EQ(internal::FindAggregationStartNode(cross_process_child),
+            cross_process_child);
+
+  // The search starting from |same_process_child| should skip over
+  // |cross_process_child|, which is in a different process, and find
+  // |main_frame| which is in the same process.
+  EXPECT_EQ(internal::FindAggregationStartNode(same_process_child), main_frame);
+}
+
 TEST_F(WebMemoryAggregatorTest, AggregateWindowOpener) {
   FrameNodeImpl* main_frame = AddFrameNode("https://example.com/", Bytes{10});
   FrameNodeImpl* child_frame = AddFrameNode("https://example.com/iframe.html",
diff --git a/components/performance_manager/v8_memory/web_memory_impl_unittest.cc b/components/performance_manager/v8_memory/web_memory_impl_unittest.cc
index d2dc784..a9718ace 100644
--- a/components/performance_manager/v8_memory/web_memory_impl_unittest.cc
+++ b/components/performance_manager/v8_memory/web_memory_impl_unittest.cc
@@ -70,12 +70,13 @@
         base::flat_map<std::string, Bytes> actual;
         for (const auto& entry : result->breakdown) {
           EXPECT_EQ(1u, entry->attribution.size());
-          if (mojom::WebMemoryAttribution::Scope::kWindow ==
-              entry->attribution[0]->scope) {
-            actual[*entry->attribution[0]->url] = Bytes{entry->bytes};
-          } else {
-            actual[*entry->attribution[0]->src] = Bytes{entry->bytes};
-          }
+          std::string attribution_tag =
+              (mojom::WebMemoryAttribution::Scope::kWindow ==
+               entry->attribution[0]->scope)
+                  ? *entry->attribution[0]->url
+                  : *entry->attribution[0]->src;
+          actual[attribution_tag] =
+              entry->memory ? Bytes{entry->memory->bytes} : base::nullopt;
         }
         EXPECT_EQ(expected, actual);
         measurement_done = true;
@@ -130,7 +131,8 @@
         const auto& entry = result->breakdown[0];
         EXPECT_EQ(1u, entry->attribution.size());
         EXPECT_EQ(kMainFrameUrl, *(entry->attribution[0]->url));
-        EXPECT_EQ(1001u, entry->bytes);
+        ASSERT_TRUE(entry->memory);
+        EXPECT_EQ(1001u, entry->memory->bytes);
         run_loop.Quit();
       });
   auto bad_message_callback =
diff --git a/components/safe_browsing/core/browser/BUILD.gn b/components/safe_browsing/core/browser/BUILD.gn
index 7d110830..c7dba7f 100644
--- a/components/safe_browsing/core/browser/BUILD.gn
+++ b/components/safe_browsing/core/browser/BUILD.gn
@@ -77,28 +77,7 @@
 }
 
 source_set("token_fetcher") {
-  sources = [
-    "safe_browsing_token_fetcher.cc",
-    "safe_browsing_token_fetcher.h",
-  ]
+  sources = [ "safe_browsing_token_fetcher.h" ]
 
-  deps = [
-    "//base",
-    "//components/safe_browsing/core/common:thread_utils",
-    "//components/signin/public/identity_manager",
-    "//google_apis",
-  ]
-}
-
-source_set("token_fetcher_unittest") {
-  testonly = true
-  sources = [ "safe_browsing_token_fetcher_unittest.cc" ]
-
-  deps = [
-    ":token_fetcher",
-    "//base/test:test_support",
-    "//components/safe_browsing/core/common:test_support",
-    "//components/signin/public/identity_manager:test_support",
-    "//testing/gtest",
-  ]
+  deps = [ "//base" ]
 }
diff --git a/components/safe_browsing/core/browser/safe_browsing_token_fetcher.h b/components/safe_browsing/core/browser/safe_browsing_token_fetcher.h
index de5f5a8..13fab0f 100644
--- a/components/safe_browsing/core/browser/safe_browsing_token_fetcher.h
+++ b/components/safe_browsing/core/browser/safe_browsing_token_fetcher.h
@@ -5,65 +5,24 @@
 #ifndef COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SAFE_BROWSING_TOKEN_FETCHER_H_
 #define COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SAFE_BROWSING_TOKEN_FETCHER_H_
 
-#include <memory>
-
 #include "base/callback.h"
-#include "base/containers/flat_map.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "components/signin/public/identity_manager/access_token_info.h"
-#include "components/signin/public/identity_manager/consent_level.h"
-#include "google_apis/gaia/google_service_auth_error.h"
-
-namespace signin {
-class IdentityManager;
-class AccessTokenFetcher;
-}  // namespace signin
 
 namespace safe_browsing {
 
-// This class is used to fetch access tokens for communcations with Safe
-// Browsing. It asynchronously returns the access token for the current
-// primary account, or nullopt if an error occurred. This must be
-// run on the UI thread.
+// This interface is used to fetch access tokens for communcations with Safe
+// Browsing. It asynchronously returns an access token for the current account
+// (as determined in concrete implementations), or the empty string if no access
+// token is available (e.g., an error occurred).
+// This must be run on the UI thread.
 class SafeBrowsingTokenFetcher {
  public:
-  using Callback =
-      base::OnceCallback<void(base::Optional<signin::AccessTokenInfo>)>;
+  using Callback = base::OnceCallback<void(const std::string& access_token)>;
 
-  // Create a SafeBrowsingTokenFetcher for the primary account of
-  // |identity_manager|. |identity_manager| is unowned, and must outlive this
-  // object.
-  explicit SafeBrowsingTokenFetcher(signin::IdentityManager* identity_manager);
+  virtual ~SafeBrowsingTokenFetcher() = default;
 
-  ~SafeBrowsingTokenFetcher();
-
-  // Begin fetching a token for the account with the given |consent_level|. The
+  // Begin fetching a token for the account. The
   // result will be returned in |callback|. Must be called on the UI thread.
-  void Start(signin::ConsentLevel consent_level, Callback callback);
-
- private:
-  void OnTokenFetched(int request_id,
-                      GoogleServiceAuthError error,
-                      signin::AccessTokenInfo access_token_info);
-  void OnTokenTimeout(int request_id);
-  void Finish(int request_id,
-              base::Optional<signin::AccessTokenInfo> token_info);
-
-  // Reference to the identity manager to fetch from.
-  signin::IdentityManager* identity_manager_;
-
-  // The count of requests sent. This is used as an ID for requests.
-  int requests_sent_;
-
-  // Active fetchers, keyed by ID.
-  base::flat_map<int, std::unique_ptr<signin::AccessTokenFetcher>>
-      token_fetchers_;
-
-  // Active callbacks, keyed by ID.
-  base::flat_map<int, Callback> callbacks_;
-
-  base::WeakPtrFactory<SafeBrowsingTokenFetcher> weak_ptr_factory_;
+  virtual void Start(Callback callback) = 0;
 };
 
 }  // namespace safe_browsing
diff --git a/components/safe_browsing/core/browser/safe_browsing_token_fetcher_unittest.cc b/components/safe_browsing/core/browser/safe_browsing_token_fetcher_unittest.cc
deleted file mode 100644
index 77137ed9..0000000
--- a/components/safe_browsing/core/browser/safe_browsing_token_fetcher_unittest.cc
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2020 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/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
-#include <memory>
-
-#include "base/run_loop.h"
-#include "base/test/task_environment.h"
-#include "components/safe_browsing/core/common/test_task_environment.h"
-#include "components/signin/public/identity_manager/identity_test_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace safe_browsing {
-
-class SafeBrowsingTokenFetcherTest : public ::testing::Test {
- public:
-  SafeBrowsingTokenFetcherTest()
-      : task_environment_(CreateTestTaskEnvironment()) {}
-
- protected:
-  std::unique_ptr<base::test::TaskEnvironment> task_environment_;
-  signin::IdentityTestEnvironment identity_test_environment_;
-};
-
-TEST_F(SafeBrowsingTokenFetcherTest, Success) {
-  identity_test_environment_.MakeUnconsentedPrimaryAccountAvailable(
-      "test@example.com");
-  base::Optional<signin::AccessTokenInfo> maybe_account_info;
-  SafeBrowsingTokenFetcher fetcher(
-      identity_test_environment_.identity_manager());
-  fetcher.Start(signin::ConsentLevel::kNotRequired,
-                base::BindOnce(
-                    [](base::Optional<signin::AccessTokenInfo>* target_info,
-                       base::Optional<signin::AccessTokenInfo> info) {
-                      *target_info = info;
-                    },
-                    &maybe_account_info));
-  identity_test_environment_
-      .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
-          "token", base::Time::Now());
-  ASSERT_TRUE(maybe_account_info.has_value());
-  EXPECT_EQ(maybe_account_info.value().token, "token");
-}
-
-TEST_F(SafeBrowsingTokenFetcherTest, Failure) {
-  identity_test_environment_.MakeUnconsentedPrimaryAccountAvailable(
-      "test@example.com");
-  base::Optional<signin::AccessTokenInfo> maybe_account_info;
-  SafeBrowsingTokenFetcher fetcher(
-      identity_test_environment_.identity_manager());
-  fetcher.Start(signin::ConsentLevel::kNotRequired,
-                base::BindOnce(
-                    [](base::Optional<signin::AccessTokenInfo>* target_info,
-                       base::Optional<signin::AccessTokenInfo> info) {
-                      *target_info = info;
-                    },
-                    &maybe_account_info));
-  identity_test_environment_
-      .WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
-          GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
-  ASSERT_FALSE(maybe_account_info.has_value());
-}
-
-TEST_F(SafeBrowsingTokenFetcherTest, NoSyncingAccount) {
-  identity_test_environment_.MakeUnconsentedPrimaryAccountAvailable(
-      "test@example.com");
-  base::Optional<signin::AccessTokenInfo> maybe_account_info;
-  SafeBrowsingTokenFetcher fetcher(
-      identity_test_environment_.identity_manager());
-  fetcher.Start(signin::ConsentLevel::kSync,
-                base::BindOnce(
-                    [](base::Optional<signin::AccessTokenInfo>* target_info,
-                       base::Optional<signin::AccessTokenInfo> info) {
-                      *target_info = info;
-                    },
-                    &maybe_account_info));
-  identity_test_environment_
-      .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
-          "token", base::Time::Now());
-  ASSERT_FALSE(maybe_account_info.has_value());
-}
-
-TEST_F(SafeBrowsingTokenFetcherTest, SyncSuccess) {
-  identity_test_environment_.MakePrimaryAccountAvailable("test@example.com");
-  base::Optional<signin::AccessTokenInfo> maybe_account_info;
-  SafeBrowsingTokenFetcher fetcher(
-      identity_test_environment_.identity_manager());
-  fetcher.Start(signin::ConsentLevel::kSync,
-                base::BindOnce(
-                    [](base::Optional<signin::AccessTokenInfo>* target_info,
-                       base::Optional<signin::AccessTokenInfo> info) {
-                      *target_info = info;
-                    },
-                    &maybe_account_info));
-  identity_test_environment_
-      .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
-          "token", base::Time::Now());
-  ASSERT_TRUE(maybe_account_info.has_value());
-  EXPECT_EQ(maybe_account_info.value().token, "token");
-}
-
-}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/browser/sync/BUILD.gn b/components/safe_browsing/core/browser/sync/BUILD.gn
new file mode 100644
index 0000000..68a0503
--- /dev/null
+++ b/components/safe_browsing/core/browser/sync/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright 2021 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("//build/config/features.gni")
+
+source_set("sync") {
+  sources = [
+    "safe_browsing_primary_account_token_fetcher.cc",
+    "safe_browsing_primary_account_token_fetcher.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/safe_browsing/core/browser:token_fetcher",
+    "//components/safe_browsing/core/common:thread_utils",
+    "//components/signin/public/identity_manager",
+    "//google_apis",
+  ]
+}
+
+source_set("unittests") {
+  testonly = true
+  sources = [ "safe_browsing_primary_account_token_fetcher_unittest.cc" ]
+
+  deps = [
+    ":sync",
+    "//base/test:test_support",
+    "//components/safe_browsing/core/common:test_support",
+    "//components/signin/public/identity_manager:test_support",
+    "//testing/gtest",
+  ]
+}
diff --git a/components/safe_browsing/core/browser/sync/README.md b/components/safe_browsing/core/browser/sync/README.md
new file mode 100644
index 0000000..6f45bc4
--- /dev/null
+++ b/components/safe_browsing/core/browser/sync/README.md
@@ -0,0 +1 @@
+Holds touchpoints for safe_browsing's integration with signin and sync.
diff --git a/components/safe_browsing/core/browser/safe_browsing_token_fetcher.cc b/components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.cc
similarity index 67%
rename from components/safe_browsing/core/browser/safe_browsing_token_fetcher.cc
rename to components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.cc
index 97a18d0c..f2e12675 100644
--- a/components/safe_browsing/core/browser/safe_browsing_token_fetcher.cc
+++ b/components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
+#include "components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h"
 
 #include "base/bind.h"
 #include "base/memory/weak_ptr.h"
@@ -33,7 +33,7 @@
 
 }  // namespace
 
-SafeBrowsingTokenFetcher::SafeBrowsingTokenFetcher(
+SafeBrowsingPrimaryAccountTokenFetcher::SafeBrowsingPrimaryAccountTokenFetcher(
     signin::IdentityManager* identity_manager)
     : identity_manager_(identity_manager),
       requests_sent_(0),
@@ -41,54 +41,56 @@
   DCHECK(CurrentlyOnThread(ThreadID::UI));
 }
 
-SafeBrowsingTokenFetcher::~SafeBrowsingTokenFetcher() {
+SafeBrowsingPrimaryAccountTokenFetcher::
+    ~SafeBrowsingPrimaryAccountTokenFetcher() {
   for (auto& id_and_callback : callbacks_) {
-    std::move(id_and_callback.second).Run(base::nullopt);
+    std::move(id_and_callback.second).Run(std::string());
   }
 }
 
-void SafeBrowsingTokenFetcher::Start(signin::ConsentLevel consent_level,
-                                     Callback callback) {
+void SafeBrowsingPrimaryAccountTokenFetcher::Start(
+    Callback callback) {
   DCHECK(CurrentlyOnThread(ThreadID::UI));
   const int request_id = requests_sent_;
   requests_sent_++;
-  CoreAccountId account_id =
-      identity_manager_->GetPrimaryAccountId(consent_level);
+  CoreAccountId account_id = identity_manager_->GetPrimaryAccountId(
+      signin::ConsentLevel::kNotRequired);
   callbacks_[request_id] = std::move(callback);
   token_fetchers_[request_id] =
       identity_manager_->CreateAccessTokenFetcherForAccount(
           account_id, "safe_browsing_service", {kAPIScope},
-          base::BindOnce(&SafeBrowsingTokenFetcher::OnTokenFetched,
-                         weak_ptr_factory_.GetWeakPtr(), request_id),
+          base::BindOnce(
+              &SafeBrowsingPrimaryAccountTokenFetcher::OnTokenFetched,
+              weak_ptr_factory_.GetWeakPtr(), request_id),
           signin::AccessTokenFetcher::Mode::kImmediate);
   base::PostDelayedTask(
       FROM_HERE, CreateTaskTraits(ThreadID::UI),
-      base::BindOnce(&SafeBrowsingTokenFetcher::OnTokenTimeout,
+      base::BindOnce(&SafeBrowsingPrimaryAccountTokenFetcher::OnTokenTimeout,
                      weak_ptr_factory_.GetWeakPtr(), request_id),
       base::TimeDelta::FromMilliseconds(kTimeoutDelayFromMilliseconds));
 }
 
-void SafeBrowsingTokenFetcher::OnTokenFetched(
+void SafeBrowsingPrimaryAccountTokenFetcher::OnTokenFetched(
     int request_id,
     GoogleServiceAuthError error,
     signin::AccessTokenInfo access_token_info) {
   UMA_HISTOGRAM_ENUMERATION("SafeBrowsing.TokenFetcher.ErrorType",
                             error.state(), GoogleServiceAuthError::NUM_STATES);
   if (error.state() == GoogleServiceAuthError::NONE)
-    Finish(request_id, access_token_info);
+    Finish(request_id, access_token_info.token);
   else
-    Finish(request_id, base::nullopt);
+    Finish(request_id, std::string());
 }
 
-void SafeBrowsingTokenFetcher::OnTokenTimeout(int request_id) {
-  Finish(request_id, base::nullopt);
+void SafeBrowsingPrimaryAccountTokenFetcher::OnTokenTimeout(int request_id) {
+  Finish(request_id, std::string());
 }
 
-void SafeBrowsingTokenFetcher::Finish(
+void SafeBrowsingPrimaryAccountTokenFetcher::Finish(
     int request_id,
-    base::Optional<signin::AccessTokenInfo> token_info) {
+    const std::string& access_token) {
   if (callbacks_.contains(request_id)) {
-    std::move(callbacks_[request_id]).Run(token_info);
+    std::move(callbacks_[request_id]).Run(access_token);
   }
 
   token_fetchers_.erase(request_id);
diff --git a/components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h b/components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h
new file mode 100644
index 0000000..8aebacd
--- /dev/null
+++ b/components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h
@@ -0,0 +1,64 @@
+// Copyright 2021 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_SAFE_BROWSING_CORE_BROWSER_SYNC_SAFE_BROWSING_PRIMARY_ACCOUNT_TOKEN_FETCHER_H_
+#define COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SYNC_SAFE_BROWSING_PRIMARY_ACCOUNT_TOKEN_FETCHER_H_
+
+#include <memory>
+
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+
+namespace signin {
+class AccessTokenFetcher;
+class IdentityManager;
+}  // namespace signin
+
+namespace safe_browsing {
+
+// This class fetches access tokens for Safe Browsing for the current
+// primary account.
+class SafeBrowsingPrimaryAccountTokenFetcher : public SafeBrowsingTokenFetcher {
+ public:
+  // Create a SafeBrowsingPrimaryAccountTokenFetcher for the primary account of
+  // |identity_manager|. |identity_manager| is unowned, and must outlive this
+  // object.
+  explicit SafeBrowsingPrimaryAccountTokenFetcher(
+      signin::IdentityManager* identity_manager);
+
+  ~SafeBrowsingPrimaryAccountTokenFetcher() override;
+
+  // SafeBrowsingTokenFetcher:
+  void Start(Callback callback) override;
+
+ private:
+  void OnTokenFetched(int request_id,
+                      GoogleServiceAuthError error,
+                      signin::AccessTokenInfo access_token_info);
+  void OnTokenTimeout(int request_id);
+  void Finish(int request_id, const std::string& access_token);
+
+  // Reference to the identity manager to fetch from.
+  signin::IdentityManager* identity_manager_;
+
+  // The count of requests sent. This is used as an ID for requests.
+  int requests_sent_;
+
+  // Active fetchers, keyed by ID.
+  base::flat_map<int, std::unique_ptr<signin::AccessTokenFetcher>>
+      token_fetchers_;
+
+  // Active callbacks, keyed by ID.
+  base::flat_map<int, Callback> callbacks_;
+
+  base::WeakPtrFactory<SafeBrowsingPrimaryAccountTokenFetcher>
+      weak_ptr_factory_;
+};
+
+}  // namespace safe_browsing
+
+#endif  // COMPONENTS_SAFE_BROWSING_CORE_BROWSER_SYNC_SAFE_BROWSING_PRIMARY_ACCOUNT_TOKEN_FETCHER_H_
diff --git a/components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher_unittest.cc b/components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher_unittest.cc
new file mode 100644
index 0000000..bf8418b0
--- /dev/null
+++ b/components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright 2020 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/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h"
+#include <memory>
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "components/safe_browsing/core/common/test_task_environment.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace safe_browsing {
+
+class SafeBrowsingPrimaryAccountTokenFetcherTest : public ::testing::Test {
+ public:
+  SafeBrowsingPrimaryAccountTokenFetcherTest()
+      : task_environment_(CreateTestTaskEnvironment()) {}
+
+ protected:
+  std::unique_ptr<base::test::TaskEnvironment> task_environment_;
+  signin::IdentityTestEnvironment identity_test_environment_;
+};
+
+TEST_F(SafeBrowsingPrimaryAccountTokenFetcherTest, Success) {
+  identity_test_environment_.MakeUnconsentedPrimaryAccountAvailable(
+      "test@example.com");
+  std::string access_token;
+  SafeBrowsingPrimaryAccountTokenFetcher fetcher(
+      identity_test_environment_.identity_manager());
+  fetcher.Start(
+      base::BindOnce([](std::string* target_token,
+                        const std::string& token) { *target_token = token; },
+                     &access_token));
+  identity_test_environment_
+      .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+          "token", base::Time::Now());
+  EXPECT_EQ(access_token, "token");
+}
+
+TEST_F(SafeBrowsingPrimaryAccountTokenFetcherTest, Failure) {
+  identity_test_environment_.MakeUnconsentedPrimaryAccountAvailable(
+      "test@example.com");
+  std::string access_token;
+  SafeBrowsingPrimaryAccountTokenFetcher fetcher(
+      identity_test_environment_.identity_manager());
+  fetcher.Start(
+      base::BindOnce([](std::string* target_token,
+                        const std::string& token) { *target_token = token; },
+                     &access_token));
+  identity_test_environment_
+      .WaitForAccessTokenRequestIfNecessaryAndRespondWithError(
+          GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED));
+  ASSERT_TRUE(access_token.empty());
+}
+
+TEST_F(SafeBrowsingPrimaryAccountTokenFetcherTest,
+       SuccessWithConsentedPrimaryAccount) {
+  identity_test_environment_.MakePrimaryAccountAvailable("test@example.com");
+  std::string access_token;
+  SafeBrowsingPrimaryAccountTokenFetcher fetcher(
+      identity_test_environment_.identity_manager());
+  fetcher.Start(
+      base::BindOnce([](std::string* target_token,
+                        const std::string& token) { *target_token = token; },
+                     &access_token));
+  identity_test_environment_
+      .WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
+          "token", base::Time::Now());
+  EXPECT_EQ(access_token, "token");
+}
+
+}  // namespace safe_browsing
diff --git a/components/safe_browsing/core/realtime/BUILD.gn b/components/safe_browsing/core/realtime/BUILD.gn
index 1a0aa02..f600bd8c 100644
--- a/components/safe_browsing/core/realtime/BUILD.gn
+++ b/components/safe_browsing/core/realtime/BUILD.gn
@@ -38,7 +38,7 @@
     "//components/safe_browsing/core:csd_proto",
     "//components/safe_browsing/core:realtimeapi_proto",
     "//components/safe_browsing/core:verdict_cache_manager",
-    "//components/safe_browsing/core/browser:token_fetcher",
+    "//components/safe_browsing/core/browser/sync",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/safe_browsing/core/common:thread_utils",
     "//components/safe_browsing/core/db:v4_protocol_manager_util",
diff --git a/components/safe_browsing/core/realtime/url_lookup_service.cc b/components/safe_browsing/core/realtime/url_lookup_service.cc
index 6d0851c..1c796a7 100644
--- a/components/safe_browsing/core/realtime/url_lookup_service.cc
+++ b/components/safe_browsing/core/realtime/url_lookup_service.cc
@@ -13,7 +13,7 @@
 #include "base/time/time.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/buildflags.h"
-#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
+#include "components/safe_browsing/core/browser/sync/safe_browsing_primary_account_token_fetcher.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/core/common/thread_utils.h"
 #include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
@@ -54,8 +54,8 @@
       pref_service_(pref_service),
       is_off_the_record_(is_off_the_record),
       variations_(variations_service) {
-  token_fetcher_ =
-      std::make_unique<SafeBrowsingTokenFetcher>(identity_manager_);
+  token_fetcher_ = std::make_unique<SafeBrowsingPrimaryAccountTokenFetcher>(
+      identity_manager_);
 }
 
 void RealTimeUrlLookupService::GetAccessToken(
@@ -63,7 +63,6 @@
     RTLookupRequestCallback request_callback,
     RTLookupResponseCallback response_callback) {
   token_fetcher_->Start(
-      signin::ConsentLevel::kNotRequired,
       base::BindOnce(&RealTimeUrlLookupService::OnGetAccessToken,
                      weak_factory_.GetWeakPtr(), url,
                      std::move(request_callback), std::move(response_callback),
@@ -75,14 +74,12 @@
     RTLookupRequestCallback request_callback,
     RTLookupResponseCallback response_callback,
     base::TimeTicks get_token_start_time,
-    base::Optional<signin::AccessTokenInfo> access_token_info) {
+    const std::string& access_token) {
   base::UmaHistogramTimes("SafeBrowsing.RT.GetToken.Time",
                           base::TimeTicks::Now() - get_token_start_time);
   base::UmaHistogramBoolean("SafeBrowsing.RT.HasTokenFromFetcher",
-                            access_token_info.has_value());
-  std::string access_token_string =
-      access_token_info.value_or(signin::AccessTokenInfo()).token;
-  SendRequest(url, access_token_string, std::move(request_callback),
+                            !access_token.empty());
+  SendRequest(url, access_token, std::move(request_callback),
               std::move(response_callback));
 }
 
diff --git a/components/safe_browsing/core/realtime/url_lookup_service.h b/components/safe_browsing/core/realtime/url_lookup_service.h
index 5f4de4d..2eebf4a 100644
--- a/components/safe_browsing/core/realtime/url_lookup_service.h
+++ b/components/safe_browsing/core/realtime/url_lookup_service.h
@@ -84,12 +84,11 @@
   bool ShouldIncludeCredentials() const override;
 
   // Called when the access token is obtained from |token_fetcher_|.
-  void OnGetAccessToken(
-      const GURL& url,
-      RTLookupRequestCallback request_callback,
-      RTLookupResponseCallback response_callback,
-      base::TimeTicks get_token_start_time,
-      base::Optional<signin::AccessTokenInfo> access_token_info);
+  void OnGetAccessToken(const GURL& url,
+                        RTLookupRequestCallback request_callback,
+                        RTLookupResponseCallback response_callback,
+                        base::TimeTicks get_token_start_time,
+                        const std::string& access_token);
 
   // Unowned object used for getting access token when real time url check with
   // token is enabled.
diff --git a/components/services/storage/dom_storage/session_storage_impl.cc b/components/services/storage/dom_storage/session_storage_impl.cc
index 5238f8e..40e8a56 100644
--- a/components/services/storage/dom_storage/session_storage_impl.cc
+++ b/components/services/storage/dom_storage/session_storage_impl.cc
@@ -995,19 +995,21 @@
     std::move(callbacks[i]).Run();
 }
 
+void SessionStorageImpl::PurgeAllNamespaces() {
+  for (const auto& it : data_maps_)
+    it.second->storage_area()->CancelAllPendingRequests();
+  for (const auto& namespace_pair : namespaces_)
+    namespace_pair.second->Reset();
+  DCHECK(data_maps_.empty());
+}
+
 void SessionStorageImpl::DeleteAndRecreateDatabase(const char* histogram_name) {
   if (connection_state_ == CONNECTION_SHUTDOWN)
     return;
 
   // We're about to set database_ to null, so delete the StorageAreas
   // that might still be using the old database.
-  for (const auto& it : data_maps_)
-    it.second->storage_area()->CancelAllPendingRequests();
-
-  for (const auto& namespace_pair : namespaces_) {
-    namespace_pair.second->Reset();
-  }
-  DCHECK(data_maps_.empty());
+  PurgeAllNamespaces();
 
   // Reset state to be in process of connecting. This will cause requests for
   // StorageAreas to be queued until the connection is complete.
@@ -1059,6 +1061,7 @@
 void SessionStorageImpl::OnShutdownComplete() {
   DCHECK(shutdown_complete_callback_);
   // Flush any final tasks on the DB task runner before invoking the callback.
+  PurgeAllNamespaces();
   database_.reset();
   leveldb_task_runner_->PostTaskAndReply(
       FROM_HERE, base::DoNothing(), std::move(shutdown_complete_callback_));
diff --git a/components/services/storage/dom_storage/session_storage_impl.h b/components/services/storage/dom_storage/session_storage_impl.h
index 4d2cb96..206ddae 100644
--- a/components/services/storage/dom_storage/session_storage_impl.h
+++ b/components/services/storage/dom_storage/session_storage_impl.h
@@ -221,6 +221,7 @@
   MetadataParseResult ParseNextMapId(ValueAndStatus next_map_id);
 
   void OnConnectionFinished();
+  void PurgeAllNamespaces();
   void DeleteAndRecreateDatabase(const char* histogram_name);
   void OnDBDestroyed(bool recreate_in_memory, leveldb::Status status);
 
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
index afcaf9a..e847b80 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacade.java
@@ -24,6 +24,16 @@
  */
 public interface AccountManagerFacade {
     /**
+     * Listener for {@link ChildAccountStatus.Status}.
+     */
+    interface ChildAccountStatusListener {
+        /**
+         * The method is called when child account status is ready.
+         */
+        void onStatusReady(@ChildAccountStatus.Status int status);
+    }
+
+    /**
      * Adds an observer to receive accounts change notifications.
      * @param observer the observer to add.
      */
@@ -118,13 +128,13 @@
     void invalidateAccessToken(String accessToken) throws AuthException;
 
     /**
-     * Checks the child account status in background.
+     * Checks the child account status of the given account.
      *
-     * @param account The account to check the child account status
-     * @param callback The callback takes the ChildAccountStatus.Status as argument
+     * @param account The account to check the child account status.
+     * @param listener The listener is called when the {@link ChildAccountStatus.Status} is ready.
      */
     @MainThread
-    void checkChildAccountStatus(Account account, Callback<Integer> callback);
+    void checkChildAccountStatus(Account account, ChildAccountStatusListener listener);
 
     /**
      * Creates an intent that will ask the user to add a new account to the device. See
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
index 59163fa..2c8b62a 100644
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
+++ b/components/signin/core/browser/android/java/src/org/chromium/components/signin/AccountManagerFacadeImpl.java
@@ -221,7 +221,7 @@
     // overriding.
     @SuppressWarnings("WrongThread")
     @Override
-    public void checkChildAccountStatus(Account account, Callback<Integer> callback) {
+    public void checkChildAccountStatus(Account account, ChildAccountStatusListener listener) {
         ThreadUtils.assertOnUiThread();
         new AsyncTask<Integer>() {
             @Override
@@ -236,8 +236,8 @@
             }
 
             @Override
-            public void onPostExecute(@ChildAccountStatus.Status Integer value) {
-                callback.onResult(value);
+            public void onPostExecute(@ChildAccountStatus.Status Integer status) {
+                listener.onStatusReady(status);
             }
         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
     }
diff --git a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
index 82f4cb7..116bd424 100644
--- a/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
+++ b/components/signin/core/browser/android/javatests/src/org/chromium/components/signin/test/util/FakeAccountManagerFacade.java
@@ -127,7 +127,7 @@
     }
 
     @Override
-    public void checkChildAccountStatus(Account account, Callback<Integer> callback) {}
+    public void checkChildAccountStatus(Account account, ChildAccountStatusListener listener) {}
 
     @Override
     public void createAddAccountIntent(Callback<Intent> callback) {}
diff --git a/components/signin/core/browser/android/junit/src/org/chromium/components/signin/test/AccountManagerFacadeRobolectricTest.java b/components/signin/core/browser/android/junit/src/org/chromium/components/signin/test/AccountManagerFacadeRobolectricTest.java
index f5f0b7f..c946d08 100644
--- a/components/signin/core/browser/android/junit/src/org/chromium/components/signin/test/AccountManagerFacadeRobolectricTest.java
+++ b/components/signin/core/browser/android/junit/src/org/chromium/components/signin/test/AccountManagerFacadeRobolectricTest.java
@@ -271,8 +271,7 @@
         mDelegate.removeAccountHolderExplicitly(AccountHolder.builder(account).build());
     }
 
-    private void assertChildAccountStatus(
-            Account account, @ChildAccountStatus.Status Integer status) {
+    private void assertChildAccountStatus(Account account, @ChildAccountStatus.Status int status) {
         final AtomicInteger callCount = new AtomicInteger();
         mFacade.checkChildAccountStatus(account, result -> {
             callCount.incrementAndGet();
diff --git a/components/signin/internal/identity_manager/primary_account_manager.cc b/components/signin/internal/identity_manager/primary_account_manager.cc
index a3f3a572..7b95c04 100644
--- a/components/signin/internal/identity_manager/primary_account_manager.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager.cc
@@ -328,7 +328,6 @@
   if (primary_account_info().IsEmpty()) {
     return;
   }
-
   // TODO(crbug.com/887756): Consider moving this higher up, or document why
   // the above blocks are exempt from the |signout_decision| early return.
   if (signout_decision == SigninClient::SignoutDecision::DISALLOW_SIGNOUT) {
@@ -351,6 +350,12 @@
               kPrimaryAccountManager_ClearAccount);
       break;
     case RemoveAccountsOption::kKeepAllAccounts:
+      if (previous_state.consent_level == signin::ConsentLevel::kNotRequired) {
+        // Nothing to update as the primary account is already at kNotRequired
+        // consent level. Prefer returning to avoid firing useless
+        // OnPrimaryAccountChanged() notifications.
+        return;
+      }
       SetPrimaryAccountInternal(primary_account_info(),
                                 /*consented_to_sync=*/false);
       break;
diff --git a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
index 89a4638..f067f52 100644
--- a/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
+++ b/components/signin/internal/identity_manager/primary_account_manager_unittest.cc
@@ -43,9 +43,7 @@
       : test_signin_client_(&user_prefs_),
         token_service_(
             &user_prefs_,
-            std::make_unique<FakeProfileOAuth2TokenServiceDelegate>()),
-        num_successful_signins_(0),
-        num_unconsented_account_changed_(0) {
+            std::make_unique<FakeProfileOAuth2TokenServiceDelegate>()) {
     AccountFetcherService::RegisterPrefs(user_prefs_.registry());
     AccountTrackerService::RegisterPrefs(user_prefs_.registry());
     ProfileOAuth2TokenService::RegisterProfilePrefs(user_prefs_.registry());
@@ -135,7 +133,7 @@
         num_successful_signins_++;
         break;
       case signin::PrimaryAccountChangeEvent::Type::kCleared:
-        // ignored
+        num_successful_signouts_++;
         break;
       case signin::PrimaryAccountChangeEvent::Type::kNone:
         break;
@@ -163,8 +161,9 @@
   std::unique_ptr<PrimaryAccountManager> manager_;
   std::vector<std::string> oauth_tokens_fetched_;
   std::vector<std::string> cookies_;
-  int num_successful_signins_;
-  int num_unconsented_account_changed_;
+  int num_successful_signins_{0};
+  int num_successful_signouts_{0};
+  int num_unconsented_account_changed_{0};
 };
 
 #if !BUILDFLAG(IS_CHROMEOS_ASH)
@@ -176,6 +175,7 @@
       account_tracker()->GetAccountInfo(main_account_id));
   manager_->ClearPrimaryAccount(signin_metrics::SIGNOUT_TEST,
                                 signin_metrics::SignoutDelete::IGNORE_METRIC);
+  EXPECT_EQ(1, num_successful_signouts_);
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
   EXPECT_TRUE(
       manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email.empty());
@@ -211,6 +211,7 @@
                                 signin_metrics::SignoutDelete::IGNORE_METRIC);
 
   // Tokens are revoked.
+  EXPECT_EQ(1, num_successful_signouts_);
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
   EXPECT_TRUE(token_service_.GetAccounts().empty());
 }
@@ -229,10 +230,12 @@
   signin_client()->set_is_signout_allowed(false);
   manager_->ClearPrimaryAccount(signin_metrics::SIGNOUT_TEST,
                                 signin_metrics::SignoutDelete::IGNORE_METRIC);
+  EXPECT_EQ(0, num_successful_signouts_);
   EXPECT_TRUE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
   signin_client()->set_is_signout_allowed(true);
   manager_->ClearPrimaryAccount(signin_metrics::SIGNOUT_TEST,
                                 signin_metrics::SignoutDelete::IGNORE_METRIC);
+  EXPECT_EQ(1, num_successful_signouts_);
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
 }
 
@@ -282,6 +285,26 @@
 }
 #endif
 
+// Regression test for https://crbug.com/1155519.
+TEST_F(PrimaryAccountManagerTest, NoopSignOutDoesNotNotifyObservers) {
+  CreatePrimaryAccountManager();
+  EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
+
+  CoreAccountId account_id = AddToAccountTracker("gaia_id", "user@gmail.com");
+  CoreAccountInfo account_info = account_tracker()->GetAccountInfo(account_id);
+  manager_->SetUnconsentedPrimaryAccountInfo(account_info);
+  EXPECT_EQ(1, num_unconsented_account_changed_);
+  EXPECT_TRUE(manager_->HasPrimaryAccount(ConsentLevel::kNotRequired));
+  EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
+
+  manager_->RevokeSyncConsent(signin_metrics::SIGNOUT_TEST,
+                              signin_metrics::SignoutDelete::IGNORE_METRIC);
+
+  // Since there was no sync consent, observers shouldn't be notified.
+  EXPECT_EQ(0, num_successful_signouts_);
+  EXPECT_EQ(1, num_unconsented_account_changed_);
+}
+
 TEST_F(PrimaryAccountManagerTest, SignIn) {
   CreatePrimaryAccountManager();
   EXPECT_EQ("", manager_->GetPrimaryAccountInfo(ConsentLevel::kSync).email);
@@ -472,6 +495,7 @@
   account_info.email = "user@gmail.com";
   manager_->SetUnconsentedPrimaryAccountInfo(account_info);
   EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(0, num_successful_signouts_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
   EXPECT_EQ(account_info,
             manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
@@ -481,6 +505,7 @@
   // Set the same account again.
   manager_->SetUnconsentedPrimaryAccountInfo(account_info);
   EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(0, num_successful_signouts_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
   EXPECT_EQ(account_info,
             manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
@@ -492,6 +517,7 @@
   account_info.email = "us.er@gmail.com";
   manager_->SetUnconsentedPrimaryAccountInfo(account_info);
   EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(0, num_successful_signouts_);
   EXPECT_EQ(1, num_unconsented_account_changed_);
   EXPECT_EQ(account_info,
             manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
@@ -501,6 +527,7 @@
   // Clear it.
   manager_->SetUnconsentedPrimaryAccountInfo(CoreAccountInfo());
   EXPECT_EQ(0, num_successful_signins_);
+  EXPECT_EQ(0, num_successful_signouts_);
   EXPECT_EQ(2, num_unconsented_account_changed_);
   EXPECT_EQ(CoreAccountInfo(),
             manager_->GetPrimaryAccountInfo(ConsentLevel::kNotRequired));
@@ -517,6 +544,7 @@
 
   manager_->RevokeSyncConsent(signin_metrics::ProfileSignout::SIGNOUT_TEST,
                               signin_metrics::SignoutDelete::IGNORE_METRIC);
+  EXPECT_EQ(1, num_successful_signouts_);
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
   EXPECT_TRUE(manager_->HasPrimaryAccount(ConsentLevel::kNotRequired));
   EXPECT_EQ(
@@ -534,6 +562,7 @@
 
   manager_->ClearPrimaryAccount(signin_metrics::ProfileSignout::SIGNOUT_TEST,
                                 signin_metrics::SignoutDelete::IGNORE_METRIC);
+  EXPECT_EQ(1, num_successful_signouts_);
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kSync));
   EXPECT_FALSE(manager_->HasPrimaryAccount(ConsentLevel::kNotRequired));
 }
diff --git a/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountInfo.java b/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountInfo.java
index 87b5b5f..aa6fab7 100644
--- a/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountInfo.java
+++ b/components/signin/public/android/java/src/org/chromium/components/signin/base/AccountInfo.java
@@ -17,17 +17,35 @@
  * This class has a native counterpart called AccountInfo.
  */
 public class AccountInfo extends CoreAccountInfo {
+    private final String mFullName;
+    private final String mGivenName;
     private final @Nullable Bitmap mAccountImage;
 
     @VisibleForTesting
     @CalledByNative
-    public AccountInfo(
-            CoreAccountId id, String email, String gaiaId, @Nullable Bitmap accountImage) {
+    public AccountInfo(CoreAccountId id, String email, String gaiaId, String fullName,
+            String givenName, @Nullable Bitmap accountImage) {
         super(id, email, gaiaId);
+        mFullName = fullName;
+        mGivenName = givenName;
         mAccountImage = accountImage;
     }
 
     /**
+     * @return Full name of the account.
+     */
+    public String getFullName() {
+        return mFullName;
+    }
+
+    /**
+     * @return Given name of the account.
+     */
+    public String getGivenName() {
+        return mGivenName;
+    }
+
+    /**
      * Gets the account's image.
      * It can be the image user uploaded, monogram or null.
      */
diff --git a/components/signin/public/identity_manager/account_info.cc b/components/signin/public/identity_manager/account_info.cc
index 3ee85a7..9f6f24dc 100644
--- a/components/signin/public/identity_manager/account_info.cc
+++ b/components/signin/public/identity_manager/account_info.cc
@@ -157,6 +157,8 @@
       env, ConvertToJavaCoreAccountId(env, account_info.account_id),
       base::android::ConvertUTF8ToJavaString(env, account_info.email),
       base::android::ConvertUTF8ToJavaString(env, account_info.gaia),
+      base::android::ConvertUTF8ToJavaString(env, account_info.full_name),
+      base::android::ConvertUTF8ToJavaString(env, account_info.given_name),
       avatar_image.IsEmpty()
           ? nullptr
           : gfx::ConvertToJavaBitmap(*avatar_image.AsImageSkia().bitmap()));
diff --git a/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc b/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc
index e96ecfc9..df2d6e02 100644
--- a/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc
+++ b/components/signin/public/identity_manager/primary_account_access_token_fetcher.cc
@@ -77,25 +77,15 @@
       AccessTokenFetcher::Mode::kImmediate);
 }
 
-void PrimaryAccountAccessTokenFetcher::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  // When sync consent is not required the signin is handled in
-  // OnUnconsentedPrimaryAccountChanged() below.
-  if (consent_ == ConsentLevel::kNotRequired)
+void PrimaryAccountAccessTokenFetcher::OnPrimaryAccountChanged(
+    const PrimaryAccountChangeEvent& event) {
+  // We're only interested when the account is set for the |consent_|
+  // consent level.
+  if (event.GetEventTypeFor(consent_) !=
+      PrimaryAccountChangeEvent::Type::kSet) {
     return;
-  DCHECK(!primary_account_info.account_id.empty());
-  ProcessSigninStateChange();
-}
-
-void PrimaryAccountAccessTokenFetcher::OnUnconsentedPrimaryAccountChanged(
-    const CoreAccountInfo& primary_account_info) {
-  // This method is called after both SetPrimaryAccount and
-  // SetUnconsentedPrimaryAccount.
-  if (consent_ == ConsentLevel::kSync)
-    return;
-  // We're only interested when the account is set.
-  if (primary_account_info.account_id.empty())
-    return;
+  }
+  DCHECK(!event.GetCurrentState().primary_account.account_id.empty());
   ProcessSigninStateChange();
 }
 
diff --git a/components/signin/public/identity_manager/primary_account_access_token_fetcher.h b/components/signin/public/identity_manager/primary_account_access_token_fetcher.h
index 36d37f9..e982fa4 100644
--- a/components/signin/public/identity_manager/primary_account_access_token_fetcher.h
+++ b/components/signin/public/identity_manager/primary_account_access_token_fetcher.h
@@ -177,10 +177,7 @@
   void StartAccessTokenRequest();
 
   // IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnUnconsentedPrimaryAccountChanged(
-      const CoreAccountInfo& primary_account_info) override;
+  void OnPrimaryAccountChanged(const PrimaryAccountChangeEvent& event) override;
   void OnRefreshTokenUpdatedForAccount(
       const CoreAccountInfo& account_info) override;
 
diff --git a/components/signin/public/identity_manager/test_identity_manager_observer.cc b/components/signin/public/identity_manager/test_identity_manager_observer.cc
index afa01cb..6fcdd672 100644
--- a/components/signin/public/identity_manager/test_identity_manager_observer.cc
+++ b/components/signin/public/identity_manager/test_identity_manager_observer.cc
@@ -133,25 +133,38 @@
 }
 
 // IdentityManager::Observer:
-void TestIdentityManagerObserver::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  primary_account_from_set_callback_ = primary_account_info;
-  if (on_primary_account_set_callback_)
-    std::move(on_primary_account_set_callback_).Run();
-}
-
-void TestIdentityManagerObserver::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
-  primary_account_from_cleared_callback_ = previous_primary_account_info;
-  if (on_primary_account_cleared_callback_)
-    std::move(on_primary_account_cleared_callback_).Run();
-}
-
-void TestIdentityManagerObserver::OnUnconsentedPrimaryAccountChanged(
-    const CoreAccountInfo& unconsented_primary_account_info) {
-  unconsented_primary_account_from_callback_ = unconsented_primary_account_info;
-  if (on_unconsented_primary_account_callback_)
-    std::move(on_unconsented_primary_account_callback_).Run();
+void TestIdentityManagerObserver::OnPrimaryAccountChanged(
+    const PrimaryAccountChangeEvent& event) {
+  // TODO(https://crbug.com/1158855): Refactor this test observer to
+  // have a single on_primary_account_changed_callback_  and a single
+  // on_primary_account_changed_event_.
+  switch (event.GetEventTypeFor(ConsentLevel::kNotRequired)) {
+    case PrimaryAccountChangeEvent::Type::kSet:
+    case PrimaryAccountChangeEvent::Type::kCleared:
+      unconsented_primary_account_from_callback_ =
+          event.GetCurrentState().primary_account;
+      if (on_unconsented_primary_account_callback_)
+        std::move(on_unconsented_primary_account_callback_).Run();
+      break;
+    case PrimaryAccountChangeEvent::Type::kNone:
+      break;
+  }
+  switch (event.GetEventTypeFor(ConsentLevel::kSync)) {
+    case PrimaryAccountChangeEvent::Type::kSet:
+      primary_account_from_set_callback_ =
+          event.GetCurrentState().primary_account;
+      if (on_primary_account_set_callback_)
+        std::move(on_primary_account_set_callback_).Run();
+      break;
+    case PrimaryAccountChangeEvent::Type::kCleared:
+      primary_account_from_cleared_callback_ =
+          event.GetPreviousState().primary_account;
+      if (on_primary_account_cleared_callback_)
+        std::move(on_primary_account_cleared_callback_).Run();
+      break;
+    case PrimaryAccountChangeEvent::Type::kNone:
+      break;
+  }
 }
 
 void TestIdentityManagerObserver::OnRefreshTokenUpdatedForAccount(
diff --git a/components/signin/public/identity_manager/test_identity_manager_observer.h b/components/signin/public/identity_manager/test_identity_manager_observer.h
index 506e246..91479fe 100644
--- a/components/signin/public/identity_manager/test_identity_manager_observer.h
+++ b/components/signin/public/identity_manager/test_identity_manager_observer.h
@@ -64,12 +64,8 @@
 
  private:
   // IdentityManager::Observer:
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
-  void OnUnconsentedPrimaryAccountChanged(
-      const CoreAccountInfo& unconsented_primary_account_info) override;
+  void OnPrimaryAccountChanged(
+      const PrimaryAccountChangeEvent& event_details) override;
   void OnRefreshTokenUpdatedForAccount(
       const CoreAccountInfo& account_info) override;
   void OnRefreshTokenRemovedForAccount(
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index c03cda90..4b5da2b 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -818,6 +818,8 @@
   VISIT(navigation_time_usec);
   VISIT(device_name);
   VISIT(target_device_sync_cache_guid);
+  VISIT(opened);
+  VISIT(notification_dismissed);
 }
 
 VISIT_PROTO_FIELDS(const sync_pb::SessionHeader& proto) {
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_client.cc b/components/sync/trusted_vault/standalone_trusted_vault_client.cc
index f49bb8c..98a8eda 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_client.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_client.cc
@@ -54,12 +54,8 @@
   ~PrimaryAccountObserver() override;
 
   // signin::IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
-  void OnUnconsentedPrimaryAccountChanged(
-      const CoreAccountInfo& unconsented_primary_account_info) override;
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override;
 
  private:
   void UpdatePrimaryAccountIfNeeded();
@@ -89,18 +85,8 @@
   identity_manager_->RemoveObserver(this);
 }
 
-void PrimaryAccountObserver::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  UpdatePrimaryAccountIfNeeded();
-}
-
-void PrimaryAccountObserver::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
-  UpdatePrimaryAccountIfNeeded();
-}
-
-void PrimaryAccountObserver::OnUnconsentedPrimaryAccountChanged(
-    const CoreAccountInfo& unconsented_primary_account_info) {
+void PrimaryAccountObserver::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event) {
   UpdatePrimaryAccountIfNeeded();
 }
 
diff --git a/components/sync/trusted_vault/trusted_vault_access_token_fetcher_frontend.cc b/components/sync/trusted_vault/trusted_vault_access_token_fetcher_frontend.cc
index 96f7f619..3a329d8ed 100644
--- a/components/sync/trusted_vault/trusted_vault_access_token_fetcher_frontend.cc
+++ b/components/sync/trusted_vault/trusted_vault_access_token_fetcher_frontend.cc
@@ -50,18 +50,8 @@
   }
 }
 
-void TrustedVaultAccessTokenFetcherFrontend::OnPrimaryAccountSet(
-    const CoreAccountInfo& primary_account_info) {
-  UpdatePrimaryAccountIfNeeded();
-}
-
-void TrustedVaultAccessTokenFetcherFrontend::OnPrimaryAccountCleared(
-    const CoreAccountInfo& previous_primary_account_info) {
-  UpdatePrimaryAccountIfNeeded();
-}
-
-void TrustedVaultAccessTokenFetcherFrontend::OnUnconsentedPrimaryAccountChanged(
-    const CoreAccountInfo& unconsented_primary_account_info) {
+void TrustedVaultAccessTokenFetcherFrontend::OnPrimaryAccountChanged(
+    const signin::PrimaryAccountChangeEvent& event) {
   UpdatePrimaryAccountIfNeeded();
 }
 
diff --git a/components/sync/trusted_vault/trusted_vault_access_token_fetcher_frontend.h b/components/sync/trusted_vault/trusted_vault_access_token_fetcher_frontend.h
index b0e0b25..7f882be7 100644
--- a/components/sync/trusted_vault/trusted_vault_access_token_fetcher_frontend.h
+++ b/components/sync/trusted_vault/trusted_vault_access_token_fetcher_frontend.h
@@ -46,12 +46,8 @@
                         TrustedVaultAccessTokenFetcher::TokenCallback callback);
 
   // signin::IdentityManager::Observer implementation.
-  void OnPrimaryAccountSet(
-      const CoreAccountInfo& primary_account_info) override;
-  void OnPrimaryAccountCleared(
-      const CoreAccountInfo& previous_primary_account_info) override;
-  void OnUnconsentedPrimaryAccountChanged(
-      const CoreAccountInfo& unconsented_primary_account_info) override;
+  void OnPrimaryAccountChanged(
+      const signin::PrimaryAccountChangeEvent& event) override;
 
  private:
   // Updates |primary_account_| and runs |pending_requests_| in case
diff --git a/components/tracing/common/trace_startup_config.cc b/components/tracing/common/trace_startup_config.cc
index 59c1e6e..e9d0e43 100644
--- a/components/tracing/common/trace_startup_config.cc
+++ b/components/tracing/common/trace_startup_config.cc
@@ -90,11 +90,11 @@
     DCHECK(IsEnabled());
     DCHECK(!IsTracingStartupForDuration());
     DCHECK_EQ(SessionOwner::kBackgroundTracing, session_owner_);
-    CHECK(!ShouldTraceToResultFile());
+    CHECK(GetResultFile().empty());
   } else if (EnableFromATrace()) {
     DCHECK(IsEnabled());
     DCHECK_EQ(SessionOwner::kSystemTracing, session_owner_);
-    CHECK(!ShouldTraceToResultFile());
+    CHECK(GetResultFile().empty());
   }
 }
 
@@ -128,20 +128,11 @@
   return output_format_;
 }
 
-bool TraceStartupConfig::ShouldTraceToResultFile() const {
-  return IsEnabled() && should_trace_to_result_file_;
-}
-
 base::FilePath TraceStartupConfig::GetResultFile() const {
   DCHECK(IsEnabled());
-  DCHECK(ShouldTraceToResultFile());
   return result_file_;
 }
 
-void TraceStartupConfig::OnTraceToResultFileFinished() {
-  finished_writing_to_file_ = true;
-}
-
 void TraceStartupConfig::SetBackgroundStartupTracingEnabled(bool enabled) {
 #if defined(OS_ANDROID)
   base::android::SetBackgroundStartupTracingFlag(enabled);
@@ -174,25 +165,41 @@
                     << "=" << startup_duration_str << " defaulting to 5 (secs)";
       startup_duration_in_seconds_ = kDefaultStartupDurationInSeconds;
     }
+  } else if (command_line->HasSwitch(switches::kEnableTracing)) {
+    // For --enable-tracing, tracing should last until browser shutdown.
+    startup_duration_in_seconds_ = 0;
   }
 
-  if (command_line->GetSwitchValueASCII(switches::kTraceStartupFormat) ==
-      "proto") {
-    // Default is "json".
+  if (command_line->HasSwitch(switches::kTraceStartupFormat)) {
+    if (command_line->GetSwitchValueASCII(switches::kTraceStartupFormat) ==
+        "proto") {
+      // Default is "json".
+      output_format_ = OutputFormat::kProto;
+    }
+  } else if (command_line->GetSwitchValueASCII(
+                 switches::kEnableTracingFormat) == "proto") {
     output_format_ = OutputFormat::kProto;
   }
 
-  if (!command_line->HasSwitch(switches::kTraceStartup))
+  if (!command_line->HasSwitch(switches::kTraceStartup) &&
+      !command_line->HasSwitch(switches::kEnableTracing)) {
     return false;
+  }
+
+  std::string categories;
+  if (command_line->HasSwitch(switches::kTraceStartup)) {
+    categories = command_line->GetSwitchValueASCII(switches::kTraceStartup);
+  } else {
+    categories = command_line->GetSwitchValueASCII(switches::kEnableTracing);
+  }
 
   trace_config_ = base::trace_event::TraceConfig(
-      command_line->GetSwitchValueASCII(switches::kTraceStartup),
+      categories,
       command_line->GetSwitchValueASCII(switches::kTraceStartupRecordMode));
 
   result_file_ = command_line->GetSwitchValuePath(switches::kTraceStartupFile);
 
   is_enabled_ = true;
-  should_trace_to_result_file_ = true;
   return true;
 }
 
@@ -227,7 +234,6 @@
 
   if (trace_config_file.empty()) {
     is_enabled_ = true;
-    should_trace_to_result_file_ = true;
     DLOG(WARNING) << "Use default trace config.";
     return true;
   }
@@ -247,7 +253,6 @@
   is_enabled_ = ParseTraceConfigFileContent(trace_config_file_content);
   if (!is_enabled_)
     DLOG(WARNING) << "Cannot parse the trace config file correctly.";
-  should_trace_to_result_file_ = is_enabled_;
   return is_enabled_;
 }
 
@@ -269,7 +274,6 @@
 
   is_enabled_ = true;
   session_owner_ = SessionOwner::kBackgroundTracing;
-  should_trace_to_result_file_ = false;
   // Set startup duration to 0 since background tracing config will configure
   // the durations later.
   startup_duration_in_seconds_ = 0;
diff --git a/components/tracing/common/trace_startup_config.h b/components/tracing/common/trace_startup_config.h
index 8f0c3e8c..74e6f31 100644
--- a/components/tracing/common/trace_startup_config.h
+++ b/components/tracing/common/trace_startup_config.h
@@ -126,21 +126,12 @@
   base::trace_event::TraceConfig GetTraceConfig() const;
   int GetStartupDuration() const;
 
-  // Returns true while startup tracing is not finished, if trace should be
-  // saved to result file.
-  bool ShouldTraceToResultFile() const;
+  // Returns the name of the file to write the trace result into.
   base::FilePath GetResultFile() const;
-  void OnTraceToResultFileFinished();
 
   // Set the background tracing config in preferences for the next session.
   void SetBackgroundStartupTracingEnabled(bool enabled);
 
-  // Returns when the startup tracing is finished and written to file, false on
-  // all other cases.
-  bool finished_writing_to_file_for_testing() const {
-    return finished_writing_to_file_;
-  }
-
   SessionOwner GetSessionOwner() const;
 
   OutputFormat GetOutputFormat() const;
@@ -173,9 +164,7 @@
   bool enable_background_tracing_for_testing_ = false;
   base::trace_event::TraceConfig trace_config_;
   int startup_duration_in_seconds_ = kDefaultStartupDurationInSeconds;
-  bool should_trace_to_result_file_ = false;
   base::FilePath result_file_;
-  bool finished_writing_to_file_ = false;
   SessionOwner session_owner_ = SessionOwner::kTracingController;
   bool session_adopted_ = false;
   OutputFormat output_format_ = OutputFormat::kLegacyJSON;
diff --git a/components/tracing/common/tracing_switches.cc b/components/tracing/common/tracing_switches.cc
index 1d545b1..6b2d8137 100644
--- a/components/tracing/common/tracing_switches.cc
+++ b/components/tracing/common/tracing_switches.cc
@@ -18,14 +18,25 @@
 // specify the specific trace categories to include (e.g.
 // --trace-startup=base,net) otherwise, all events are recorded. Setting this
 // flag results in the first call to BeginTracing() to receive all trace events
-// since startup. In Chrome, you may find --trace-startup-file and
+// since startup.
+//
+// Historically, --trace-startup was used for browser startup profiling and
+// --enable-tracing was used for browsertest tracing. Now they are share the
+// same implementation, but both are still supported to avoid disrupting
+// existing workflows. The only difference between them is the default duration
+// (5 seconds for trace-startup, unlimited for enable-tracing). If both are
+// specified, 'trace-startup' takes precedence.
+//
+// In Chrome, you may find --trace-startup-file and
 // --trace-startup-duration to control the auto-saving of the trace (not
 // supported in the base-only TraceLog component).
-const char kTraceStartup[]                  = "trace-startup";
+const char kTraceStartup[] = "trace-startup";
+const char kEnableTracing[] = "enable-tracing";
 
-// Sets the time in seconds until startup tracing ends. If omitted a default of
-// 5 seconds is used. Has no effect without --trace-startup, or if
-// --startup-trace-file=none was supplied.
+// Sets the time in seconds until startup tracing ends. If omitted:
+// - if --trace-startup is specified, a default of 5 seconds is used.
+// - if --enable-tracing is specified, tracing lasts until the browser is
+// closed. Has no effect otherwise.
 const char kTraceStartupDuration[]          = "trace-startup-duration";
 
 // If supplied, sets the file which startup tracing will be stored into, if
@@ -35,7 +46,16 @@
 // As a special case, can be set to 'none' - this disables automatically saving
 // the result to a file and the first manually recorded trace will then receive
 // all events since startup.
-const char kTraceStartupFile[]              = "trace-startup-file";
+const char kTraceStartupFile[] = "trace-startup-file";
+
+// Similar to the flag above, with the following differences:
+// - A more detailed basename will be generated.
+// - If the value is empty or ends with path separator, the provided directory
+// will be used (with empty standing for current directory) and a detailed
+// basename file will be generated.
+//
+// It is ignored if --trace-startup-file is specified.
+const char kEnableTracingOutput[] = "enable-tracing-output";
 
 // Sets the output format for the trace, valid values are "json" and "proto".
 // If not set, the current default is "json".
@@ -44,6 +64,7 @@
 // unexpectedly terminates.
 // Ignored if "trace-startup-owner" is not "controller".
 const char kTraceStartupFormat[] = "trace-startup-format";
+const char kEnableTracingFormat[] = "enable-tracing-format";
 
 // If supplied, sets the tracing record mode and options; otherwise, the default
 // "record-until-full" mode will be used.
diff --git a/components/tracing/common/tracing_switches.h b/components/tracing/common/tracing_switches.h
index 05ee0b9..d0ae956 100644
--- a/components/tracing/common/tracing_switches.h
+++ b/components/tracing/common/tracing_switches.h
@@ -12,10 +12,13 @@
 TRACING_EXPORT extern const char kEnableBackgroundTracing[];
 TRACING_EXPORT extern const char kTraceConfigFile[];
 TRACING_EXPORT extern const char kTraceStartup[];
+TRACING_EXPORT extern const char kEnableTracing[];
 TRACING_EXPORT extern const char kTraceStartupDuration[];
 TRACING_EXPORT extern const char kTraceStartupFile[];
+TRACING_EXPORT extern const char kEnableTracingOutput[];
 TRACING_EXPORT extern const char kTraceStartupRecordMode[];
 TRACING_EXPORT extern const char kTraceStartupFormat[];
+TRACING_EXPORT extern const char kEnableTracingFormat[];
 TRACING_EXPORT extern const char kTraceStartupOwner[];
 TRACING_EXPORT extern const char kTraceStartupEnablePrivacyFiltering[];
 TRACING_EXPORT extern const char kPerfettoDisableInterning[];
diff --git a/components/ukm/ukm_recorder_impl.cc b/components/ukm/ukm_recorder_impl.cc
index 0871b25e..aaa28a3 100644
--- a/components/ukm/ukm_recorder_impl.cc
+++ b/components/ukm/ukm_recorder_impl.cc
@@ -59,8 +59,9 @@
 // new ones being added.
 size_t GetMaxSources() {
   constexpr size_t kDefaultMaxSources = 500;
-  return static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt(
+  static auto value = static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt(
       kUkmFeature, "MaxSources", kDefaultMaxSources));
+  return value;
 }
 
 // Gets the maximum number of Sources we can keep in memory at the end of the
@@ -68,16 +69,18 @@
 // interval.
 size_t GetMaxKeptSources() {
   constexpr size_t kDefaultMaxKeptSources = 100;
-  return static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt(
+  static auto value = static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt(
       kUkmFeature, "MaxKeptSources", kDefaultMaxKeptSources));
+  return value;
 }
 
 // Gets the maximum number of Entries we'll keep in memory before discarding any
 // new ones being added.
 size_t GetMaxEntries() {
   constexpr size_t kDefaultMaxEntries = 5000;
-  return static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt(
+  static auto value = static_cast<size_t>(base::GetFieldTrialParamByFeatureAsInt(
       kUkmFeature, "MaxEntries", kDefaultMaxEntries));
+  return value;
 }
 
 // Returns whether |url| has one of the schemes supported for logging to UKM.
diff --git a/components/webapps/webapps_client.h b/components/webapps/webapps_client.h
index 9e850e1..a9b3f3f2 100644
--- a/components/webapps/webapps_client.h
+++ b/components/webapps/webapps_client.h
@@ -50,6 +50,9 @@
                                         const GURL& manifest_url) = 0;
 
   virtual bool CanShowAppBanners(content::WebContents* web_contents) = 0;
+
+  virtual void OnWebApkInstallInitiatedFromAppMenu(
+      content::WebContents* web_contents) = 0;
 #endif
 };
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 0f6a9b7..6fc3a0e 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2101,6 +2101,10 @@
     deps += [ "//ui/ozone:buildflags" ]
   }
 
+  if (is_chromeos) {
+    deps += [ "//ui/base/ime/chromeos:chromeos" ]
+  }
+
   if (is_linux || is_chromeos) {
     sources += [
       "child_process_launcher_helper_linux.cc",
diff --git a/content/browser/loader/cross_site_document_blocking_browsertest.cc b/content/browser/loader/cross_site_document_blocking_browsertest.cc
index a673e7d..fbc123c 100644
--- a/content/browser/loader/cross_site_document_blocking_browsertest.cc
+++ b/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -1632,4 +1632,110 @@
             LoadBasicRequest(partition->GetNetworkContext(), test_url));
 }
 
+// This test class sets up a link element for webbundle subresource loading.
+// e.g. <link rel=webbundle href=".../foo.wbn" resources="...">.
+class CrossSiteDocumentBlockingWebBundleTest
+    : public CrossSiteDocumentBlockingTestBase {
+ public:
+  CrossSiteDocumentBlockingWebBundleTest() {
+    scoped_feature_list_.InitAndEnableFeature(features::kSubresourceWebBundles);
+  }
+  ~CrossSiteDocumentBlockingWebBundleTest() override = default;
+
+  CrossSiteDocumentBlockingWebBundleTest(
+      const CrossSiteDocumentBlockingWebBundleTest&) = delete;
+  CrossSiteDocumentBlockingWebBundleTest& operator=(
+      const CrossSiteDocumentBlockingWebBundleTest&) = delete;
+
+ protected:
+  void SetupLinkWebBundleElementAndImgElement(const GURL& bundle_url,
+                                              const GURL subresource_url) {
+    // Navigate to the test page.
+    ASSERT_TRUE(NavigateToURL(shell(), GURL("http://foo.com/title1.html")));
+
+    const char kScriptTemplate[] = R"(
+      const link = document.createElement('link');
+      link.rel = 'webbundle';
+      link.href = $1;
+      link.resources.add($2);
+      document.body.appendChild(link);
+
+      const img = document.createElement('img');
+      img.src = $2;
+      document.body.appendChild(img);
+)";
+    // Insert a <link> element for webbundle subresoruce loading, and insert an
+    // <img> element which loads a resource from the webbundle.
+    ASSERT_TRUE(ExecJs(
+        shell(), JsReplace(kScriptTemplate, bundle_url, subresource_url)));
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// CrossSiteDocumentBlockingWebBundleTest has 4 tests; a cartesian product of
+// 1) cross-origin bundle, 2) same-origin bundle
+// X
+// A). CORB-protected MIME type (e.g. text/json), B) other type (e.g. image/png)
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
+                       CrossOriginWebBundleSubresoruceJson) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  GURL bundle_url("http://cross-origin.com/web_bundle/cross_origin.wbn");
+  GURL subresource_url("http://cross-origin.com/web_bundle/resource.json");
+  RequestInterceptor interceptor(subresource_url);
+  SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
+  interceptor.WaitForRequestCompletion();
+
+  EXPECT_EQ(0, interceptor.completion_status().error_code);
+  EXPECT_EQ("", interceptor.response_body())
+      << "JSON in a cross-origin webbundle should be blocked by CORB";
+}
+
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
+                       CrossOriginWebBundleSubresorucePng) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  GURL bundle_url("http://cross-origin.com/web_bundle/cross_origin.wbn");
+  GURL subresource_url("http://cross-origin.com/web_bundle/resource.png");
+  RequestInterceptor interceptor(subresource_url);
+  SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
+  interceptor.WaitForRequestCompletion();
+
+  EXPECT_EQ(0, interceptor.completion_status().error_code);
+  EXPECT_EQ("broken png", interceptor.response_body())
+      << "PNG in a cross-origin webbundle should not be blocked";
+}
+
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
+                       SameOriginWebBundleSubresoruceJson) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  GURL bundle_url("http://foo.com/web_bundle/same_origin.wbn");
+  GURL subresource_url("http://foo.com/web_bundle/resource.json");
+  RequestInterceptor interceptor(subresource_url);
+  SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
+  interceptor.WaitForRequestCompletion();
+
+  EXPECT_EQ(0, interceptor.completion_status().error_code);
+  EXPECT_EQ("{ secret: 1 }", interceptor.response_body())
+      << "JSON in a same-origin webbundle should not be blocked";
+}
+
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingWebBundleTest,
+                       SameOriginWebBundleSubresorucePng) {
+  embedded_test_server()->StartAcceptingConnections();
+
+  GURL bundle_url("http://foo.com/web_bundle/same_origin.wbn");
+  GURL subresource_url("http://foo.com/web_bundle/resource.png");
+  RequestInterceptor interceptor(subresource_url);
+  SetupLinkWebBundleElementAndImgElement(bundle_url, subresource_url);
+  interceptor.WaitForRequestCompletion();
+
+  EXPECT_EQ(0, interceptor.completion_status().error_code);
+  EXPECT_EQ("broken png", interceptor.response_body())
+      << "PNG in a same-origin webbundle should not be blocked";
+}
+
 }  // namespace content
diff --git a/content/browser/prerender/prerender_browsertest.cc b/content/browser/prerender/prerender_browsertest.cc
index fb3996a..2ae486f 100644
--- a/content/browser/prerender/prerender_browsertest.cc
+++ b/content/browser/prerender/prerender_browsertest.cc
@@ -386,6 +386,30 @@
   EXPECT_EQ(GetRequestCount(kPrerenderingUrl), 2);
 }
 
+// Tests that back-forward history is preserved after activation.
+IN_PROC_BROWSER_TEST_P(PrerenderBrowserTest, HistoryAfterActivation) {
+  // This test is only meaningful with activation.
+  if (IsActivationDisabled())
+    return;
+
+  const GURL kInitialUrl = GetUrl("/prerender/add_prerender.html");
+  const GURL kPrerenderingUrl = GetUrl("/empty.html");
+
+  // Navigate to an initial page.
+  ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl));
+
+  // Make and activate a prerendered page.
+  AddPrerender(kPrerenderingUrl);
+  NavigateWithLocation(kPrerenderingUrl);
+  EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), kPrerenderingUrl);
+
+  // Navigate back to the initial page.
+  content::TestNavigationObserver observer(shell()->web_contents());
+  shell()->GoBackOrForward(-1);
+  observer.Wait();
+  EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), kInitialUrl);
+}
+
 // TODO(https://crbug.com/1132746): Test canceling prerendering.
 
 // TODO(https://crbug.com/1132746): Test prerendering for 404 page, redirection,
diff --git a/content/browser/prerender/prerender_host.cc b/content/browser/prerender/prerender_host.cc
index aad841f..a4a58cb 100644
--- a/content/browser/prerender/prerender_host.cc
+++ b/content/browser/prerender/prerender_host.cc
@@ -82,6 +82,10 @@
   if (!current_web_contents)
     return false;
 
+  // Merge browsing history.
+  prerendered_contents_->GetController().CopyStateFromAndPrune(
+      &current_web_contents->GetController(), /*replace_entry=*/false);
+
   // Activate the prerendered contents.
   WebContentsDelegate* delegate = current_web_contents->GetDelegate();
   DCHECK(delegate);
diff --git a/content/browser/prerender/prerender_host.h b/content/browser/prerender/prerender_host.h
index 5e66730..8b320eed 100644
--- a/content/browser/prerender/prerender_host.h
+++ b/content/browser/prerender/prerender_host.h
@@ -47,7 +47,8 @@
 
   // Activates the prerendered contents. Returns false when activation didn't
   // occur for some reason. This must be called after this host gets ready for
-  // activation.
+  // activation. `current_render_frame_host` is the RenderFrameHost that will
+  // be swapped out and destroyed by the activation.
   bool ActivatePrerenderedContents(
       RenderFrameHostImpl& current_render_frame_host);
 
diff --git a/content/browser/prerender/prerender_host_unittest.cc b/content/browser/prerender/prerender_host_unittest.cc
index fbbaaa8..b21b7fb 100644
--- a/content/browser/prerender/prerender_host_unittest.cc
+++ b/content/browser/prerender/prerender_host_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/scoped_feature_list.h"
 #include "content/browser/site_instance_impl.h"
+#include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/test/test_render_frame_host.h"
 #include "content/test/test_render_view_host.h"
@@ -54,7 +55,7 @@
   std::unique_ptr<TestWebContentsDelegate> web_contents_delegate_;
 };
 
-TEST_F(PrerenderHostTest, StartPrerendering) {
+TEST_F(PrerenderHostTest, PrerenderAndActivate) {
   std::unique_ptr<TestWebContents> web_contents =
       CreateWebContents(GURL("https://example.com/"));
   RenderFrameHostImpl* initiator_rfh = web_contents->GetMainFrame();
@@ -67,17 +68,25 @@
       std::move(attributes), initiator_rfh->GetGlobalFrameRoutingId(),
       initiator_rfh->GetLastCommittedOrigin());
 
+  // Start the prerendering navigation.
   prerender_host->StartPrerendering();
-  // Prepare a fake response so as to commit the navigation.
-  TestRenderFrameHost* prerendering_rfh = static_cast<TestRenderFrameHost*>(
+
+  // Finish the prerendering navigation. Normally we could use
+  // EmbeddedTestServer to provide a response, but this test uses
+  // RenderViewHostImplTestHarness so the load goes through a
+  // TestNavigationURLLoader which we don't have access to in order
+  // to complete. Use NavigationSimulator to finish the navigation on the
+  // WebContents.
+  WebContents* prerender_contents = WebContents::FromRenderFrameHost(
       prerender_host->GetPrerenderedMainFrameHostForTesting());
-  ASSERT_TRUE(prerendering_rfh);
-  prerendering_rfh->PrepareForCommit();
+  ASSERT_TRUE(prerender_contents);
+  std::unique_ptr<NavigationSimulator> sim =
+      NavigationSimulator::CreateFromPending(prerender_contents);
+  sim->ReadyToCommit();
+  sim->Commit();
+  EXPECT_TRUE(prerender_host->is_ready_for_activation());
 
-  // Artificially finish navigation to make the prerender host ready to provide
-  // the prerendered contents.
-  prerender_host->DidFinishNavigation(nullptr);
-
+  // Activate.
   EXPECT_TRUE(prerender_host->ActivatePrerenderedContents(*initiator_rfh));
 }
 
diff --git a/content/browser/renderer_host/policy_container_host.cc b/content/browser/renderer_host/policy_container_host.cc
index 2c39717..0dbba08b 100644
--- a/content/browser/renderer_host/policy_container_host.cc
+++ b/content/browser/renderer_host/policy_container_host.cc
@@ -8,7 +8,7 @@
 
 PolicyContainerHost::PolicyContainerHost() = default;
 PolicyContainerHost::PolicyContainerHost(
-    PolicyContainerHost::DocumentPolicies document_policies)
+    const PolicyContainerHost::DocumentPolicies& document_policies)
     : document_policies_(document_policies) {}
 PolicyContainerHost::~PolicyContainerHost() = default;
 
diff --git a/content/browser/renderer_host/policy_container_host.h b/content/browser/renderer_host/policy_container_host.h
index 191ad644..36ec0238 100644
--- a/content/browser/renderer_host/policy_container_host.h
+++ b/content/browser/renderer_host/policy_container_host.h
@@ -41,7 +41,7 @@
   };
 
   PolicyContainerHost();
-  explicit PolicyContainerHost(DocumentPolicies document_policies);
+  explicit PolicyContainerHost(const DocumentPolicies& document_policies);
   PolicyContainerHost(const PolicyContainerHost&) = delete;
   PolicyContainerHost& operator=(const PolicyContainerHost&) = delete;
   ~PolicyContainerHost() override;
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 552ba42..1b7f87f 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -2477,6 +2477,12 @@
 }
 
 void RenderFrameHostImpl::RenderFrameCreated() {
+  // In https://crbug.com/1146573 a WebContentsObserver was causing the frame to
+  // be reinitialized during deletion. It is not valid to re-enter navigation
+  // code like that and it led to an invalid state. This is not a DCHECK because
+  // the corruption will not be visible until later, making the bug very
+  // difficult to understand.
+  CHECK_NE(render_frame_state_, RenderFrameState::kDeleting);
   // We should not create new RenderFrames while our delegate is being destroyed
   // (e.g., via a WebContentsObserver during WebContents shutdown).  This seems
   // to have caused crashes in https://crbug.com/717650.
@@ -2522,17 +2528,25 @@
 }
 
 void RenderFrameHostImpl::RenderFrameDeleted() {
+  // In https://crbug.com/1146573 a WebContentsObserver was causing the frame to
+  // be reinitialized during deletion. It is not valid to re-enter navigation
+  // code like that and it led to an invalid state. This is not a DCHECK because
+  // the corruption will cause a crash but later, making the bug very
+  // difficult to understand.
+  CHECK_NE(render_frame_state_, RenderFrameState::kDeleting);
   bool was_created = is_render_frame_created();
-  render_frame_state_ = RenderFrameState::kDeleted;
+  render_frame_state_ = RenderFrameState::kDeleting;
 
   // If the current status is different than the new status, the delegate
   // needs to be notified.
   if (was_created) {
     delegate_->RenderFrameDeleted(this);
   }
+
   if (web_ui_) {
     web_ui_->InvalidateMojoConnection();
   }
+  render_frame_state_ = RenderFrameState::kDeleted;
 }
 
 void RenderFrameHostImpl::SwapIn() {
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 49dad88..1221c604 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -2737,11 +2737,21 @@
     // state will be kCreated or kDeleted.
     kNeverCreated = 0,
     // A RenderFrame has been created in the renderer and is still in that
-    // state. The next state will be kDeleted.
+    // state. The next state will be kDeleting.
     kCreated,
-    // A RenderFrame has either been cleanly deleted or its renderer process has
-    // exited or crashed. The next state may be kCreated or the RenderFrameHost
-    // may be destroyed.
+    // A RenderFrame has either
+    // - been cleanly deleted
+    // - its renderer process has exited or crashed
+    // We will call observers of RenderFrameDeleted in this state and this
+    // allows us to CHECK if an observer causes us to attempt to change state
+    // during deletion. See https://crbug.com/1146573. The next state will
+    // be kDeleted and we will move to that before exiting RenderFrameDeleted.
+    kDeleting,
+    // A RenderFrame has either
+    // - been cleanly deleted
+    // - its renderer process has exited or crashed
+    // The next state may be kCreated if the RenderFrameHost is being reused
+    // after a crash or the RenderFrameHost may be destroyed.
     kDeleted,
   };
   RenderFrameState render_frame_state_ = RenderFrameState::kNeverCreated;
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 42f70e7..fdd49c9 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -125,6 +125,11 @@
 #include "ui/base/ime/virtual_keyboard_controller.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "ui/base/ime/chromeos/extension_ime_util.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#endif
+
 using gfx::RectToSkIRect;
 using gfx::SkIRectToRect;
 
@@ -1498,6 +1503,20 @@
     base::UmaHistogramEnumeration(
         "InputMethod.Assistive.Autocorrect.Count",
         TextInputClient::SubClass::kRenderWidgetHostViewAura);
+
+#if defined(OS_CHROMEOS)
+    auto* input_method_manager =
+        chromeos::input_method::InputMethodManager::Get();
+    if (input_method_manager &&
+        chromeos::extension_ime_util::IsExperimentalMultilingual(
+            input_method_manager->GetActiveIMEState()
+                ->GetCurrentInputMethod()
+                .id())) {
+      base::UmaHistogramEnumeration(
+          "InputMethod.MultilingualExperiment.Autocorrect.Count",
+          TextInputClient::SubClass::kRenderWidgetHostViewAura);
+    }
+#endif
   }
 
   auto* input_handler = GetFrameWidgetInputHandlerForFocusedWidget();
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index a7281479..fe24c5ce 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -813,23 +813,6 @@
   }
 };
 
-// SitePerProcessEmbedderCSPEnforcementBrowserTest
-
-class SitePerProcessEmbedderCSPEnforcementBrowserTest
-    : public SitePerProcessBrowserTest {
- public:
-  SitePerProcessEmbedderCSPEnforcementBrowserTest() {}
-
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
-    // TODO(amalika): Remove this switch when the EmbedderCSPEnforcement becomes
-    // stable
-    command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
-                                    "EmbedderCSPEnforcement");
-  }
-};
-
 // SitePerProcessProgrammaticScrollTest.
 
 class SitePerProcessProgrammaticScrollTest : public SitePerProcessBrowserTest {
@@ -4173,7 +4156,7 @@
 
 // Verify that "csp" property on frame elements propagates to child frames
 // correctly. See  https://crbug.com/647588
-IN_PROC_BROWSER_TEST_P(SitePerProcessEmbedderCSPEnforcementBrowserTest,
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                        FrameOwnerPropertiesPropagationCSP) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/frame_owner_properties_csp.html"));
@@ -16678,9 +16661,6 @@
                          SitePerProcessBrowserTouchActionTest,
                          testing::ValuesIn(RenderDocumentFeatureLevelValues()));
 INSTANTIATE_TEST_SUITE_P(All,
-                         SitePerProcessEmbedderCSPEnforcementBrowserTest,
-                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
-INSTANTIATE_TEST_SUITE_P(All,
                          SitePerProcessHighDPIBrowserTest,
                          testing::ValuesIn(RenderDocumentFeatureLevelValues()));
 INSTANTIATE_TEST_SUITE_P(All,
diff --git a/content/browser/tracing/startup_tracing_browsertest.cc b/content/browser/tracing/startup_tracing_browsertest.cc
index eac323c2..bc1e63e 100644
--- a/content/browser/tracing/startup_tracing_browsertest.cc
+++ b/content/browser/tracing/startup_tracing_browsertest.cc
@@ -5,13 +5,16 @@
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
 #include "base/run_loop.h"
+#include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_run_loop_timeout.h"
 #include "base/test/test_timeouts.h"
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "components/tracing/common/trace_startup_config.h"
 #include "components/tracing/common/tracing_switches.h"
+#include "content/browser/tracing/startup_tracing_controller.h"
 #include "content/browser/tracing/tracing_controller_impl.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/content_browser_test.h"
@@ -42,52 +45,6 @@
 
 }  // namespace
 
-class CommandlineStartupTracingTest : public ContentBrowserTest {
- public:
-  CommandlineStartupTracingTest() = default;
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    base::CreateTemporaryFile(&temp_file_path_);
-    command_line->AppendSwitch(switches::kTraceStartup);
-    command_line->AppendSwitchASCII(switches::kTraceStartupDuration, "3");
-    command_line->AppendSwitchASCII(switches::kTraceStartupFile,
-                                    temp_file_path_.AsUTF8Unsafe());
-  }
-
- protected:
-  base::FilePath temp_file_path_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(CommandlineStartupTracingTest);
-};
-
-// Failing on Android/Win ASAN, Linux TSAN. crbug.com/1041392
-#if (defined(OS_ANDROID) && defined(ADDRESS_SANITIZER)) || \
-    (defined(OS_WIN) && defined(ADDRESS_SANITIZER)) ||     \
-    ((defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(THREAD_SANITIZER))
-#define MAYBE_TestStartupTracing DISABLED_TestStartupTracing
-#else
-#define MAYBE_TestStartupTracing TestStartupTracing
-#endif
-IN_PROC_BROWSER_TEST_F(CommandlineStartupTracingTest,
-                       MAYBE_TestStartupTracing) {
-  EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
-  WaitForCondition(base::BindRepeating([]() {
-                     return tracing::TraceStartupConfig::GetInstance()
-                         ->finished_writing_to_file_for_testing();
-                   }),
-                   "finish file write");
-
-  std::string trace;
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  ASSERT_TRUE(base::ReadFileToString(temp_file_path_, &trace));
-  EXPECT_TRUE(base::JSONReader::Read(trace));
-  EXPECT_TRUE(trace.find("StartupTracingController::Start") !=
-              std::string::npos);
-}
-
-#undef MAYBE_TestStartupTracing
-
 class StartupTracingInProcessTest : public ContentBrowserTest {
  public:
   StartupTracingInProcessTest() {
@@ -154,4 +111,263 @@
   wait_for_stop.Run();
 }
 
+namespace {
+
+enum class FinishType {
+  kWaitForTimeout,
+  kStopExplicitly,
+};
+
+std::ostream& operator<<(std::ostream& o, FinishType type) {
+  switch (type) {
+    case FinishType::kStopExplicitly:
+      o << "Stop";
+      return o;
+    case FinishType::kWaitForTimeout:
+      o << "Wait";
+      return o;
+  }
+}
+
+enum class OutputType {
+  kProto,
+  kJSON,
+};
+
+std::ostream& operator<<(std::ostream& o, OutputType type) {
+  switch (type) {
+    case OutputType::kJSON:
+      o << "json";
+      return o;
+    case OutputType::kProto:
+      o << "proto";
+      return o;
+  }
+}
+
+enum class OutputLocation {
+  // Write trace to a given file.
+  kGivenFile,
+  // Write trace into a given directory (basename will be set to trace1 before
+  // starting).
+  kDirectoryWithDefaultBasename,
+  // Write trace into a given directory (basename will be set to trace1 before
+  // starting, and updated to trace2 before calling Stop()).
+  kDirectoryWithBasenameUpdatedBeforeStop,
+};
+
+std::ostream& operator<<(std::ostream& o, OutputLocation type) {
+  switch (type) {
+    case OutputLocation::kGivenFile:
+      o << "file";
+      return o;
+    case OutputLocation::kDirectoryWithDefaultBasename:
+      o << "dir/trace1";
+      return o;
+    case OutputLocation::kDirectoryWithBasenameUpdatedBeforeStop:
+      o << "dir/trace2";
+      return o;
+  }
+}
+
+}  // namespace
+
+class StartupTracingTest
+    : public ContentBrowserTest,
+      public testing::WithParamInterface<
+          std::tuple<FinishType, OutputType, OutputLocation>> {
+ public:
+  StartupTracingTest() = default;
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitch(switches::kTraceStartup);
+    if (GetFinishType() == FinishType::kWaitForTimeout) {
+      command_line->AppendSwitchASCII(switches::kTraceStartupDuration, "3");
+    } else {
+      command_line->AppendSwitchASCII(switches::kTraceStartupDuration, "0");
+    }
+    command_line->AppendSwitchASCII(switches::kTraceStartupFormat,
+                                    GetOutputTypeAsString());
+
+    if (GetOutputLocation() == OutputLocation::kGivenFile) {
+      base::CreateTemporaryFile(&temp_file_path_);
+    } else {
+      base::CreateNewTempDirectory(base::FilePath::StringType(),
+                                   &temp_file_path_);
+      temp_file_path_ = temp_file_path_.AsEndingWithSeparator();
+    }
+
+    command_line->AppendSwitchASCII(switches::kEnableTracingOutput,
+                                    temp_file_path_.AsUTF8Unsafe());
+
+    if (GetOutputLocation() != OutputLocation::kGivenFile) {
+      // --enable-tracing-format switch should be initialised before
+      // calling SetDefaultBasenameForTest, which forces the creation of
+      // TraceStartupConfig, which queries the command line flags and
+      // stores the snapshot.
+      StartupTracingController::GetInstance().SetDefaultBasenameForTest(
+          "trace1",
+          StartupTracingController::ExtensionType::kAppendAppropriate);
+    }
+  }
+
+  FinishType GetFinishType() { return std::get<0>(GetParam()); }
+
+  OutputType GetOutputType() { return std::get<1>(GetParam()); }
+
+  std::string GetOutputTypeAsString() {
+    switch (GetOutputType()) {
+      case OutputType::kJSON:
+        return "json";
+      case OutputType::kProto:
+        return "proto";
+    }
+  }
+
+  OutputLocation GetOutputLocation() { return std::get<2>(GetParam()); }
+
+  base::FilePath GetExpectedPath() {
+    std::string filename;
+
+    switch (GetOutputLocation()) {
+      case OutputLocation::kGivenFile:
+        return temp_file_path_;
+      case OutputLocation::kDirectoryWithDefaultBasename:
+        filename = "trace1";
+        break;
+      case OutputLocation::kDirectoryWithBasenameUpdatedBeforeStop:
+        filename = "trace2";
+        break;
+    }
+
+    // Renames are not supported together with timeouts.
+    if (GetFinishType() == FinishType::kWaitForTimeout)
+      filename = "trace1";
+
+    return temp_file_path_.AppendASCII(filename + "." +
+                                       GetOutputTypeAsString());
+  }
+
+  static void CheckOutput(base::FilePath path, OutputType output_type) {
+    std::string trace;
+    base::ScopedAllowBlockingForTesting allow_blocking;
+    ASSERT_TRUE(base::ReadFileToString(path, &trace))
+        << "Failed to read file " << path;
+
+    if (output_type == OutputType::kJSON) {
+      EXPECT_TRUE(base::JSONReader::Read(trace));
+    }
+
+    // Both proto and json should have the trace event name recorded somewhere
+    // as a substring.
+    EXPECT_TRUE(trace.find("StartupTracingController::Start") !=
+                std::string::npos);
+  }
+
+  void Wait() {
+    if (GetFinishType() == FinishType::kWaitForTimeout) {
+      WaitForCondition(base::BindRepeating([]() {
+                         return StartupTracingController::GetInstance()
+                             .is_finished_for_testing();
+                       }),
+                       "finish file write");
+    } else {
+      StartupTracingController::GetInstance().WaitUntilStopped();
+    }
+  }
+
+ protected:
+  base::FilePath temp_file_path_;
+
+ private:
+  base::test::ScopedRunLoopTimeout increased_timeout_{
+      FROM_HERE, TestTimeouts::test_launcher_timeout()};
+
+  DISALLOW_COPY_AND_ASSIGN(StartupTracingTest);
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    StartupTracingTest,
+    testing::Combine(
+        testing::Values(FinishType::kStopExplicitly,
+                        FinishType::kWaitForTimeout),
+        testing::Values(OutputType::kJSON, OutputType::kProto),
+        testing::Values(
+            OutputLocation::kGivenFile,
+            OutputLocation::kDirectoryWithDefaultBasename,
+            OutputLocation::kDirectoryWithBasenameUpdatedBeforeStop)));
+
+IN_PROC_BROWSER_TEST_P(StartupTracingTest, TestEnableTracing) {
+  EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
+
+  if (GetOutputLocation() ==
+      OutputLocation::kDirectoryWithBasenameUpdatedBeforeStop) {
+    StartupTracingController::GetInstance().SetDefaultBasenameForTest(
+        "trace2", StartupTracingController::ExtensionType::kAppendAppropriate);
+  }
+
+  Wait();
+
+  CheckOutput(GetExpectedPath(), GetOutputType());
+}
+
+class EmergencyStopTracingTest : public StartupTracingTest {};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    EmergencyStopTracingTest,
+    testing::Combine(
+        testing::Values(FinishType::kStopExplicitly),
+        testing::Values(OutputType::kJSON, OutputType::kProto),
+        testing::Values(OutputLocation::kDirectoryWithDefaultBasename)));
+
+IN_PROC_BROWSER_TEST_P(EmergencyStopTracingTest, StopOnUIThread) {
+  EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
+
+  StartupTracingController::EmergencyStop();
+  CheckOutput(GetExpectedPath(), GetOutputType());
+}
+
+IN_PROC_BROWSER_TEST_P(EmergencyStopTracingTest, StopOnThreadPool) {
+  EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
+
+  auto expected_path = GetExpectedPath();
+  auto output_type = GetOutputType();
+
+  base::RunLoop run_loop;
+
+  base::ThreadPool::PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
+                               StartupTracingController::EmergencyStop();
+                               CheckOutput(expected_path, output_type);
+                               run_loop.Quit();
+                             }));
+
+  run_loop.Run();
+}
+
+IN_PROC_BROWSER_TEST_P(EmergencyStopTracingTest, StopOnThreadPoolTwice) {
+  EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
+
+  auto expected_path = GetExpectedPath();
+  auto output_type = GetOutputType();
+
+  base::RunLoop run_loop1;
+  base::RunLoop run_loop2;
+
+  base::ThreadPool::PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
+                               StartupTracingController::EmergencyStop();
+                               CheckOutput(expected_path, output_type);
+                               run_loop1.Quit();
+                             }));
+  base::ThreadPool::PostTask(FROM_HERE, base::BindLambdaForTesting([&]() {
+                               StartupTracingController::EmergencyStop();
+                               CheckOutput(expected_path, output_type);
+                               run_loop2.Quit();
+                             }));
+
+  run_loop1.Run();
+  run_loop2.Run();
+}
+
 }  // namespace content
diff --git a/content/browser/tracing/startup_tracing_controller.cc b/content/browser/tracing/startup_tracing_controller.cc
index f745fd2..a9adf2e1 100644
--- a/content/browser/tracing/startup_tracing_controller.cc
+++ b/content/browser/tracing/startup_tracing_controller.cc
@@ -3,14 +3,19 @@
 // found in the LICENSE file.
 
 #include "content/browser/tracing/startup_tracing_controller.h"
+#include "base/bind_post_task.h"
 #include "base/command_line.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
 #include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
+#include "base/thread_annotations.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
 #include "components/tracing/common/trace_startup_config.h"
@@ -29,17 +34,87 @@
 
 namespace content {
 
+// A helper class responsible for coordinating emergency trace finalisation
+// (e.g. when the process is about to be killed), which can be initiated from
+// any thread.
+class EmergencyTraceFinalisationCoordinator {
+ public:
+  static EmergencyTraceFinalisationCoordinator& GetInstance() {
+    static base::NoDestructor<EmergencyTraceFinalisationCoordinator> g_instance;
+    return *g_instance;
+  }
+
+  void OnTracingStarted(scoped_refptr<base::SequencedTaskRunner> task_runner,
+                        base::OnceClosure stop_tracing) {
+    tracing_started_.Set();
+    base::AutoLock lock(lock_);
+    startup_tracing_controller_task_runner_ = std::move(task_runner);
+    stop_tracing_ = std::move(stop_tracing);
+  }
+
+  void OnTracingStopped() { finalisation_.Signal(); }
+
+  // May be called multiple times per session, e.g. if a second thread
+  // encounters a crash after the first.
+  void StopAndBlockUntilStopped() {
+    // If DCHECK fires before tracing has started, there isn't much for us to
+    // do.
+    if (!tracing_started_.IsSet())
+      return;
+
+    base::trace_event::TraceLog::GetInstance()
+        ->SetCurrentThreadBlocksMessageLoop();
+
+    base::OnceClosure stop_tracing;
+    scoped_refptr<base::SequencedTaskRunner> task_runner;
+    {
+      base::AutoLock lock(lock_);
+      task_runner = startup_tracing_controller_task_runner_;
+      stop_tracing = std::move(stop_tracing_);
+    }
+
+    if (task_runner->RunsTasksInCurrentSequence()) {
+      VLOG(0) << "Ignored an emergency tracing stop request from the "
+                 "StartupTracingController sequence";
+      return;
+    }
+
+    if (stop_tracing)
+      task_runner->PostTask(FROM_HERE, std::move(stop_tracing));
+
+    base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
+
+    // Wait for the tracing to be finished before processing.
+    // Note that we should wait even if |stop_tracing| is null — if a second
+    // thread hits DCHECK while the first has posted a task and waits for the
+    // trace to be written, the second one should wait as well to avoid crashing
+    // the process.
+    finalisation_.Wait();
+  }
+
+ private:
+  base::WaitableEvent finalisation_;
+  base::AtomicFlag tracing_started_;
+
+  base::Lock lock_;
+  scoped_refptr<base::SequencedTaskRunner>
+      startup_tracing_controller_task_runner_ GUARDED_BY(lock_);
+  base::OnceClosure stop_tracing_ GUARDED_BY(lock_);
+};
+
 class StartupTracingController::BackgroundTracer {
  public:
   enum class WriteMode { kAfterStopping, kStreaming };
 
   BackgroundTracer(WriteMode write_mode,
+                   TempFilePolicy temp_file_policy,
                    base::FilePath output_file,
                    tracing::TraceStartupConfig::OutputFormat output_format,
                    perfetto::TraceConfig trace_config,
                    base::OnceClosure on_tracing_finished)
       : state_(State::kTracing),
         write_mode_(write_mode),
+        temp_file_policy_(temp_file_policy),
         task_runner_(base::SequencedTaskRunnerHandle::Get()),
         output_file_(output_file),
         output_format_(output_format),
@@ -57,17 +132,33 @@
       tracing_session_->Setup(trace_config);
     }
 
-    tracing_session_->StartBlocking();
+    // |StartBlocking| can take a non-trivial amount of time, so
+    // EmergencyTraceFinalisationController should be set up before it to catch
+    // DCHECKs early.
+    EmergencyTraceFinalisationCoordinator::GetInstance().OnTracingStarted(
+        task_runner_,
+        base::BindOnce(&BackgroundTracer::Stop, weak_ptr_factory_.GetWeakPtr(),
+                       base::nullopt));
+
     tracing_session_->SetOnStopCallback([&]() { OnTracingStopped(); });
+    tracing_session_->StartBlocking();
 
     TRACE_EVENT("startup", "StartupTracingController::Start");
   }
 
-  void Stop() {
+  void Stop(base::Optional<base::FilePath> output_file) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    if (state_ != State::kTracing)
-      return;
 
+    // Tracing might have already been finished due to a timeout.
+    if (state_ != State::kTracing) {
+      // Note: updating output files is not supported together with
+      // timeout-based tracing.
+      return;
+    }
+    state_ = State::kStopping;
+
+    if (output_file)
+      output_file_ = output_file.value();
     tracing_session_->StopBlocking();
   }
 
@@ -82,11 +173,13 @@
     }
 
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK_EQ(state_, State::kTracing);
+    // State will be kStopping if Stop() is called and kTracing if tracing
+    // finishes due to a timeout.
+    DCHECK(state_ == State::kStopping || state_ == State::kTracing);
     if (write_mode_ == WriteMode::kStreaming) {
       // No need to explicitly call ReadTrace as Perfetto has already written
       // the file.
-      Finalise(output_file_);
+      Finalise();
       return;
     }
     state_ = State::kWritingToFile;
@@ -102,7 +195,7 @@
           if (args.has_more)
             return;
 
-          Finalise(output_file_);
+          Finalise();
         });
   }
 
@@ -138,15 +231,15 @@
   // In order to atomically commit the trace file, create a temporary file first
   // which then will be subsequently renamed.
   void OpenFile(const base::FilePath& path) {
-    file_ = base::CreateAndOpenTemporaryFileInDir(path.DirName(),
-                                                  &written_to_file_);
-    if (file_.IsValid()) {
-      LOG(ERROR) << "Created valid file";
-      return;
-    }
+    if (temp_file_policy_ == TempFilePolicy::kUseTemporaryFile) {
+      file_ = base::CreateAndOpenTemporaryFileInDir(path.DirName(),
+                                                    &written_to_file_);
+      if (file_.IsValid())
+        return;
 
-    VLOG(1) << "Failed to create temporary file, using file '" << path
-            << "' directly instead";
+      VLOG(1) << "Failed to create temporary file, using file '" << path
+              << "' directly instead";
+    }
 
     // On Android, it might not be possible to create a temporary file.
     // In that case, we should use the file directly.
@@ -159,38 +252,48 @@
   }
 
   // Close the file and rename if needed.
-  void Finalise(const base::FilePath& path) {
+  void Finalise() {
     DCHECK_NE(state_, State::kFinished);
     file_.Close();
 
-    if (written_to_file_ != path) {
+    if (written_to_file_ != output_file_) {
       base::File::Error error;
       if (!base::ReplaceFile(written_to_file_, output_file_, &error)) {
         LOG(ERROR) << "Cannot move file '" << written_to_file_ << "' to '"
                    << output_file_
                    << "' : " << base::File::ErrorToString(error);
+      } else {
+        written_to_file_ = output_file_;
       }
     }
 
+    VLOG(0) << "Completed startup tracing to " << written_to_file_;
+    EmergencyTraceFinalisationCoordinator::GetInstance().OnTracingStopped();
+
     state_ = State::kFinished;
     std::move(on_tracing_finished_).Run();
-
-    VLOG(0) << "Completed startup tracing to " << path;
   }
 
   enum class State {
     kTracing,
+    kStopping,
     kWritingToFile,
     kFinished,
   };
   State state_;
 
   const WriteMode write_mode_;
+  const TempFilePolicy temp_file_policy_;
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
 
+  // Output file might be customised during the execution (e.g. test result
+  // becomes available), which means that if Perfetto has already started
+  // streaming the trace, the trace file should be renamed after trace
+  // completes.
   base::FilePath output_file_;
   base::FilePath written_to_file_;
+
   base::File file_;
 
   const tracing::TraceStartupConfig::OutputFormat output_format_;
@@ -200,27 +303,32 @@
   std::unique_ptr<tracing::TracePacketTokenizer> trace_packet_tokenizer_;
 
   base::OnceClosure on_tracing_finished_;
+
   std::unique_ptr<perfetto::TracingSession> tracing_session_;
 
   SEQUENCE_CHECKER(sequence_checker_);
+
+  base::WeakPtrFactory<BackgroundTracer> weak_ptr_factory_{this};
 };
 
+// static
+StartupTracingController& StartupTracingController::GetInstance() {
+  // Note: no DCHECK_CURRENTLY_ON, as it can be called prior to initialisation
+  // of BrowserThreads.
+
+  static base::NoDestructor<StartupTracingController> g_instance;
+  return *g_instance;
+}
+
 namespace {
 
-base::FilePath GetStartupTraceFileName() {
-  base::FilePath trace_file;
-
-  trace_file = tracing::TraceStartupConfig::GetInstance()->GetResultFile();
-  if (trace_file.empty()) {
+base::FilePath BasenameToPath(std::string basename) {
 #if defined(OS_ANDROID)
-    trace_file = TracingControllerAndroid::GenerateTracingFilePath("");
+  return TracingControllerAndroid::GenerateTracingFilePath(basename);
 #else
-    // Default to saving the startup trace into the current dir.
-    trace_file = base::FilePath().AppendASCII("chrometrace.log");
+  // Default to saving the startup trace into the current dir.
+  return base::FilePath().AppendASCII(basename);
 #endif
-  }
-
-  return trace_file;
 }
 
 }  // namespace
@@ -228,12 +336,46 @@
 StartupTracingController::StartupTracingController() = default;
 StartupTracingController::~StartupTracingController() = default;
 
-// static
-StartupTracingController& StartupTracingController::GetInstance() {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+base::FilePath StartupTracingController::GetOutputPath() {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
 
-  static base::NoDestructor<StartupTracingController> g_instance;
-  return *g_instance;
+  base::FilePath path_from_config =
+      tracing::TraceStartupConfig::GetInstance()->GetResultFile();
+  if (!path_from_config.empty())
+    return path_from_config;
+
+  // If --trace-startup-file is specified, use it.
+  if (command_line->HasSwitch(switches::kTraceStartupFile)) {
+    base::FilePath result =
+        command_line->GetSwitchValuePath(switches::kTraceStartupFile);
+    if (result.empty())
+      return BasenameToPath("chrometrace.log");
+    return result;
+  }
+
+  base::FilePath result =
+      command_line->GetSwitchValuePath(switches::kEnableTracingOutput);
+  if (result.empty() && command_line->HasSwitch(switches::kTraceStartup)) {
+    // If --trace-startup is present, return chrometrace.log for backwards
+    // compatibility.
+    return BasenameToPath("chrometrace.log");
+  }
+
+  // If a non-directory path is specified, use it.
+  if (!result.empty() && !result.EndsWithSeparator())
+    return result;
+
+  std::string basename = default_basename_;
+  if (basename.empty())
+    basename = "chrometrace.log";
+
+  // If a non-empty directory is specified, use it.
+  if (!result.empty())
+    return result.AppendASCII(basename);
+
+  // If the directory is empty, go through BasenameToPath to generate a valid
+  // path on Android.
+  return BasenameToPath(basename);
 }
 
 void StartupTracingController::StartIfNeeded() {
@@ -283,12 +425,9 @@
       tracing::TraceStartupConfig::GetInstance()->GetStartupDuration();
   perfetto_config.set_duration_ms(duration_in_seconds * 1000);
 
-  if (output_file_.empty())
-    output_file_ = GetStartupTraceFileName();
-
   background_tracer_ = base::SequenceBound<BackgroundTracer>(
-      std::move(background_task_runner), write_mode, output_file_,
-      output_format, perfetto_config,
+      std::move(background_task_runner), write_mode, temp_file_policy_,
+      GetOutputPath(), output_format, perfetto_config,
       base::BindOnce(
           [](StartupTracingController* controller) {
             GetUIThreadTaskRunner({})->PostTask(
@@ -302,7 +441,8 @@
 void StartupTracingController::Stop(base::OnceClosure on_tracing_finished) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  if (state_ == State::kNotRunning) {
+  if (state_ != State::kRunning) {
+    // Both kStopped and kNotRunning are valid states.
     std::move(on_tracing_finished).Run();
     return;
   }
@@ -310,22 +450,58 @@
   DCHECK(!on_tracing_finished_) << "Stop() should be called only once.";
   on_tracing_finished_ = std::move(on_tracing_finished);
 
-  background_tracer_.AsyncCall(&BackgroundTracer::Stop);
+  background_tracer_.AsyncCall(&BackgroundTracer::Stop)
+      .WithArgs(GetOutputPath());
 }
 
 void StartupTracingController::OnStoppedOnUIThread() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK_EQ(state_, State::kRunning);
-  state_ = State::kNotRunning;
+  state_ = State::kStopped;
   background_tracer_.Reset();
 
   if (on_tracing_finished_)
     std::move(on_tracing_finished_).Run();
 
-  tracing::TraceStartupConfig::GetInstance()->OnTraceToResultFileFinished();
   tracing::TraceStartupConfig::GetInstance()->SetDisabled();
 }
 
+void StartupTracingController::SetUsingTemporaryFile(
+    StartupTracingController::TempFilePolicy temp_file_policy) {
+  DCHECK_EQ(state_, State::kNotEnabled) << "Should be called before Start()";
+  temp_file_policy_ = temp_file_policy;
+}
+
+void StartupTracingController::SetDefaultBasename(
+    std::string basename,
+    ExtensionType extension_type) {
+  if (!tracing::TraceStartupConfig::GetInstance()->IsEnabled())
+    return;
+
+  if (basename_for_test_set_)
+    return;
+
+  if (extension_type == ExtensionType::kAppendAppropriate) {
+    switch (tracing::TraceStartupConfig::GetInstance()->GetOutputFormat()) {
+      case tracing::TraceStartupConfig::OutputFormat::kLegacyJSON:
+        basename += ".json";
+        break;
+      case tracing::TraceStartupConfig::OutputFormat::kProto:
+        basename += ".proto";
+        break;
+    }
+  }
+  default_basename_ = basename;
+}
+
+void StartupTracingController::SetDefaultBasenameForTest(
+    std::string basename,
+    ExtensionType extension_type) {
+  basename_for_test_set_ = false;
+  SetDefaultBasename(basename, extension_type);
+  basename_for_test_set_ = true;
+}
+
 void StartupTracingController::WaitUntilStopped() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
@@ -334,4 +510,16 @@
   run_loop.Run();
 }
 
+// static
+void StartupTracingController::EmergencyStop() {
+  if (GetIOThreadTaskRunner({})->RunsTasksInCurrentSequence()) {
+    VLOG(0) << "Emergency tracing stop request from IO thread is ignored - not "
+               "possible to finalise trace without running tasks on IO thread";
+    return;
+  }
+
+  EmergencyTraceFinalisationCoordinator::GetInstance()
+      .StopAndBlockUntilStopped();
+}
+
 }  // namespace content
diff --git a/content/browser/tracing/startup_tracing_controller.h b/content/browser/tracing/startup_tracing_controller.h
index 744a14b..d7d69f6 100644
--- a/content/browser/tracing/startup_tracing_controller.h
+++ b/content/browser/tracing/startup_tracing_controller.h
@@ -6,32 +6,80 @@
 #define CONTENT_BROWSER_TRACING_STARTUP_TRACING_CONTROLLER_H_
 
 #include "base/threading/sequence_bound.h"
+#include "content/common/content_export.h"
 
 namespace content {
 
 // Class responsible for starting and stopping startup tracing as configured by
 // StartupTracingConfig. All interactions with it are limited to UI thread, but
 // the actual logic lives on a background ThreadPool sequence.
-class StartupTracingController {
+class CONTENT_EXPORT StartupTracingController {
  public:
   StartupTracingController();
   ~StartupTracingController();
 
   static StartupTracingController& GetInstance();
 
+  // Stop the trace recording, write the trace to disk and block until complete.
+  // Intended to be used in the situation when the browser process is going to
+  // crash (e.g. DCHECK failure) and we want to avoid losing the trace data. Can
+  // be called from any thread.
+  // May not succeed if called from a sequence that is required to be responsive
+  // during trace finalisation.
+  static void EmergencyStop();
+
   void StartIfNeeded();
   void WaitUntilStopped();
 
+  // By default, a trace is written into a temporary file which then is renamed,
+  // however this can lead to data loss when the browser process crashes.
+  // Embedders can disable this (especially if a name provided to
+  // SetDefaultBasename makes it clear that the trace is incomplete and final
+  // name will be provided via SetDefaultBasename call before calling Stop).
+  enum class TempFilePolicy {
+    kUseTemporaryFile,
+    kWriteDirectly,
+  };
+  void SetUsingTemporaryFile(TempFilePolicy temp_file_policy);
+
+  // Set default basename for the trace output file to allow //content embedders
+  // to customise it using some metadata (like test names).
+  //
+  // If --enable-trace-output is a directory (default value, empty, designated
+  // "current directory"), then the startup trace will be written in a file with
+  // the given basename in this directory. Depending on the |extension_type|,
+  // an appropriate extension (.json or .proto) will be added.
+  //
+  // Note that embedders can call it even after tracing has started and Perfetto
+  // started streaming the trace into it — in that case,
+  // StartupTracingController will rename the file after finishing. However,
+  // this is guaranteed to work only when tracing lasts until Stop() (not with
+  // duration-based tracing).
+  enum class ExtensionType {
+    kAppendAppropriate,
+    kNone,
+  };
+  void SetDefaultBasename(std::string basename, ExtensionType extension_type);
+  // As the test harness calls SetDefaultBasename, expose ForTest() version for
+  // the tests checking the StartupTracingController logic itself.
+  void SetDefaultBasenameForTest(std::string basename,
+                                 ExtensionType extension_type);
+
+  bool is_finished_for_testing() const { return state_ == State::kStopped; }
+
  private:
   void Stop(base::OnceClosure on_finished_callback);
 
   void OnStoppedOnUIThread();
 
+  base::FilePath GetOutputPath();
+
   enum class State {
+    kNotEnabled,
     kRunning,
-    kNotRunning,
+    kStopped,
   };
-  State state_ = State::kNotRunning;
+  State state_ = State::kNotEnabled;
 
   // All actual interactions with the tracing service and the process of writing
   // files happens on a background thread.
@@ -40,6 +88,11 @@
 
   base::OnceClosure on_tracing_finished_;
   base::FilePath output_file_;
+
+  std::string default_basename_;
+  bool basename_for_test_set_ = false;
+
+  TempFilePolicy temp_file_policy_ = TempFilePolicy::kUseTemporaryFile;
 };
 
 }  // namespace content
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 3139a07..4d26731 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -402,14 +402,6 @@
 // Enabled threaded compositing for web tests.
 const char kEnableThreadedCompositing[]     = "enable-threaded-compositing";
 
-// Enable tracing during the execution of browser tests.
-const char kEnableTracing[]                 = "enable-tracing";
-
-// The filename to write the output of the test tracing to. If it is empty
-// or it ends in a directory separator then an auto-generated filename will be
-// appended.
-const char kEnableTracingOutput[]           = "enable-tracing-output";
-
 // Enable screen capturing support for MediaStream API.
 const char kEnableUserMediaScreenCapturing[] =
     "enable-usermedia-screen-capturing";
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index a48873f..ae35cec5 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -123,8 +123,6 @@
 CONTENT_EXPORT extern const char kEnableStrictMixedContentChecking[];
 CONTENT_EXPORT extern const char kEnableStrictPowerfulFeatureRestrictions[];
 CONTENT_EXPORT extern const char kEnableThreadedCompositing[];
-CONTENT_EXPORT extern const char kEnableTracing[];
-CONTENT_EXPORT extern const char kEnableTracingOutput[];
 CONTENT_EXPORT extern const char kEnableUserMediaScreenCapturing[];
 CONTENT_EXPORT extern const char kEnableUseZoomForDSF[];
 CONTENT_EXPORT extern const char kEnableViewport[];
diff --git a/content/public/test/DEPS b/content/public/test/DEPS
index 2dc6203..a9f5558 100644
--- a/content/public/test/DEPS
+++ b/content/public/test/DEPS
@@ -33,6 +33,7 @@
   ".*\.(cc|mm)": [
     # Testing utilities can access anything in content/
     "+content",
+    "+components/tracing/common",
     "+gin/v8_initializer.h",
     "+services/network/network_context.h",
     "+third_party/iaccessible2",
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index eb959bf2..6bc7162 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -21,6 +21,7 @@
 #include "base/files/scoped_file.h"
 #include "base/i18n/icu_util.h"
 #include "base/location.h"
+#include "base/logging.h"
 #include "base/macros.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
@@ -37,6 +38,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
+#include "components/tracing/common/tracing_switches.h"
 #include "content/browser/browser_main_loop.h"
 #include "content/browser/browser_thread_impl.h"
 #include "content/browser/network_service_instance_impl.h"
@@ -46,6 +48,7 @@
 #include "content/browser/startup_helper.h"
 #include "content/browser/storage_partition_impl.h"
 #include "content/browser/tracing/memory_instrumentation_util.h"
+#include "content/browser/tracing/startup_tracing_controller.h"
 #include "content/browser/tracing/tracing_controller_impl.h"
 #include "content/public/app/content_main.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -166,10 +169,47 @@
   GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(quit_task));
 }
 
-void TraceStopTracingComplete(base::OnceClosure quit,
-                              const base::FilePath& file_path) {
-  LOG(ERROR) << "Tracing written to: " << file_path.value();
-  std::move(quit).Run();
+enum class TraceBasenameType {
+  kWithoutTestStatus,
+  kWithTestStatus,
+};
+
+std::string GetDefaultTraceBasename(TraceBasenameType type) {
+  std::string test_suite_name = ::testing::UnitTest::GetInstance()
+                                    ->current_test_info()
+                                    ->test_suite_name();
+  std::string test_name =
+      ::testing::UnitTest::GetInstance()->current_test_info()->name();
+  // Parameterised tests might have slashes in their full name — replace them
+  // before using it as a file name to avoid trying to write to an incorrect
+  // location.
+  base::ReplaceChars(test_suite_name, "/", "_", &test_suite_name);
+  base::ReplaceChars(test_name, "/", "_", &test_name);
+  // Add random number to the trace file to distinguish traces from different
+  // test runs. We don't use timestamp here to avoid collisions with parallel
+  // runs of the same test. Browser test runner runs one test per browser
+  // process instantiation, so saving the seed here is appopriate.
+  // GetDefaultTraceBasename() is going to be called twice:
+  // - for the first time, before the test starts to get the name of the file to
+  // stream the results (to avoid losing them if test crashes).
+  // - the second time, if test execution finishes normally, to calculate the
+  // resulting name of the file, including test result.
+  static std::string random_seed =
+      base::NumberToString(base::RandInt(1e7, 1e8 - 1));
+  std::string status;
+  if (type == TraceBasenameType::kWithTestStatus) {
+    status = ::testing::UnitTest::GetInstance()
+                     ->current_test_info()
+                     ->result()
+                     ->Passed()
+                 ? "OK"
+                 : "FAIL";
+  } else {
+    // In order to be able to stream the test to the file,
+    status = "NOT_FINISHED";
+  }
+  return "trace_test_" + test_suite_name + "_" + test_name + "_" + random_seed +
+         "_" + status;
 }
 
 // See SetInitialWebContents comment for more information.
@@ -483,6 +523,32 @@
       std::make_unique<CreatedMainPartsClosure>(base::BindOnce(
           &BrowserTestBase::CreatedBrowserMainParts, base::Unretained(this)));
 
+  // If tracing is enabled, customise the output filename based on the name of
+  // the test.
+  StartupTracingController::GetInstance().SetDefaultBasename(
+      GetDefaultTraceBasename(TraceBasenameType::kWithoutTestStatus),
+      StartupTracingController::ExtensionType::kAppendAppropriate);
+  // Write to the provided file directly to recover at least some data when the
+  // test crashes or times out.
+  StartupTracingController::GetInstance().SetUsingTemporaryFile(
+      StartupTracingController::TempFilePolicy::kWriteDirectly);
+  // Set a logging handler to flush a trace before crashing the test when
+  // hitting a DCHECK / LOG(FATAL).
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kEnableTracing)) {
+    DCHECK(!logging::GetLogMessageHandler());
+    logging::SetLogMessageHandler([](int severity, const char* file, int line,
+                                     size_t message_start,
+                                     const std::string& str) {
+      // TODO(crbug.com/1157954): Print the message to the console before
+      // calling this to ensure that the message is still printed if something
+      // goes wrong.
+      if (severity == logging::LOGGING_FATAL)
+        StartupTracingController::EmergencyStop();
+      return false;
+    });
+  }
+
 #if defined(OS_ANDROID)
   // For all other platforms, we call ContentMain for browser tests which goes
   // through the normal browser initialization paths. For Android, we must set
@@ -670,36 +736,6 @@
 }
 #endif
 
-namespace {
-
-std::string GetDefaultTraceFilename() {
-  std::string test_suite_name = ::testing::UnitTest::GetInstance()
-                                    ->current_test_info()
-                                    ->test_suite_name();
-  std::string test_name =
-      ::testing::UnitTest::GetInstance()->current_test_info()->name();
-  // Parameterised tests might have slashes in their full name — replace them
-  // before using it as a file name to avoid trying to write to an incorrect
-  // location.
-  base::ReplaceChars(test_suite_name, "/", "_", &test_suite_name);
-  base::ReplaceChars(test_name, "/", "_", &test_name);
-  // Add random number to the trace file to distinguish traces from different
-  // test runs.
-  // We don't use timestamp here to avoid collisions with parallel runs of the
-  // same test.
-  std::string random_seed = base::NumberToString(base::RandInt(1e7, 1e8 - 1));
-  std::string status = ::testing::UnitTest::GetInstance()
-                               ->current_test_info()
-                               ->result()
-                               ->Passed()
-                           ? "OK"
-                           : "FAIL";
-  return "trace_test_" + test_suite_name + "_" + test_name + "_" + random_seed +
-         "_" + status + ".json";
-}
-
-}  // namespace
-
 void BrowserTestBase::ProxyRunTestOnMainThreadLoop() {
 #if !defined(OS_ANDROID)
   // All FeatureList overrides should have been registered prior to browser test
@@ -728,17 +764,6 @@
     signal(SIGTERM, DumpStackTraceSignalHandler);
 #endif  // defined(OS_POSIX)
 
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableTracing)) {
-    base::trace_event::TraceConfig trace_config(
-        base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-            switches::kEnableTracing),
-        base::trace_event::RECORD_CONTINUOUSLY);
-    TracingController::GetInstance()->StartTracing(
-        trace_config,
-        TracingController::StartTracingDoneCallback());
-  }
-
   {
     // This can be called from a posted task. Allow nested tasks here, because
     // otherwise the test body will have to do it in order to use RunLoop for
@@ -782,31 +807,23 @@
     TearDownOnMainThread();
   }
 
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kEnableTracing)) {
-    base::FilePath trace_file =
-        base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
-            switches::kEnableTracingOutput);
-    // If |trace_file| ends in a directory separator or is empty use a generated
-    // name in that directory (empty means current directory).
-    if (trace_file.empty() || trace_file.EndsWithSeparator())
-      trace_file = trace_file.AppendASCII(GetDefaultTraceFilename());
-
-    // Wait for tracing to collect results from the renderers.
-    base::RunLoop run_loop;
-    TracingController::GetInstance()->StopTracing(
-        TracingControllerImpl::CreateFileEndpoint(
-            trace_file, base::BindOnce(&TraceStopTracingComplete,
-                                       run_loop.QuitClosure(), trace_file)));
-    run_loop.Run();
-  }
-
   PostRunTestOnMainThread();
 
   // Sometimes tests initialize a storage partition and the initialization
   // schedules some tasks which need to be executed before finishing tests.
   // Run these tasks.
   content::RunAllPendingInMessageLoop();
+
+  // Update the trace output filename to include the test result.
+  StartupTracingController::GetInstance().SetDefaultBasename(
+      GetDefaultTraceBasename(TraceBasenameType::kWithTestStatus),
+      StartupTracingController::ExtensionType::kAppendAppropriate);
+
+#if defined(OS_ANDROID)
+  // On Android, browser main runner is not shut down, so stop trace recording
+  // here.
+  StartupTracingController::GetInstance().WaitUntilStopped();
+#endif
 }
 
 void BrowserTestBase::SetAllowNetworkAccessToHostResolutions() {
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 4a53c9a..903703fe 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -963,11 +963,18 @@
   return render_thread->GetGpuFactories();
 }
 
-media::DecoderFactory* RendererBlinkPlatformImpl::GetMediaDecoderFactory() {
+scoped_refptr<base::SingleThreadTaskRunner>
+RendererBlinkPlatformImpl::MediaThreadTaskRunner() {
   auto* render_thread = RenderThreadImpl::current();
   if (!render_thread)
     return nullptr;
 
+  return render_thread->GetMediaThreadTaskRunner();
+}
+
+media::DecoderFactory* RendererBlinkPlatformImpl::GetMediaDecoderFactory() {
+  auto* render_thread = RenderThreadImpl::current();
+  DCHECK(!!render_thread);
   return render_thread->GetMediaDecoderFactory();
 }
 
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 8f83462..b854f5b 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -210,6 +210,7 @@
       blink::MediaInspectorContext* inspector_context,
       scoped_refptr<base::SingleThreadTaskRunner> owner_task_runner) override;
   media::GpuVideoAcceleratorFactories* GetGpuFactories() override;
+  scoped_refptr<base::SingleThreadTaskRunner> MediaThreadTaskRunner() override;
   media::DecoderFactory* GetMediaDecoderFactory() override;
   void SetRenderingColorSpace(const gfx::ColorSpace& color_space) override;
   void SetActiveURL(const blink::WebURL& url,
diff --git a/content/test/data/web_bundle/cross_origin.har b/content/test/data/web_bundle/cross_origin.har
new file mode 100644
index 0000000..8966449
--- /dev/null
+++ b/content/test/data/web_bundle/cross_origin.har
@@ -0,0 +1,44 @@
+{
+  "log": {
+    "entries": [
+      {
+        "request": {
+          "method": "GET",
+          "url": "http://cross-origin.com/web_bundle/resource.json",
+          "headers": []
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-type",
+              "value": "application/json"
+            }
+          ],
+          "content": {
+            "text": "{ secret: 1 }"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "http://cross-origin.com/web_bundle/resource.png",
+          "headers": []
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-type",
+              "value": "image/png"
+            }
+          ],
+          "content": {
+            "text": "broken png"
+          }
+        }
+      }
+    ]
+  }
+}
diff --git a/content/test/data/web_bundle/cross_origin.wbn b/content/test/data/web_bundle/cross_origin.wbn
new file mode 100644
index 0000000..7b97d8ae
--- /dev/null
+++ b/content/test/data/web_bundle/cross_origin.wbn
Binary files differ
diff --git a/content/test/data/web_bundle/cross_origin.wbn.mock-http-headers b/content/test/data/web_bundle/cross_origin.wbn.mock-http-headers
new file mode 100644
index 0000000..f2c3f09
--- /dev/null
+++ b/content/test/data/web_bundle/cross_origin.wbn.mock-http-headers
@@ -0,0 +1,3 @@
+HTTP/1.1 200 OK
+Content-Type: application/webbundle
+Access-Control-Allow-Origin: *
diff --git a/content/test/data/web_bundle/generate-test-wbns.sh b/content/test/data/web_bundle/generate-test-wbns.sh
index 22e52a6f..cde6825 100755
--- a/content/test/data/web_bundle/generate-test-wbns.sh
+++ b/content/test/data/web_bundle/generate-test-wbns.sh
@@ -52,3 +52,17 @@
   -primaryURL https://test.example.org/ \
   -har variants_test.har \
   -o variants_test.wbn
+
+# Generate a WBN which will be used as a cross origin bundle.
+gen-bundle \
+  -version b1 \
+  -har cross_origin.har \
+  -primaryURL http://cross-origin.com/web_bundle/resource.json \
+  -o cross_origin.wbn
+
+# Generate a WBN which will be used as a same origin bundle.
+gen-bundle \
+  -version b1 \
+  -har same_origin.har \
+  -primaryURL http://foo.com/web_bundle/resource.json \
+  -o same_origin.wbn
diff --git a/content/test/data/web_bundle/same_origin.har b/content/test/data/web_bundle/same_origin.har
new file mode 100644
index 0000000..ba07848
--- /dev/null
+++ b/content/test/data/web_bundle/same_origin.har
@@ -0,0 +1,44 @@
+{
+  "log": {
+    "entries": [
+      {
+        "request": {
+          "method": "GET",
+          "url": "http://foo.com/web_bundle/resource.json",
+          "headers": []
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-type",
+              "value": "application/json"
+            }
+          ],
+          "content": {
+            "text": "{ secret: 1 }"
+          }
+        }
+      },
+      {
+        "request": {
+          "method": "GET",
+          "url": "http://foo.com/web_bundle/resource.png",
+          "headers": []
+        },
+        "response": {
+          "status": 200,
+          "headers": [
+            {
+              "name": "Content-type",
+              "value": "image/png"
+            }
+          ],
+          "content": {
+            "text": "broken png"
+          }
+        }
+      }
+    ]
+  }
+}
diff --git a/content/test/data/web_bundle/same_origin.wbn b/content/test/data/web_bundle/same_origin.wbn
new file mode 100644
index 0000000..dd8aced
--- /dev/null
+++ b/content/test/data/web_bundle/same_origin.wbn
Binary files differ
diff --git a/content/web_test/renderer/web_frame_test_proxy.cc b/content/web_test/renderer/web_frame_test_proxy.cc
index 92d0b04..0c52cd9 100644
--- a/content/web_test/renderer/web_frame_test_proxy.cc
+++ b/content/web_test/renderer/web_frame_test_proxy.cc
@@ -263,7 +263,7 @@
     GetWebFrame()->SetName(blink::WebString());
     GetWebFrame()->ClearOpener();
 
-    blink::WebTestingSupport::ResetInternalsObject(GetWebFrame());
+    blink::WebTestingSupport::ResetMainFrame(GetWebFrame());
     // Resetting the internals object also overrides the WebPreferences, so we
     // have to sync them to WebKit again.
     blink::WebView* web_view = GetWebFrame()->View();
diff --git a/docs/webui_explainer.md b/docs/webui_explainer.md
index f62e208..cc01a79 100644
--- a/docs/webui_explainer.md
+++ b/docs/webui_explainer.md
@@ -883,28 +883,34 @@
 Chrome's sandbox.  To make sure that the special powers granted to WebUI pages
 are safe, WebUI pages are restricted in what they can do:
 
-* WebUI pages cannot embed http/https resources or frames
+* WebUI pages cannot embed http/https resources
 * WebUI pages cannot issue http/https fetches
 
 In the rare case that a WebUI page really needs to include web content, the safe
-way to do this is by using a `<webview>` tag.  Using a `<webview>` tag is more
-secure than using an iframe for multiple reasons, even if Site Isolation and
-out-of-process iframes keep the web content out of the privileged WebUI process.
+way to do this is by using an `<iframe>` tag. Chrome's security model gives
+process isolation between the WebUI and the web content. However, some extra
+precautions need to be taken, because there are properties of the page that are
+accessible cross-origin and malicious code can take advantage of such data to
+attack the WebUI. Here are some things to keep in mind:
 
-First, the content inside the `<webview>` tag has a much reduced attack surface,
-since it does not have a window reference to its embedder or any other frames.
-Only postMessage channel is supported, and this needs to be initiated by the
-embedder, not the guest.
+* The WebUI page can receive postMessage payloads from the web and should
+  ensure it verifies any messages as they are not trustworthy.
+* The entire frame tree is visible to the embedded web content, including
+  ancestor origins.
+* The web content runs in the same StoragePartition and Profile as the WebUI,
+  which reflect where the WebUI page was loaded (e.g., the default profile,
+  Incognito, etc). The corresponding user credentials will thus be available to
+  the web content inside the WebUI, possibly showing the user as signed in.
 
-Second, the content inside the `<webview>` tag is hosted in a separate
-StoragePartition. Thus, cookies and other persistent storage for both the WebUI
-page and other browser tabs are inaccessible to it.
+Note: WebUIs have a default Content Security Policy which disallows embedding
+any frames. If you want to include any web content in an <iframe> you will need
+to update the policy for your WebUI. When doing so, allow only known origins and
+avoid making the policy more permissive than strictly necessary.
 
-This greater level of isolation makes it safer to load possibly untrustworthy or
-compromised web content, reducing the risk of sandbox escapes.
-
-For an example of switching from iframe to webview tag see
-https://crrev.com/c/710738.
+Alternatively, a `<webview>` tag can be used, which runs in a separate
+StoragePartition, a separate frame tree, and restricts postMessage communication
+by default. However, `<webview>` does not support Site Isolation and
+therefore it is not advisable to use for any sensitive content.
 
 
 ## See also
diff --git a/gin/gin_features.cc b/gin/gin_features.cc
index b200a04..570eaef1 100644
--- a/gin/gin_features.cc
+++ b/gin/gin_features.cc
@@ -50,4 +50,8 @@
 const base::Feature kV8TurboDirectHeapAccess{"V8TurboDirectHeapAccess",
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables fallback to a breadth-first regexp engine on excessive backtracking.
+const base::Feature kV8ExperimentalRegexpEngine{
+    "V8ExperimentalRegexpEngine", base::FEATURE_ENABLED_BY_DEFAULT};
+
 }  // namespace features
diff --git a/gin/gin_features.h b/gin/gin_features.h
index 0e2b51c8..69bc3b9 100644
--- a/gin/gin_features.h
+++ b/gin/gin_features.h
@@ -21,6 +21,7 @@
 GIN_EXPORT extern const base::Feature kV8NoReclaimUnmodifiedWrappers;
 GIN_EXPORT extern const base::Feature kV8LocalHeaps;
 GIN_EXPORT extern const base::Feature kV8TurboDirectHeapAccess;
+GIN_EXPORT extern const base::Feature kV8ExperimentalRegexpEngine;
 
 }  // namespace features
 
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
index 4174fd9..3967c18d 100644
--- a/gin/v8_initializer.cc
+++ b/gin/v8_initializer.cc
@@ -288,6 +288,16 @@
     v8::V8::SetFlagsFromString(no_direct_access, sizeof(no_direct_access) - 1);
   }
 
+  if (!base::FeatureList::IsEnabled(features::kV8ExperimentalRegexpEngine)) {
+    // The --enable-experimental-regexp-engine-on-excessive-backtracks flag is
+    // enabled by default, so we need to explicitly disable it if
+    // kV8ExperimentalRegexpEngine is disabled.
+    static constexpr char no_experimental_regexp_engine[] =
+        "--no-enable-experimental-regexp-engine-on-excessive-backtracks";
+    v8::V8::SetFlagsFromString(no_experimental_regexp_engine,
+                               sizeof(no_experimental_regexp_engine) - 1);
+  }
+
   if (IsolateHolder::kStrictMode == mode) {
     static const char use_strict[] = "--use_strict";
     v8::V8::SetFlagsFromString(use_strict, sizeof(use_strict) - 1);
diff --git a/headless/lib/browser/headless_web_contents_impl.cc b/headless/lib/browser/headless_web_contents_impl.cc
index 1eb384ea..af6718c 100644
--- a/headless/lib/browser/headless_web_contents_impl.cc
+++ b/headless/lib/browser/headless_web_contents_impl.cc
@@ -177,6 +177,13 @@
         ->block_new_web_contents();
   }
 
+  void RequestToLockMouse(content::WebContents* web_contents,
+                          bool user_gesture,
+                          bool last_unlocked_by_target) override {
+    web_contents->GotResponseToLockMouseRequest(
+        blink::mojom::PointerLockResult::kSuccess);
+  }
+
  private:
   HeadlessBrowserImpl* browser() { return headless_web_contents_->browser(); }
 
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index d8d94921..5fe4dd11 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -27321,110 +27321,6 @@
       }
     }
     builders {
-      name: "Win7 Builder (dbg) Goma Canary"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builder:Win7 Builder (dbg) Goma Canary"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Windows-7"
-      dimensions: "pool:luci.chromium.ci"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        cmd: "recipes"
-      }
-      properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.goma.fyi\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 36000
-      build_numbers: YES
-      service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-      }
-    }
-    builders {
-      name: "Win7 Builder (dbg) Goma Latest Client"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builder:Win7 Builder (dbg) Goma Latest Client"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Windows-7"
-      dimensions: "pool:luci.chromium.ci"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        cmd: "recipes"
-      }
-      properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.goma.fyi\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 36000
-      build_numbers: YES
-      service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-      }
-    }
-    builders {
-      name: "Win7 Builder Goma Canary"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builder:Win7 Builder Goma Canary"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Windows-7"
-      dimensions: "pool:luci.chromium.ci"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        cmd: "recipes"
-      }
-      properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.goma.fyi\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 36000
-      build_numbers: YES
-      service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-      }
-    }
-    builders {
-      name: "Win7 Builder Goma Latest Client"
-      swarming_host: "chromium-swarm.appspot.com"
-      swarming_tags: "vpython:native-python-wrapper"
-      dimensions: "builder:Win7 Builder Goma Latest Client"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "os:Windows-7"
-      dimensions: "pool:luci.chromium.ci"
-      exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/master"
-        cmd: "recipes"
-      }
-      properties: "{\"$build/goma\":{\"use_luci_auth\":true},\"$kitchen\":{\"devshell\":true,\"git_auth\":true},\"$recipe_engine/isolated\":{\"server\":\"https://isolateserver.appspot.com\"},\"builder_group\":\"chromium.goma.fyi\",\"recipe\":\"chromium\"}"
-      execution_timeout_secs: 36000
-      build_numbers: YES
-      service_account: "goma-release-testing@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "luci.use_realms"
-        value: 100
-      }
-      resultdb {
-        enable: true
-      }
-    }
-    builders {
       name: "android-archive-dbg-goma-canary"
       swarming_host: "chromium-swarm.appspot.com"
       swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index 5ac10ba..993cd27c 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -6731,14 +6731,6 @@
     short_name: "loc"
   }
   builders {
-    name: "buildbucket/luci.chromium.goma/Win7 Builder Goma Canary"
-    category: "win7|rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.goma/Win7 Builder (dbg) Goma Canary"
-    category: "win7|dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.goma/Mac Builder Goma Canary"
     category: "mac|rel"
   }
@@ -11458,14 +11450,6 @@
     short_name: "loc"
   }
   builders {
-    name: "buildbucket/luci.chromium.goma/Win7 Builder Goma Latest Client"
-    category: "win7|rel"
-  }
-  builders {
-    name: "buildbucket/luci.chromium.goma/Win7 Builder (dbg) Goma Latest Client"
-    category: "win7|dbg"
-  }
-  builders {
     name: "buildbucket/luci.chromium.goma/Mac Builder Goma Latest Client"
     category: "mac|rel"
   }
diff --git a/infra/config/generated/luci-scheduler.cfg b/infra/config/generated/luci-scheduler.cfg
index b49f2fb..fa0d744 100644
--- a/infra/config/generated/luci-scheduler.cfg
+++ b/infra/config/generated/luci-scheduler.cfg
@@ -4011,46 +4011,6 @@
   }
 }
 job {
-  id: "Win7 Builder (dbg) Goma Canary"
-  realm: "goma"
-  acl_sets: "goma"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.goma"
-    builder: "Win7 Builder (dbg) Goma Canary"
-  }
-}
-job {
-  id: "Win7 Builder (dbg) Goma Latest Client"
-  realm: "goma"
-  acl_sets: "goma"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.goma"
-    builder: "Win7 Builder (dbg) Goma Latest Client"
-  }
-}
-job {
-  id: "Win7 Builder Goma Canary"
-  realm: "goma"
-  acl_sets: "goma"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.goma"
-    builder: "Win7 Builder Goma Canary"
-  }
-}
-job {
-  id: "Win7 Builder Goma Latest Client"
-  realm: "goma"
-  acl_sets: "goma"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "luci.chromium.goma"
-    builder: "Win7 Builder Goma Latest Client"
-  }
-}
-job {
   id: "Win7 FYI Debug (AMD)"
   realm: "ci"
   acls {
@@ -7257,10 +7217,6 @@
   triggers: "Win Builder Goma Canary"
   triggers: "Win Builder Goma Latest Client"
   triggers: "Win Builder Goma RBE Latest Client"
-  triggers: "Win7 Builder (dbg) Goma Canary"
-  triggers: "Win7 Builder (dbg) Goma Latest Client"
-  triggers: "Win7 Builder Goma Canary"
-  triggers: "Win7 Builder Goma Latest Client"
   triggers: "android-archive-dbg-goma-canary"
   triggers: "android-archive-dbg-goma-latest"
   triggers: "android-archive-dbg-goma-rbe-ats-canary"
diff --git a/infra/config/subprojects/goma/consoles/chromium.goma.fyi.star b/infra/config/subprojects/goma/consoles/chromium.goma.fyi.star
index f1588ce..a734330 100644
--- a/infra/config/subprojects/goma/consoles/chromium.goma.fyi.star
+++ b/infra/config/subprojects/goma/consoles/chromium.goma.fyi.star
@@ -24,14 +24,6 @@
             short_name = "loc",
         ),
         luci.console_view_entry(
-            builder = "goma/Win7 Builder Goma Canary",
-            category = "win7|rel",
-        ),
-        luci.console_view_entry(
-            builder = "goma/Win7 Builder (dbg) Goma Canary",
-            category = "win7|dbg",
-        ),
-        luci.console_view_entry(
             builder = "goma/Mac Builder Goma Canary",
             category = "mac|rel",
         ),
diff --git a/infra/config/subprojects/goma/consoles/goma.latest.star b/infra/config/subprojects/goma/consoles/goma.latest.star
index 4bf267b..133e1a3 100644
--- a/infra/config/subprojects/goma/consoles/goma.latest.star
+++ b/infra/config/subprojects/goma/consoles/goma.latest.star
@@ -23,14 +23,6 @@
             short_name = "loc",
         ),
         luci.console_view_entry(
-            builder = "goma/Win7 Builder Goma Latest Client",
-            category = "win7|rel",
-        ),
-        luci.console_view_entry(
-            builder = "goma/Win7 Builder (dbg) Goma Latest Client",
-            category = "win7|dbg",
-        ),
-        luci.console_view_entry(
             builder = "goma/Mac Builder Goma Latest Client",
             category = "mac|rel",
         ),
diff --git a/infra/config/subprojects/goma/goma.star b/infra/config/subprojects/goma/goma.star
index 83dc774..dd1f5a7a 100644
--- a/infra/config/subprojects/goma/goma.star
+++ b/infra/config/subprojects/goma/goma.star
@@ -91,16 +91,6 @@
 )
 
 fyi_goma_canary_builder(
-    name = "Win7 Builder (dbg) Goma Canary",
-    os = os.WINDOWS_7,
-)
-
-fyi_goma_canary_builder(
-    name = "Win7 Builder Goma Canary",
-    os = os.WINDOWS_7,
-)
-
-fyi_goma_canary_builder(
     name = "android-archive-dbg-goma-canary",
 )
 
@@ -245,16 +235,6 @@
 )
 
 fyi_goma_latest_client_builder(
-    name = "Win7 Builder (dbg) Goma Latest Client",
-    os = os.WINDOWS_7,
-)
-
-fyi_goma_latest_client_builder(
-    name = "Win7 Builder Goma Latest Client",
-    os = os.WINDOWS_7,
-)
-
-fyi_goma_latest_client_builder(
     name = "android-archive-dbg-goma-latest",
 )
 
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 6f3023c..bb34afe 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -95,6 +95,7 @@
 #import "ios/chrome/browser/ui/main/scene_delegate.h"
 #import "ios/chrome/browser/ui/ui_feature_flags.h"
 #include "ios/chrome/browser/ui/util/multi_window_support.h"
+#include "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -733,9 +734,8 @@
 }
 
 - (void)orientationDidChange:(NSNotification*)notification {
-  crash_keys::SetCurrentOrientation(
-      [[UIApplication sharedApplication] statusBarOrientation],
-      [[UIDevice currentDevice] orientation]);
+  crash_keys::SetCurrentOrientation(GetInterfaceOrientation(),
+                                    [[UIDevice currentDevice] orientation]);
 }
 
 - (void)registerForOrientationChangeNotifications {
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index 4c8c0639..b97f0e7 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -292,10 +292,6 @@
 
   [accessory_mediator_ injectWebState:web_state()];
   [accessory_mediator_ injectProvider:suggestion_controller_];
-  auto suggestionManager = base::mac::ObjCCastStrict<JsSuggestionManager>(
-      [web_state()->GetJSInjectionReceiver()
-          instanceOfClass:[JsSuggestionManager class]]);
-  [accessory_mediator_ injectSuggestionManager:suggestionManager];
 
   histogram_tester_.reset(new base::HistogramTester());
 }
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_handler.h b/ios/chrome/browser/autofill/form_input_accessory_view_handler.h
index fd4b04f..3df1a7a 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_handler.h
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_handler.h
@@ -7,13 +7,15 @@
 
 #import "ios/chrome/browser/autofill/form_input_navigator.h"
 
-@class JsSuggestionManager;
+namespace autofill {
+class JsSuggestionManager;
+}  // namespace autofill
 
 // This handles user actions in the default keyboard accessory view buttons.
 @interface FormInputAccessoryViewHandler : NSObject <FormInputNavigator>
 
 // The JS manager for interacting with the underlying form.
-@property(nonatomic, weak) JsSuggestionManager* JSSuggestionManager;
+@property(nonatomic) autofill::JsSuggestionManager* JSSuggestionManager;
 
 // Resets the metrics logger of the instance.
 - (void)reset;
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_handler.mm b/ios/chrome/browser/autofill/form_input_accessory_view_handler.mm
index a26bdfa..2d8b585 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_handler.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_handler.mm
@@ -9,6 +9,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
+#include "base/strings/sys_string_conversions.h"
 #import "components/autofill/core/browser/keyboard_accessory_metrics_logger.h"
 #import "components/autofill/ios/browser/js_suggestion_manager.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
@@ -255,12 +256,17 @@
 }
 
 - (void)fetchPreviousAndNextElementsPresenceWithCompletionHandler:
-    (void (^)(BOOL, BOOL))completionHandler {
+    (void (^)(bool, bool))completionHandler {
   DCHECK(completionHandler);
-  [_JSSuggestionManager
-      fetchPreviousAndNextElementsPresenceInFrameWithID:
-          _lastFocusFormActivityWebFrameID
-                                      completionHandler:completionHandler];
+
+  if (!_JSSuggestionManager) {
+    completionHandler(false, false);
+    return;
+  }
+
+  _JSSuggestionManager->FetchPreviousAndNextElementsPresenceInFrameWithID(
+      base::SysNSStringToUTF8(_lastFocusFormActivityWebFrameID),
+      base::BindOnce(completionHandler));
 }
 
 #pragma mark - Private
@@ -272,11 +278,12 @@
   NSString* actionName = kFormSuggestionAssistButtonDone;
   BOOL performedAction = [self executeFormAssistAction:actionName];
 
-  if (!performedAction && [_lastFocusFormActivityWebFrameID length]) {
+  if (!performedAction && [_lastFocusFormActivityWebFrameID length] &&
+      _JSSuggestionManager) {
     // We could not find the built-in form assist controls, so try to focus
     // the next or previous control using JavaScript.
-    [_JSSuggestionManager
-        closeKeyboardForFrameWithID:_lastFocusFormActivityWebFrameID];
+    _JSSuggestionManager->CloseKeyboardForFrameWithID(
+        base::SysNSStringToUTF8(_lastFocusFormActivityWebFrameID));
   }
   if (loggingButtonPressed) {
     _keyboardAccessoryMetricsLogger->OnCloseButtonPressed();
@@ -290,11 +297,11 @@
   NSString* actionName = kFormSuggestionAssistButtonPreviousElement;
   BOOL performedAction = [self executeFormAssistAction:actionName];
 
-  if (!performedAction) {
+  if (!performedAction && _JSSuggestionManager) {
     // We could not find the built-in form assist controls, so try to focus
     // the next or previous control using JavaScript.
-    [_JSSuggestionManager
-        selectPreviousElementInFrameWithID:_lastFocusFormActivityWebFrameID];
+    _JSSuggestionManager->SelectPreviousElementInFrameWithID(
+        base::SysNSStringToUTF8(_lastFocusFormActivityWebFrameID));
   }
   if (loggingButtonPressed) {
     _keyboardAccessoryMetricsLogger->OnPreviousButtonPressed();
@@ -308,11 +315,11 @@
   NSString* actionName = kFormSuggestionAssistButtonNextElement;
   BOOL performedAction = [self executeFormAssistAction:actionName];
 
-  if (!performedAction) {
+  if (!performedAction && _JSSuggestionManager) {
     // We could not find the built-in form assist controls, so try to focus
     // the next or previous control using JavaScript.
-    [_JSSuggestionManager
-        selectNextElementInFrameWithID:_lastFocusFormActivityWebFrameID];
+    _JSSuggestionManager->SelectNextElementInFrameWithID(
+        base::SysNSStringToUTF8(_lastFocusFormActivityWebFrameID));
   }
   if (loggingButtonPressed) {
     _keyboardAccessoryMetricsLogger->OnNextButtonPressed();
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view_handler_unittest.mm b/ios/chrome/browser/autofill/form_input_accessory_view_handler_unittest.mm
index 22f9c78..54472cf 100644
--- a/ios/chrome/browser/autofill/form_input_accessory_view_handler_unittest.mm
+++ b/ios/chrome/browser/autofill/form_input_accessory_view_handler_unittest.mm
@@ -23,10 +23,7 @@
       [[FormInputAccessoryViewHandler alloc] init];
   ASSERT_TRUE(accessory_view_delegate);
   [accessory_view_delegate closeKeyboardWithoutButtonPress];
-  CRWJSInjectionReceiver* injection_receiver =
-      web_state()->GetJSInjectionReceiver();
   accessory_view_delegate.JSSuggestionManager =
-      base::mac::ObjCCastStrict<JsSuggestionManager>(
-          [injection_receiver instanceOfClass:[JsSuggestionManager class]]);
+      autofill::JsSuggestionManager::GetOrCreateForWebState(web_state());
   [accessory_view_delegate closeKeyboardWithoutButtonPress];
 }
diff --git a/ios/chrome/browser/autofill/form_input_navigator.h b/ios/chrome/browser/autofill/form_input_navigator.h
index e0dc03b..f03704f 100644
--- a/ios/chrome/browser/autofill/form_input_navigator.h
+++ b/ios/chrome/browser/autofill/form_input_navigator.h
@@ -30,11 +30,11 @@
 
 // Called when updating the keyboard view. Checks if the page contains a next
 // and a previous element.
-// |completionHandler| is called with 2 BOOLs, the first indicating if a
+// |completionHandler| is called with 2 bools, the first indicating if a
 // previous element was found, and the second indicating if a next element was
 // found. |completionHandler| cannot be nil.
 - (void)fetchPreviousAndNextElementsPresenceWithCompletionHandler:
-    (void (^)(BOOL, BOOL))completionHandler;
+    (void (^)(bool, bool))completionHandler;
 
 @end
 
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller.h b/ios/chrome/browser/autofill/form_suggestion_controller.h
index 41e0889..2d1755b 100644
--- a/ios/chrome/browser/autofill/form_suggestion_controller.h
+++ b/ios/chrome/browser/autofill/form_suggestion_controller.h
@@ -52,12 +52,6 @@
 
 @interface FormSuggestionController (ForTesting)
 
-// Initializes a new controller in the same way as the public initializer, but
-// supports specifying a JsSuggestionManager for testing.
-- (instancetype)initWithWebState:(web::WebState*)webState
-                       providers:(NSArray*)providers
-             JsSuggestionManager:(JsSuggestionManager*)jsSuggestionManager;
-
 // Overrides the web view proxy.
 - (void)setWebViewProxy:(id<CRWWebViewProxy>)webViewProxy;
 
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller.mm b/ios/chrome/browser/autofill/form_suggestion_controller.mm
index e6a36bb..c6e29fd 100644
--- a/ios/chrome/browser/autofill/form_suggestion_controller.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_controller.mm
@@ -110,9 +110,6 @@
   // Bridge to observe the web state from Objective-C.
   std::unique_ptr<web::WebStateObserverBridge> _webStateObserverBridge;
 
-  // Manager for FormSuggestion JavaScripts.
-  JsSuggestionManager* _jsSuggestionManager;
-
   // The provider for the current set of suggestions.
   __weak id<FormSuggestionProvider> _provider;
 }
@@ -120,8 +117,7 @@
 @synthesize formInputNavigator = _formInputNavigator;
 
 - (instancetype)initWithWebState:(web::WebState*)webState
-                       providers:(NSArray*)providers
-             JsSuggestionManager:(JsSuggestionManager*)jsSuggestionManager {
+                       providers:(NSArray*)providers {
   self = [super init];
   if (self) {
     DCHECK(webState);
@@ -130,24 +126,11 @@
         std::make_unique<web::WebStateObserverBridge>(self);
     _webState->AddObserver(_webStateObserverBridge.get());
     _webViewProxy = webState->GetWebViewProxy();
-    _jsSuggestionManager = jsSuggestionManager;
     _suggestionProviders = [providers copy];
   }
   return self;
 }
 
-- (instancetype)initWithWebState:(web::WebState*)webState
-                       providers:(NSArray*)providers {
-  JsSuggestionManager* jsSuggestionManager =
-      base::mac::ObjCCast<JsSuggestionManager>(
-          [webState->GetJSInjectionReceiver()
-              instanceOfClass:[JsSuggestionManager class]]);
-  [jsSuggestionManager setWebFramesManager:webState->GetWebFramesManager()];
-  return [self initWithWebState:webState
-                      providers:providers
-            JsSuggestionManager:jsSuggestionManager];
-}
-
 - (void)dealloc {
   if (_webState) {
     _webState->RemoveObserver(_webStateObserverBridge.get());
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
index a4289e4a..2da9678 100644
--- a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
@@ -156,10 +156,6 @@
   void SetUp() override {
     PlatformTest::SetUp();
 
-    // Mock out the JsSuggestionManager.
-    mock_js_suggestion_manager_ =
-        [OCMockObject niceMockForClass:[JsSuggestionManager class]];
-
     fake_web_state_.SetWebViewProxy(mock_web_view_proxy_);
   }
 
@@ -172,10 +168,9 @@
   // Sets up |suggestion_controller_| with the specified array of
   // FormSuggestionProviders.
   void SetUpController(NSArray* providers) {
-    suggestion_controller_ = [[FormSuggestionController alloc]
-           initWithWebState:&fake_web_state_
-                  providers:providers
-        JsSuggestionManager:mock_js_suggestion_manager_];
+    suggestion_controller_ =
+        [[FormSuggestionController alloc] initWithWebState:&fake_web_state_
+                                                 providers:providers];
     [suggestion_controller_ setWebViewProxy:mock_web_view_proxy_];
 
     id mock_consumer = [OCMockObject
@@ -217,7 +212,6 @@
 
     [accessory_mediator_ injectWebState:&fake_web_state_];
     [accessory_mediator_ injectProvider:suggestion_controller_];
-    [accessory_mediator_ injectSuggestionManager:mock_js_suggestion_manager_];
   }
 
   // The FormSuggestionController under test.
@@ -226,9 +220,6 @@
   // The suggestions the controller sent to the client, if any.
   NSArray* received_suggestions_;
 
-  // Mock JsSuggestionManager for verifying interactions.
-  id mock_js_suggestion_manager_;
-
   // Mock CRWWebViewProxy for verifying interactions.
   id mock_web_view_proxy_;
 
@@ -253,7 +244,6 @@
   fake_web_state_.SetCurrentURL(GURL("data:text/html;charset=utf8;base64,"));
   fake_web_state_.OnPageLoaded(web::PageLoadCompletionStatus::SUCCESS);
   EXPECT_FALSE(received_suggestions_.count);
-  EXPECT_OCMOCK_VERIFY(mock_js_suggestion_manager_);
 }
 
 // Tests that pages whose content isn't HTML aren't processed.
diff --git a/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm b/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
index 99937d4..a1641ea 100644
--- a/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
+++ b/ios/chrome/browser/autofill/js_suggestion_manager_unittest.mm
@@ -31,7 +31,7 @@
   JsSuggestionManagerTest()
       : ChromeWebTest(std::make_unique<ChromeWebClient>()) {}
   // Returns the frame ID for the main frame of |web_state()|'s current page.
-  NSString* GetFrameIdForMainFrame();
+  std::string GetFrameIdForMainFrame();
   // Helper method that initializes a form with three fields. Can be used to
   // test whether adding an attribute on the second field causes it to be
   // skipped (or not, as is appropriate) by selectNextElement.
@@ -47,19 +47,17 @@
           return [GetActiveElementName() isEqualToString:name];
         });
   }
-  JsSuggestionManager* manager_;
+  autofill::JsSuggestionManager* manager_;
 };
 
 void JsSuggestionManagerTest::SetUp() {
   ChromeWebTest::SetUp();
-  manager_ = [[JsSuggestionManager alloc]
-      initWithReceiver:web_state()->GetJSInjectionReceiver()];
-  [manager_ setWebFramesManager:web_state()->GetWebFramesManager()];
+  manager_ = autofill::JsSuggestionManager::GetOrCreateForWebState(web_state());
 }
 
-NSString* JsSuggestionManagerTest::GetFrameIdForMainFrame() {
+std::string JsSuggestionManagerTest::GetFrameIdForMainFrame() {
   web::WebFramesManager* manager = web_state()->GetWebFramesManager();
-  return base::SysUTF8ToNSString(manager->GetMainWebFrame()->GetFrameId());
+  return manager->GetMainWebFrame()->GetFrameId();
 }
 
 TEST_F(JsSuggestionManagerTest, InitAndInject) {
@@ -234,24 +232,22 @@
 
   ExecuteJavaScript(@"document.getElementsByName('firstname')[0].focus()");
 
-  [manager_ selectNextElementInFrameWithID:GetFrameIdForMainFrame()];
+  manager_->SelectNextElementInFrameWithID(GetFrameIdForMainFrame());
   EXPECT_TRUE(WaitUntilElementSelected(@"lastname"));
   __block BOOL block_was_called = NO;
-  [manager_
-      fetchPreviousAndNextElementsPresenceInFrameWithID:GetFrameIdForMainFrame()
-                                      completionHandler:^void(
-                                          BOOL has_previous_element,
-                                          BOOL has_next_element) {
-                                        block_was_called = YES;
-                                        EXPECT_TRUE(has_previous_element);
-                                        EXPECT_TRUE(has_next_element);
-                                      }];
+  manager_->FetchPreviousAndNextElementsPresenceInFrameWithID(
+      GetFrameIdForMainFrame(),
+      base::BindOnce(^void(bool has_previous_element, bool has_next_element) {
+        block_was_called = YES;
+        EXPECT_TRUE(has_previous_element);
+        EXPECT_TRUE(has_next_element);
+      }));
   base::test::ios::WaitUntilCondition(^bool() {
     return block_was_called;
   });
-  [manager_ selectNextElementInFrameWithID:GetFrameIdForMainFrame()];
+  manager_->SelectNextElementInFrameWithID(GetFrameIdForMainFrame());
   EXPECT_TRUE(WaitUntilElementSelected(@"email"));
-  [manager_ selectPreviousElementInFrameWithID:GetFrameIdForMainFrame()];
+  manager_->SelectPreviousElementInFrameWithID(GetFrameIdForMainFrame());
   EXPECT_TRUE(WaitUntilElementSelected(@"lastname"));
 }
 
@@ -266,7 +262,7 @@
                                       attribute]);
   ExecuteJavaScript(@"document.getElementsByName('firstname')[0].focus()");
   EXPECT_NSEQ(@"firstname", GetActiveElementName());
-  [manager_ selectNextElementInFrameWithID:GetFrameIdForMainFrame()];
+  manager_->SelectNextElementInFrameWithID(GetFrameIdForMainFrame());
   if (shouldSkip)
     EXPECT_TRUE(WaitUntilElementSelected(@"lastname"));
   else
@@ -331,7 +327,7 @@
       @"select.onblur = function(){window.location.href = '#test'}");
   ExecuteJavaScript(@"select.focus()");
   // In the failure condition the app will crash during the next line.
-  [manager_ closeKeyboardForFrameWithID:GetFrameIdForMainFrame()];
+  manager_->CloseKeyboardForFrameWithID(GetFrameIdForMainFrame());
   // TODO(crbug.com/661624): add a check for the keyboard actually being
   // dismissed; unfortunately it is not known how to adapt
   // WaitForBackgroundTasks to yield for events wrapped with window.setTimeout()
@@ -339,7 +335,7 @@
 }
 
 // Test fixture to test
-// |fetchPreviousAndNextElementsPresenceWithCompletionHandler|.
+// |FetchPreviousAndNextElementsPresenceInFrameWithID|.
 class FetchPreviousAndNextExceptionTest : public JsSuggestionManagerTest {
  public:
   void SetUp() override {
@@ -349,20 +345,18 @@
 
  protected:
   // Evaluates JS and tests that the completion handler passed to
-  // |fetchPreviousAndNextElementsPresenceWithCompletionHandler| is called with
-  // (NO, NO) indicating no previous and next element.
+  // |FetchPreviousAndNextElementsPresenceInFrameWithID| is called with
+  // (false, false) indicating no previous and next element.
   void EvaluateJavaScriptAndExpectNoPreviousAndNextElement(NSString* js) {
     ExecuteJavaScript(js);
     __block BOOL block_was_called = NO;
-    id completionHandler = ^(BOOL hasPreviousElement, BOOL hasNextElement) {
-      EXPECT_FALSE(hasPreviousElement);
-      EXPECT_FALSE(hasNextElement);
-      block_was_called = YES;
-    };
-    [manager_
-        fetchPreviousAndNextElementsPresenceInFrameWithID:
-            GetFrameIdForMainFrame()
-                                        completionHandler:completionHandler];
+    manager_->FetchPreviousAndNextElementsPresenceInFrameWithID(
+        GetFrameIdForMainFrame(),
+        base::BindOnce(^(bool hasPreviousElement, bool hasNextElement) {
+          EXPECT_FALSE(hasPreviousElement);
+          EXPECT_FALSE(hasNextElement);
+          block_was_called = YES;
+        }));
     base::test::ios::WaitUntilCondition(^bool() {
       base::RunLoop().RunUntilIdle();
       return block_was_called;
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index 491b312..d621f52 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -1226,11 +1226,6 @@
     ON_CALL(*store_, IsAbleToSavePasswords).WillByDefault(Return(true));
 
     std::unique_ptr<TestChromeBrowserState> browser_state(builder.Build());
-    id mock_js_injection_receiver =
-        [OCMockObject mockForClass:[CRWJSInjectionReceiver class]];
-    [[mock_js_injection_receiver expect] executeJavaScript:[OCMArg any]
-                                         completionHandler:[OCMArg any]];
-    web_state_.SetJSInjectionReceiver(mock_js_injection_receiver);
     ON_CALL(web_state_, GetBrowserState)
         .WillByDefault(testing::Return(browser_state.get()));
     UniqueIDDataTabHelper::CreateForWebState(&web_state_);
diff --git a/ios/chrome/browser/prerender/preload_controller.mm b/ios/chrome/browser/prerender/preload_controller.mm
index 5adbd51..62b108c 100644
--- a/ios/chrome/browser/prerender/preload_controller.mm
+++ b/ios/chrome/browser/prerender/preload_controller.mm
@@ -144,7 +144,7 @@
 // Maximum time to let a cancelled webState attempt to finish restore.
 static const size_t kMaximumCancelledWebStateDelay = 2;
 
-// Used to enable the workaround for a WebKit crash, see crbug.com/1032928.
+// Kill switch guarding a workaround for a WebKit crash, see crbug.com/1032928.
 const base::Feature kPreloadDelayWebStateReset{
     "PreloadDelayWebStateReset", base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/ios/chrome/browser/ui/alert_view/alert_view_controller.mm b/ios/chrome/browser/ui/alert_view/alert_view_controller.mm
index fd34467..1538693 100644
--- a/ios/chrome/browser/ui/alert_view/alert_view_controller.mm
+++ b/ios/chrome/browser/ui/alert_view/alert_view_controller.mm
@@ -144,8 +144,7 @@
   self.contentView = [[UIView alloc] init];
   self.contentView.accessibilityIdentifier = self.alertAccessibilityIdentifier;
   self.contentView.clipsToBounds = YES;
-  self.contentView.backgroundColor =
-      [UIColor colorNamed:kPrimaryBackgroundColor];
+  self.contentView.backgroundColor = UIColor.cr_systemBackgroundColor;
   self.contentView.layer.cornerRadius = kCornerRadius;
   self.contentView.layer.shadowOffset =
       CGSizeMake(kShadowOffsetX, kShadowOffsetY);
@@ -295,8 +294,7 @@
     }
     stackHolder.layer.borderWidth = 1.0 / [UIScreen mainScreen].scale;
     stackHolder.clipsToBounds = YES;
-    stackHolder.backgroundColor =
-        [UIColor colorNamed:kSecondaryBackgroundColor];
+    stackHolder.backgroundColor = UIColor.cr_secondarySystemBackgroundColor;
     stackHolder.translatesAutoresizingMaskIntoConstraints = NO;
     self.textFieldStackHolder = stackHolder;
 
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.mm b/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.mm
index 220c77a..44cb6fd 100644
--- a/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.mm
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.h"
 
 #import "base/mac/foundation_util.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -53,9 +53,8 @@
   [CATransaction setDisableActions:YES];
 
   self.gradientLayer.colors = @[
-    (id)[[UIColor colorNamed:kPrimaryBackgroundColor] colorWithAlphaComponent:0]
-        .CGColor,
-    (id)[UIColor colorNamed:kPrimaryBackgroundColor].CGColor,
+    (id)[UIColor.cr_systemBackgroundColor colorWithAlphaComponent:0].CGColor,
+    (id)UIColor.cr_systemBackgroundColor.CGColor,
   ];
   [CATransaction commit];
 }
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
index 5bd1c58..87f3094a 100644
--- a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_coordinator.mm
@@ -23,6 +23,7 @@
 #import "ios/chrome/browser/ui/authentication/unified_consent/unified_consent_coordinator.h"
 #import "ios/chrome/browser/ui/commands/browsing_data_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/unified_consent/unified_consent_service_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -213,6 +214,15 @@
   [self interruptUserSigninUIWithAction:action completion:completion];
 }
 
+- (void)stop {
+  DCHECK(!self.viewController);
+  DCHECK(!self.mediator);
+  DCHECK(!self.unifiedConsentCoordinator);
+  DCHECK(!self.addAccountSigninCoordinator);
+  DCHECK(!self.advancedSettingsSigninCoordinator);
+  [super stop];
+}
+
 #pragma mark - UnifiedConsentCoordinatorDelegate
 
 - (void)unifiedConsentCoordinatorDidTapSettingsLink:
@@ -432,8 +442,7 @@
       // Avoid presenting the promo if the current device orientation is not
       // supported. The promo will be presented at a later moment, when the
       // device orientation is supported.
-      UIInterfaceOrientation orientation =
-          [UIApplication sharedApplication].statusBarOrientation;
+      UIInterfaceOrientation orientation = GetInterfaceOrientation();
       NSUInteger supportedOrientationsMask =
           [self.viewController supportedInterfaceOrientations];
       if (!((1 << orientation) & supportedOrientationsMask)) {
@@ -505,10 +514,10 @@
   __weak UserSigninCoordinator* weakSelf = self;
   ProceduralBlock runCompletionCallback = ^{
     [weakSelf
-        runCompletionCallbackWithSigninResult:SigninCoordinatorResultInterrupted
-                                     identity:self.unifiedConsentCoordinator
-                                                  .selectedIdentity
-                   showAdvancedSettingsSignin:NO];
+        viewControllerDismissedWithResult:SigninCoordinatorResultInterrupted
+                                 identity:weakSelf.unifiedConsentCoordinator
+                                              .selectedIdentity
+                    settingsLinkWasTapped:NO];
     if (completion) {
       completion();
     }
diff --git a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
index 90ad96b..c93c77f 100644
--- a/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/signin/user_signin/user_signin_view_controller.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/ui/authentication/signin/user_signin/gradient_view.h"
 #import "ios/chrome/browser/ui/util/rtl_geometry.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #import "ios/chrome/common/ui/util/pointer_interaction_util.h"
@@ -306,7 +307,7 @@
 #pragma mark - Properties
 
 - (UIColor*)systemBackgroundColor {
-  return [UIColor colorNamed:kPrimaryBackgroundColor];
+  return UIColor.cr_systemBackgroundColor;
 }
 
 - (NSString*)confirmationButtonTitle {
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.mm b/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.mm
index 6d8ccf65..538e5e1 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/identity_picker_view.mm
@@ -12,7 +12,6 @@
 #import "ios/chrome/browser/ui/authentication/unified_consent/unified_consent_constants.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #import "ios/chrome/common/ui/util/pointer_interaction_util.h"
 
@@ -48,7 +47,7 @@
   if (self) {
     self.accessibilityIdentifier = kIdentityPickerViewIdentifier;
     self.layer.cornerRadius = kIdentityPickerViewRadius;
-    self.backgroundColor = [UIColor colorNamed:kSecondaryBackgroundColor];
+    self.backgroundColor = UIColor.cr_secondarySystemBackgroundColor;
     // Adding view elements inside.
     // Ink view.
     _rippleView = [[MDCRippleView alloc] initWithFrame:CGRectZero];
diff --git a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm
index 10c7f11..1f2f4900 100644
--- a/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm
+++ b/ios/chrome/browser/ui/authentication/unified_consent/unified_consent_view_controller.mm
@@ -193,7 +193,7 @@
   // Separator.
   UIView* separator = [[UIView alloc] initWithFrame:CGRectZero];
   separator.translatesAutoresizingMaskIntoConstraints = NO;
-  separator.backgroundColor = [UIColor colorNamed:kSecondaryBackgroundColor];
+  separator.backgroundColor = UIColor.cr_secondarySystemBackgroundColor;
   [container addSubview:separator];
 
   // Customize label.
diff --git a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
index f4f718d..b40bbe9 100644
--- a/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
+++ b/ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.mm
@@ -22,6 +22,7 @@
 #import "ios/chrome/browser/ui/collection_view/collection_view_controller.h"
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
 #import "ios/chrome/browser/ui/util/rtl_geometry.h"
+#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -363,8 +364,7 @@
   // Focus the first visible input, unless the orientation is landscape. In
   // landscape, the keyboard covers up the storage checkbox shown below this
   // view and the user might never see it.
-  if (UIInterfaceOrientationIsPortrait(
-          [UIApplication sharedApplication].statusBarOrientation)) {
+  if (UIInterfaceOrientationIsPortrait(GetInterfaceOrientation())) {
     // Also check whether any of the inputs are already the first responder and
     // are non-empty, in which case the focus should be left there.
     if ((!CVC.monthInput.isFirstResponder || CVC.monthInput.text.length == 0) &&
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h
index 63bbcbf..40d289a0 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.h
@@ -18,7 +18,6 @@
 @protocol FormInputAccessoryConsumer;
 @class FormInputAccessoryMediator;
 @protocol FormInputSuggestionsProvider;
-@class JsSuggestionManager;
 @class ReauthenticationModule;
 @protocol SecurityAlertCommands;
 
@@ -86,9 +85,6 @@
 // The WebState this instance is observing. Can be null.
 - (void)injectWebState:(web::WebState*)webState;
 
-// The JS manager for interacting with the underlying form.
-- (void)injectSuggestionManager:(JsSuggestionManager*)JSSuggestionManager;
-
 // Replaces the object in charge of providing suggestions.
 - (void)injectProvider:(id<FormInputSuggestionsProvider>)provider;
 
diff --git a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
index eb1d693..2ecaa70 100644
--- a/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
+++ b/ios/chrome/browser/ui/autofill/form_input_accessory/form_input_accessory_mediator.mm
@@ -73,9 +73,6 @@
 @property(nonatomic, strong)
     FormInputAccessoryViewHandler* formInputAccessoryHandler;
 
-// The JS manager for interacting with the underlying form.
-@property(nonatomic, weak) JsSuggestionManager* JSSuggestionManager;
-
 // The observer to determine when the keyboard dissapears and when it stays.
 @property(nonatomic, strong) KeyboardObserverHelper* keyboardObserver;
 
@@ -164,12 +161,6 @@
       web::WebState* webState = webStateList->GetActiveWebState();
       if (webState) {
         _webState = webState;
-        CRWJSInjectionReceiver* injectionReceiver =
-            webState->GetJSInjectionReceiver();
-        _JSSuggestionManager = base::mac::ObjCCastStrict<JsSuggestionManager>(
-            [injectionReceiver instanceOfClass:[JsSuggestionManager class]]);
-        [_JSSuggestionManager
-            setWebFramesManager:webState->GetWebFramesManager()];
         FormSuggestionTabHelper* tabHelper =
             FormSuggestionTabHelper::FromWebState(webState);
         if (tabHelper) {
@@ -184,7 +175,10 @@
       }
     }
     _formInputAccessoryHandler = [[FormInputAccessoryViewHandler alloc] init];
-    _formInputAccessoryHandler.JSSuggestionManager = _JSSuggestionManager;
+    _formInputAccessoryHandler.JSSuggestionManager =
+        _webState
+            ? autofill::JsSuggestionManager::GetOrCreateForWebState(_webState)
+            : nullptr;
 
     NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
     [defaultCenter addObserver:self
@@ -464,7 +458,7 @@
   __weak __typeof(self) weakSelf = self;
   [self.formInputAccessoryHandler
       fetchPreviousAndNextElementsPresenceWithCompletionHandler:^(
-          BOOL previousButtonEnabled, BOOL nextButtonEnabled) {
+          bool previousButtonEnabled, bool nextButtonEnabled) {
         weakSelf.consumer.formInputNextButtonEnabled = nextButtonEnabled;
         weakSelf.consumer.formInputPreviousButtonEnabled =
             previousButtonEnabled;
@@ -483,21 +477,15 @@
     webState->AddObserver(_webStateObserverBridge.get());
     _formActivityObserverBridge =
         std::make_unique<autofill::FormActivityObserverBridge>(webState, self);
-    CRWJSInjectionReceiver* injectionReceiver =
-        webState->GetJSInjectionReceiver();
-    self.JSSuggestionManager = base::mac::ObjCCastStrict<JsSuggestionManager>(
-        [injectionReceiver instanceOfClass:[JsSuggestionManager class]]);
-    [self.JSSuggestionManager
-        setWebFramesManager:webState->GetWebFramesManager()];
     FormSuggestionTabHelper* tabHelper =
         FormSuggestionTabHelper::FromWebState(webState);
     if (tabHelper) {
       self.provider = tabHelper->GetAccessoryViewProvider();
     }
-    _formInputAccessoryHandler.JSSuggestionManager = self.JSSuggestionManager;
+    _formInputAccessoryHandler.JSSuggestionManager =
+        autofill::JsSuggestionManager::GetOrCreateForWebState(webState);
   } else {
     self.webState = nullptr;
-    self.JSSuggestionManager = nil;
     self.provider = nil;
   }
 }
@@ -703,11 +691,6 @@
       std::make_unique<autofill::FormActivityObserverBridge>(_webState, self);
 }
 
-- (void)injectSuggestionManager:(JsSuggestionManager*)JSSuggestionManager {
-  _JSSuggestionManager = JSSuggestionManager;
-  _formInputAccessoryHandler.JSSuggestionManager = _JSSuggestionManager;
-}
-
 - (void)injectProvider:(id<FormInputSuggestionsProvider>)provider {
   self.provider = provider;
 }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
index ff0ac7a..11f389b 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_injection_handler.mm
@@ -55,7 +55,7 @@
 @property(nonatomic, readonly) CRWJSInjectionReceiver* injectionReceiver;
 
 // Convenience getter for the current suggestion manager.
-@property(nonatomic, readonly) JsSuggestionManager* suggestionManager;
+@property(nonatomic, readonly) autofill::JsSuggestionManager* suggestionManager;
 
 // Interface for |reauthenticationModule|, handling mostly the case when no
 // hardware for authentication is available.
@@ -192,14 +192,14 @@
   return nil;
 }
 
-- (JsSuggestionManager*)suggestionManager {
-  JsSuggestionManager* manager = base::mac::ObjCCastStrict<JsSuggestionManager>(
-      [self.injectionReceiver instanceOfClass:[JsSuggestionManager class]]);
+- (autofill::JsSuggestionManager*)suggestionManager {
+  autofill::JsSuggestionManager* suggestionManager = nullptr;
   web::WebState* webState = self.webStateList->GetActiveWebState();
   if (webState) {
-    [manager setWebFramesManager:webState->GetWebFramesManager()];
+    suggestionManager =
+        autofill::JsSuggestionManager::GetOrCreateForWebState(webState);
   }
-  return manager;
+  return suggestionManager;
 }
 
 #pragma mark - Private
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_navigation_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_navigation_controller.mm
index 7cd5405d..f09595a 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmark_navigation_controller.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_navigation_controller.mm
@@ -6,7 +6,7 @@
 
 #import "ios/chrome/browser/ui/bookmarks/bookmark_ui_constants.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -16,7 +16,7 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-  self.view.backgroundColor = [UIColor colorNamed:kPrimaryBackgroundColor];
+  self.view.backgroundColor = UIColor.cr_systemBackgroundColor;
   self.navigationBar.accessibilityIdentifier = kBookmarkNavigationBarIdentifier;
 }
 
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
index e493c7e..3c24886 100644
--- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -606,6 +606,11 @@
 @property(nonatomic, strong)
     BrowserViewHiderCoordinator* browserViewHiderCoordinator;
 
+// Whether the view has been translated for thumb strip usage when smooth
+// scrolling has been enabled. This allows the correct setup to be done when
+// displaying a new web state.
+@property(nonatomic, assign) BOOL viewTranslatedForSmoothScrolling;
+
 // BVC initialization
 // ------------------
 // If the BVC is initialized with a valid browser state & tab model immediately,
@@ -1439,7 +1444,6 @@
   [self installFakeStatusBar];
   [self buildToolbarAndTabStrip];
   [self setUpViewLayout:YES];
-  [self addConstraintsToTabStrip];
   [self addConstraintsToToolbar];
 
   // If the tab model and browser state are valid, finish initialization.
@@ -2282,6 +2286,7 @@
         [self addChildViewController:self.tabStripCoordinator.viewController];
         self.tabStripView = self.tabStripCoordinator.view;
         [self.view addSubview:self.tabStripView];
+        [self addConstraintsToTabStrip];
       }
       [self.view insertSubview:primaryToolbarView
                   aboveSubview:self.tabStripView];
@@ -2408,7 +2413,15 @@
     // Make new content visible, resizing it first as the orientation may
     // have changed from the last time it was displayed.
     CGRect webStateViewFrame = self.contentArea.bounds;
-    if (!fullscreen::features::ShouldUseSmoothScrolling()) {
+    if (fullscreen::features::ShouldUseSmoothScrolling()) {
+      // If the view was translated for the thumb strip, make sure to re-apply
+      // that translation here.
+      if (self.viewTranslatedForSmoothScrolling) {
+        CGFloat toolbarHeight = [self expandedTopToolbarHeight];
+        webStateViewFrame = UIEdgeInsetsInsetRect(
+            webStateViewFrame, UIEdgeInsetsMake(toolbarHeight, 0, 0, 0));
+      }
+    } else {
       // If the Smooth Scrolling is on, the WebState view is not
       // resized, and should always match the bounds of the content area.  When
       // the provider is not initialized, viewport insets resize the webview, so
@@ -2908,25 +2921,32 @@
     // frame must be moved down and the content inset is decreased. To prevent
     // the actual web content from jumping, the content offset must be moved up
     // by a corresponding amount.
-    if (self.currentWebState && ![self isNTPActiveForCurrentWebState] &&
-        fullscreen::features::ShouldUseSmoothScrolling()) {
+    if (fullscreen::features::ShouldUseSmoothScrolling()) {
+      self.viewTranslatedForSmoothScrolling = YES;
       CGFloat toolbarHeight = [self expandedTopToolbarHeight];
-      CGRect webStateViewFrame = UIEdgeInsetsInsetRect(
-          [self viewForWebState:self.currentWebState].frame,
-          UIEdgeInsetsMake(toolbarHeight, 0, 0, 0));
-      [self viewForWebState:self.currentWebState].frame = webStateViewFrame;
+      if (self.currentWebState) {
+        CGRect webStateViewFrame = UIEdgeInsetsInsetRect(
+            [self viewForWebState:self.currentWebState].frame,
+            UIEdgeInsetsMake(toolbarHeight, 0, 0, 0));
+        [self viewForWebState:self.currentWebState].frame = webStateViewFrame;
+      }
 
-      CRWWebViewScrollViewProxy* scrollProxy =
-          self.currentWebState->GetWebViewProxy().scrollViewProxy;
-      CGPoint scrollOffset = scrollProxy.contentOffset;
-      scrollOffset.y += toolbarHeight;
-      scrollProxy.contentOffset = scrollOffset;
-
-      // TODO(crbug.com/1155536): Inform FullscreenController about these
-      // changes and allow it to calculate the correct overall contentInset.
-      UIEdgeInsets contentInset = scrollProxy.contentInset;
-      contentInset.top -= toolbarHeight;
-      scrollProxy.contentInset = contentInset;
+      // Translate all web states' offset so web states from other tabs are also
+      // updated.
+      if (self.browser) {
+        WebStateList* webStateList = self.browser->GetWebStateList();
+        for (int index = 0; index < webStateList->count(); ++index) {
+          web::WebState* webState = webStateList->GetWebStateAt(index);
+          CRWWebViewScrollViewProxy* scrollProxy =
+              webState->GetWebViewProxy().scrollViewProxy;
+          CGPoint scrollOffset = scrollProxy.contentOffset;
+          scrollOffset.y += toolbarHeight;
+          scrollProxy.contentOffset = scrollOffset;
+        }
+      }
+      // This alerts the fullscreen controller to use the correct new content
+      // insets.
+      self.fullscreenController->FreezeToolbarHeight(true);
     }
   }
 }
@@ -2973,25 +2993,31 @@
     [self installFakeStatusBar];
     [self setupStatusBarLayout];
 
-    // See the comments in |-willAnimateViewReveal:| for the explantation of why
+    // See the comments in |-willAnimateViewReveal:| for the explanation of why
     // this is necessary.
-    if (self.currentWebState && ![self isNTPActiveForCurrentWebState] &&
-        fullscreen::features::ShouldUseSmoothScrolling()) {
+    if (fullscreen::features::ShouldUseSmoothScrolling()) {
+      self.viewTranslatedForSmoothScrolling = NO;
+      self.fullscreenController->FreezeToolbarHeight(false);
       CGFloat toolbarHeight = [self expandedTopToolbarHeight];
-      CGRect webStateViewFrame = UIEdgeInsetsInsetRect(
-          [self viewForWebState:self.currentWebState].frame,
-          UIEdgeInsetsMake(-toolbarHeight, 0, 0, 0));
-      [self viewForWebState:self.currentWebState].frame = webStateViewFrame;
+      if (self.currentWebState) {
+        CGRect webStateViewFrame = UIEdgeInsetsInsetRect(
+            [self viewForWebState:self.currentWebState].frame,
+            UIEdgeInsetsMake(-toolbarHeight, 0, 0, 0));
+        [self viewForWebState:self.currentWebState].frame = webStateViewFrame;
+      }
 
-      CRWWebViewScrollViewProxy* scrollProxy =
-          self.currentWebState->GetWebViewProxy().scrollViewProxy;
-      UIEdgeInsets contentInset = scrollProxy.contentInset;
-      contentInset.top += toolbarHeight;
-      scrollProxy.contentInset = contentInset;
+      if (self.browser) {
+        WebStateList* webStateList = self.browser->GetWebStateList();
+        for (int index = 0; index < webStateList->count(); ++index) {
+          web::WebState* webState = webStateList->GetWebStateAt(index);
+          CRWWebViewScrollViewProxy* scrollProxy =
+              webState->GetWebViewProxy().scrollViewProxy;
 
-      CGPoint scrollOffset = scrollProxy.contentOffset;
-      scrollOffset.y -= toolbarHeight;
-      scrollProxy.contentOffset = scrollOffset;
+          CGPoint scrollOffset = scrollProxy.contentOffset;
+          scrollOffset.y -= toolbarHeight;
+          scrollProxy.contentOffset = scrollOffset;
+        }
+      }
     }
   }
 }
diff --git a/ios/chrome/browser/ui/collection_view/collection_view_controller.mm b/ios/chrome/browser/ui/collection_view/collection_view_controller.mm
index a62dbb3..efe8c6ee 100644
--- a/ios/chrome/browser/ui/collection_view/collection_view_controller.mm
+++ b/ios/chrome/browser/ui/collection_view/collection_view_controller.mm
@@ -13,7 +13,7 @@
 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
 #import "ios/chrome/browser/ui/material_components/chrome_app_bar_view_controller.h"
 #import "ios/chrome/browser/ui/material_components/utils.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -65,10 +65,9 @@
   }
 
   // Suport dark mode.
-  self.collectionView.backgroundColor =
-      [UIColor colorNamed:kGroupedPrimaryBackgroundColor];
+  self.collectionView.backgroundColor = UIColor.cr_systemGroupedBackgroundColor;
   self.styler.cellBackgroundColor =
-      [UIColor colorNamed:kGroupedSecondaryBackgroundColor];
+      UIColor.cr_secondarySystemGroupedBackgroundColor;
 }
 
 - (void)contentSizeCategoryDidChange:(id)sender {
diff --git a/ios/chrome/browser/ui/fancy_ui/primary_action_button.mm b/ios/chrome/browser/ui/fancy_ui/primary_action_button.mm
index a87ae86..29a07fc 100644
--- a/ios/chrome/browser/ui/fancy_ui/primary_action_button.mm
+++ b/ios/chrome/browser/ui/fancy_ui/primary_action_button.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/fancy_ui/primary_action_button.h"
 
 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/pointer_interaction_util.h"
 
@@ -42,7 +43,7 @@
   }
 #endif  // defined(__IPHONE_13_4)
 
-  UIColor* hintColor = [UIColor colorNamed:kPrimaryBackgroundColor];
+  UIColor* hintColor = UIColor.cr_systemBackgroundColor;
   UIColor* inkColor = [UIColor colorWithWhite:1 alpha:0.2f];
   UIColor* backgroundColor = [UIColor colorNamed:kBlueColor];
   UIColor* disabledColor = [UIColor colorNamed:kDisabledTintColor];
diff --git a/ios/chrome/browser/ui/first_run/static_file_view_controller.mm b/ios/chrome/browser/ui/first_run/static_file_view_controller.mm
index b61597a..68958ff 100644
--- a/ios/chrome/browser/ui/first_run/static_file_view_controller.mm
+++ b/ios/chrome/browser/ui/first_run/static_file_view_controller.mm
@@ -13,7 +13,7 @@
 #import "ios/chrome/browser/ui/icons/chrome_icon.h"
 #import "ios/chrome/browser/ui/material_components/utils.h"
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/web/common/web_view_creation_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -70,7 +70,7 @@
                             cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                         timeoutInterval:60.0];
   [_webView loadRequest:request];
-  [_webView setBackgroundColor:[UIColor colorNamed:kPrimaryBackgroundColor]];
+  [_webView setBackgroundColor:UIColor.cr_systemBackgroundColor];
   [self.view addSubview:_webView];
 
   ConfigureAppBarViewControllerWithCardStyle(_appBarViewController);
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller.h b/ios/chrome/browser/ui/fullscreen/fullscreen_controller.h
index 89a4c71..5777475 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller.h
@@ -87,6 +87,8 @@
   // 1.0.
   virtual void ExitFullscreen() = 0;
 
+  virtual void FreezeToolbarHeight(bool freeze_toolbar_height) = 0;
+
   // Force horizontal content resize, when content isn't tracking resize by
   // itself.
   virtual void ResizeHorizontalViewport() = 0;
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h
index de79087..495bf30 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.h
@@ -48,6 +48,7 @@
   void EnterFullscreen() override;
   void ExitFullscreen() override;
   void ResizeHorizontalViewport() override;
+  void FreezeToolbarHeight(bool freeze_toolbar_height) override;
 
  private:
 
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
index 9148bf6..a226ac2 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_controller_impl.mm
@@ -177,3 +177,7 @@
   // two relayouts.
   mediator_.ResizeHorizontalInsets();
 }
+
+void FullscreenControllerImpl::FreezeToolbarHeight(bool freeze_toolbar_height) {
+  model_.SetFreezeToolbarHeight(freeze_toolbar_height);
+}
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model.h b/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
index 3b2d83b..cd4ee0e7 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model.h
@@ -46,7 +46,7 @@
 
   // Returns the difference between the max and min toolbar heights.
   CGFloat toolbar_height_delta() const {
-    return expanded_toolbar_height_ - collapsed_toolbar_height_;
+    return GetExpandedToolbarHeight() - GetCollapsedToolbarHeight();
   }
 
   // Returns whether the page content is tall enough for the toolbar to be
@@ -57,7 +57,7 @@
 
   // Whether the view is scrolled all the way to the top.
   bool is_scrolled_to_top() const {
-    return y_content_offset_ <= -expanded_toolbar_height_;
+    return y_content_offset_ <= -GetExpandedToolbarHeight();
   }
 
   // Whether the view is scrolled all the way to the bottom.
@@ -79,8 +79,8 @@
   // Returns the toolbar insets at |progress|.
   UIEdgeInsets GetToolbarInsetsAtProgress(CGFloat progress) const {
     return UIEdgeInsetsMake(
-        collapsed_toolbar_height_ +
-            progress * (expanded_toolbar_height_ - collapsed_toolbar_height_),
+        GetCollapsedToolbarHeight() + progress * (GetExpandedToolbarHeight() -
+                                                  GetCollapsedToolbarHeight()),
         0, progress * bottom_toolbar_height_, 0);
   }
 
@@ -157,6 +157,9 @@
   void SetWebViewSafeAreaInsets(UIEdgeInsets safe_area_insets);
   UIEdgeInsets GetWebViewSafeAreaInsets() const;
 
+  void SetFreezeToolbarHeight(bool freeze_toolbar_height);
+  bool GetFreezeToolbarHeight() const;
+
  private:
   // Returns how a scroll to the current |y_content_offset_| from |from_offset|
   // should be handled.
@@ -234,6 +237,7 @@
   UIEdgeInsets safe_area_insets_ = UIEdgeInsetsZero;
   // The number of FullscreenModelObserver callbacks currently being executed.
   size_t observer_callback_count_ = 0;
+  bool freeze_toolbar_height_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(FullscreenModel);
 };
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm
index e24cfa52..74f4bb3 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model.mm
@@ -79,7 +79,7 @@
 }
 
 void FullscreenModel::SetCollapsedToolbarHeight(CGFloat height) {
-  if (AreCGFloatsEqual(collapsed_toolbar_height_, height))
+  if (AreCGFloatsEqual(GetCollapsedToolbarHeight(), height))
     return;
   DCHECK_GE(height, 0.0);
   collapsed_toolbar_height_ = height;
@@ -91,11 +91,11 @@
 }
 
 CGFloat FullscreenModel::GetCollapsedToolbarHeight() const {
-  return collapsed_toolbar_height_;
+  return GetFreezeToolbarHeight() ? 0 : collapsed_toolbar_height_;
 }
 
 void FullscreenModel::SetExpandedToolbarHeight(CGFloat height) {
-  if (AreCGFloatsEqual(expanded_toolbar_height_, height))
+  if (AreCGFloatsEqual(GetExpandedToolbarHeight(), height))
     return;
   DCHECK_GE(height, 0.0);
   expanded_toolbar_height_ = height;
@@ -107,7 +107,7 @@
 }
 
 CGFloat FullscreenModel::GetExpandedToolbarHeight() const {
-  return expanded_toolbar_height_;
+  return GetFreezeToolbarHeight() ? 0 : expanded_toolbar_height_;
 }
 
 void FullscreenModel::SetBottomToolbarHeight(CGFloat height) {
@@ -247,6 +247,22 @@
   return safe_area_insets_;
 }
 
+void FullscreenModel::SetFreezeToolbarHeight(bool freeze_toolbar_height) {
+  if (freeze_toolbar_height_ == freeze_toolbar_height) {
+    return;
+  }
+  freeze_toolbar_height_ = freeze_toolbar_height;
+  base_offset_ = NAN;
+  ScopedIncrementer toolbar_height_incrementer(&observer_callback_count_);
+  for (auto& observer : observers_) {
+    observer.FullscreenModelToolbarHeightsUpdated(this);
+  }
+}
+
+bool FullscreenModel::GetFreezeToolbarHeight() const {
+  return freeze_toolbar_height_;
+}
+
 FullscreenModel::ScrollAction FullscreenModel::ActionForScrollFromOffset(
     CGFloat from_offset) const {
   // Update the base offset but don't recalculate progress if:
@@ -309,7 +325,7 @@
     // When Smooth Scrolling is disabled, the scroll view can sometimes be
     // resized to account for the viewport insets after the page has been
     // rendered, so account for the maximum toolbar insets in the threshold.
-    disabling_threshold += expanded_toolbar_height_ + bottom_toolbar_height_;
+    disabling_threshold += GetExpandedToolbarHeight() + bottom_toolbar_height_;
   } else {
     // After reloads, pages whose viewports fit the screen are sometimes resized
     // to account for the safe area insets.  Adding these to the threshold helps
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_model_unittest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_model_unittest.mm
index 0e7b994..f066e91 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_model_unittest.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_model_unittest.mm
@@ -314,3 +314,16 @@
   EXPECT_FALSE(model().is_scrolled_to_top());
   EXPECT_TRUE(model().is_scrolled_to_bottom());
 }
+
+// Tests that when the toolbar height is frozen, setting the height doesn't
+// change the returned height until the toolbar is unfrozen.
+TEST_F(FullscreenModelTest, FreezeToolbarHeight) {
+  model().SetFreezeToolbarHeight(true);
+  EXPECT_EQ(model().GetExpandedToolbarHeight(), 0);
+
+  model().SetExpandedToolbarHeight(100);
+  EXPECT_EQ(model().GetExpandedToolbarHeight(), 0);
+
+  model().SetFreezeToolbarHeight(false);
+  EXPECT_EQ(model().GetExpandedToolbarHeight(), 100);
+}
diff --git a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h
index c157f265..02a711be 100644
--- a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h
+++ b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.h
@@ -41,6 +41,7 @@
   void EnterFullscreen() override;
   void ExitFullscreen() override;
   void ResizeHorizontalViewport() override;
+  void FreezeToolbarHeight(bool freeze_toolbar_height) override;
 
   // Calls FullscreenViewportInsetRangeChanged() on observers.
   void OnFullscreenViewportInsetRangeChanged(UIEdgeInsets min_viewport_insets,
diff --git a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.mm b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.mm
index 78f30cf7..62206ea2 100644
--- a/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.mm
+++ b/ios/chrome/browser/ui/fullscreen/test/test_fullscreen_controller.mm
@@ -126,3 +126,9 @@
 void TestFullscreenController::ResizeHorizontalViewport() {
   // NOOP in tests.
 }
+
+void TestFullscreenController::FreezeToolbarHeight(bool freeze_toolbar_height) {
+  if (model_) {
+    model_->SetFreezeToolbarHeight(freeze_toolbar_height);
+  }
+}
diff --git a/ios/chrome/browser/ui/material_components/utils.mm b/ios/chrome/browser/ui/material_components/utils.mm
index b9c6a6d..bee7e27 100644
--- a/ios/chrome/browser/ui/material_components/utils.mm
+++ b/ios/chrome/browser/ui/material_components/utils.mm
@@ -15,7 +15,6 @@
 
 #include "base/mac/foundation_util.h"
 #import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -45,7 +44,7 @@
   viewController.headerView.shiftBehavior =
       MDCFlexibleHeaderShiftBehaviorDisabled;
   viewController.headerView.backgroundColor =
-      [UIColor colorNamed:kSecondaryBackgroundColor];
+      UIColor.cr_secondarySystemBackgroundColor;
   viewController.navigationBar.tintColor = UIColor.cr_labelColor;
   viewController.navigationBar.titleAlignment =
       MDCNavigationBarTitleAlignmentLeading;
diff --git a/ios/chrome/browser/ui/page_info/page_info_view_controller.mm b/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
index 059d1c0..ad13ac93 100644
--- a/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
+++ b/ios/chrome/browser/ui/page_info/page_info_view_controller.mm
@@ -68,9 +68,9 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
+  self.navigationItem.titleView =
+      [self titleViewLabelForURL:self.pageInfoSecurityDescription.siteURL];
   self.title = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_SITE_INFORMATION);
-  self.navigationItem.prompt = self.pageInfoSecurityDescription.siteURL;
-  self.navigationController.navigationBar.prefersLargeTitles = NO;
   self.tableView.accessibilityIdentifier = kPageInfoViewAccessibilityIdentifier;
 
   UIBarButtonItem* dismissButton = [[UIBarButtonItem alloc]
@@ -147,4 +147,17 @@
   [self.handler showSecurityHelpPage];
 }
 
+#pragma mark - Private
+
+// Returns the navigationItem titleView for |siteURL|.
+- (UILabel*)titleViewLabelForURL:(NSString*)siteURL {
+  UILabel* labelURL = [[UILabel alloc] init];
+  labelURL.lineBreakMode = NSLineBreakByTruncatingHead;
+  labelURL.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
+  labelURL.text = siteURL;
+  labelURL.adjustsFontSizeToFitWidth = YES;
+  labelURL.minimumScaleFactor = 0.7;
+  return labelURL;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/scanner/camera_controller.mm b/ios/chrome/browser/ui/scanner/camera_controller.mm
index 0ee910c..201492b2 100644
--- a/ios/chrome/browser/ui/scanner/camera_controller.mm
+++ b/ios/chrome/browser/ui/scanner/camera_controller.mm
@@ -8,6 +8,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/notreached.h"
 #include "base/strings/stringprintf.h"
+#include "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/common/ios_app_bundle_id_prefix_buildflags.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -329,8 +330,7 @@
 }
 
 - (AVCaptureVideoOrientation)videoOrientationForCurrentInterfaceOrientation {
-  UIInterfaceOrientation orientation =
-      [[UIApplication sharedApplication] statusBarOrientation];
+  UIInterfaceOrientation orientation = GetInterfaceOrientation();
   switch (orientation) {
     case UIInterfaceOrientationUnknown:
       return AVCaptureVideoOrientationPortrait;
diff --git a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
index 46a28fc..31fdb75c 100644
--- a/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
+++ b/ios/chrome/browser/ui/send_tab_to_self/send_tab_to_self_table_view_controller.mm
@@ -19,6 +19,7 @@
 #include "ios/chrome/browser/ui/table_view/cells/table_view_url_item.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -81,9 +82,8 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-  self.view.backgroundColor = [UIColor colorNamed:kPrimaryBackgroundColor];
-  self.styler.cellBackgroundColor =
-      [UIColor colorNamed:kPrimaryBackgroundColor];
+  self.view.backgroundColor = UIColor.cr_systemBackgroundColor;
+  self.styler.cellBackgroundColor = UIColor.cr_systemBackgroundColor;
   self.tableView.sectionHeaderHeight = 0;
   self.tableView.sectionFooterHeight = 0;
   [self.tableView
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller.mm
index d8ce2948..dea998e 100644
--- a/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/autofill/autofill_add_credit_card_view_controller.mm
@@ -16,6 +16,7 @@
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
 #import "ios/chrome/browser/ui/table_view/table_view_utils.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
@@ -87,8 +88,7 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  self.view.backgroundColor =
-      [UIColor colorNamed:kGroupedPrimaryBackgroundColor];
+  self.view.backgroundColor = UIColor.cr_systemGroupedBackgroundColor;
   self.tableView.accessibilityIdentifier = kAddCreditCardViewID;
 
   self.navigationItem.title = l10n_util::GetNSString(
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
index e45bdec..2e822f9 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
@@ -139,10 +139,10 @@
 
   if (!base::FeatureList::IsEnabled(kSettingsRefresh)) {
     self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+    self.styler.cellBackgroundColor = UIColor.cr_systemBackgroundColor;
+    self.styler.tableViewBackgroundColor = UIColor.cr_systemBackgroundColor;
     self.tableView.accessibilityIdentifier =
         kClearBrowsingDataViewAccessibilityIdentifier;
-    self.styler.tableViewBackgroundColor =
-        [UIColor colorNamed:kPrimaryBackgroundColor];
     self.tableView.backgroundColor = self.styler.tableViewBackgroundColor;
 
     // TableView configuration
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/time_range_selector_table_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/time_range_selector_table_view_controller.mm
index 09d8f10..7002d7d 100644
--- a/ios/chrome/browser/ui/settings/clear_browsing_data/time_range_selector_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/clear_browsing_data/time_range_selector_table_view_controller.mm
@@ -15,7 +15,6 @@
 #import "ios/chrome/browser/ui/table_view/table_view_utils.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
-#import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
@@ -79,8 +78,7 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
   if (!base::FeatureList::IsEnabled(kSettingsRefresh)) {
-    self.styler.tableViewBackgroundColor =
-        [UIColor colorNamed:kPrimaryBackgroundColor];
+    self.styler.tableViewBackgroundColor = UIColor.cr_systemBackgroundColor;
     self.tableView.backgroundColor = self.styler.tableViewBackgroundColor;
   }
   [self loadModel];
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
index 7fa7884..befb055 100644
--- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -25,6 +25,7 @@
 #include "ios/chrome/browser/ui/ui_feature_flags.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/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -288,7 +289,7 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
 
-  self.view.backgroundColor = [UIColor colorNamed:kPrimaryBackgroundColor];
+  self.view.backgroundColor = UIColor.cr_systemBackgroundColor;
 
   if (base::FeatureList::IsEnabled(kSettingsRefresh)) {
     self.navigationBar.translucent = NO;
diff --git a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
index f20c692..29cbff5 100644
--- a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
@@ -117,7 +117,7 @@
 - (void)viewDidLoad {
   if (!base::FeatureList::IsEnabled(kSettingsRefresh)) {
     self.styler.tableViewBackgroundColor =
-        [UIColor colorNamed:kGroupedPrimaryBackgroundColor];
+        UIColor.cr_systemGroupedBackgroundColor;
   }
   UIBarButtonItem* flexibleSpace = [[UIBarButtonItem alloc]
       initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
@@ -128,7 +128,7 @@
 
   [super viewDidLoad];
   self.styler.cellBackgroundColor =
-      [UIColor colorNamed:kGroupedSecondaryBackgroundColor];
+      UIColor.cr_secondarySystemGroupedBackgroundColor;
   self.styler.cellTitleColor = UIColor.cr_labelColor;
   self.tableView.estimatedSectionHeaderHeight = kEstimatedHeaderFooterHeight;
   self.tableView.estimatedRowHeight = kSettingsCellDefaultHeight;
diff --git a/ios/chrome/browser/ui/table_view/chrome_table_view_styler.mm b/ios/chrome/browser/ui/table_view/chrome_table_view_styler.mm
index 6ee04a2..fb2725c 100644
--- a/ios/chrome/browser/ui/table_view/chrome_table_view_styler.mm
+++ b/ios/chrome/browser/ui/table_view/chrome_table_view_styler.mm
@@ -6,6 +6,7 @@
 
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -19,10 +20,9 @@
     if (base::FeatureList::IsEnabled(kSettingsRefresh)) {
       _tableViewBackgroundColor =
           [UIColor colorNamed:kSecondaryBackgroundColor];
-      _cellBackgroundColor =
-          [UIColor colorNamed:kGroupedSecondaryBackgroundColor];
+      _cellBackgroundColor = UIColor.cr_secondarySystemGroupedBackgroundColor;
     } else {
-      _tableViewBackgroundColor = [UIColor colorNamed:kPrimaryBackgroundColor];
+      _tableViewBackgroundColor = UIColor.cr_systemBackgroundColor;
     }
   }
   return self;
diff --git a/ios/chrome/browser/ui/table_view/table_view_navigation_controller.mm b/ios/chrome/browser/ui/table_view/table_view_navigation_controller.mm
index 08c4369..416f3ef 100644
--- a/ios/chrome/browser/ui/table_view/table_view_navigation_controller.mm
+++ b/ios/chrome/browser/ui/table_view/table_view_navigation_controller.mm
@@ -8,6 +8,7 @@
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_styler.h"
 #include "ios/chrome/browser/ui/ui_feature_flags.h"
+#import "ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -42,10 +43,9 @@
     self.toolbar.barTintColor = [UIColor colorNamed:kSecondaryBackgroundColor];
     self.view.backgroundColor = [UIColor colorNamed:kSecondaryBackgroundColor];
   } else {
-    self.navigationBar.barTintColor =
-        [UIColor colorNamed:kPrimaryBackgroundColor];
-    self.toolbar.barTintColor = [UIColor colorNamed:kPrimaryBackgroundColor];
-    self.view.backgroundColor = [UIColor colorNamed:kPrimaryBackgroundColor];
+    self.navigationBar.barTintColor = UIColor.cr_systemBackgroundColor;
+    self.toolbar.barTintColor = UIColor.cr_systemBackgroundColor;
+    self.view.backgroundColor = UIColor.cr_systemBackgroundColor;
   }
 }
 
diff --git a/ios/chrome/browser/ui/thumb_strip/BUILD.gn b/ios/chrome/browser/ui/thumb_strip/BUILD.gn
index 931de5a..84f20fd 100644
--- a/ios/chrome/browser/ui/thumb_strip/BUILD.gn
+++ b/ios/chrome/browser/ui/thumb_strip/BUILD.gn
@@ -51,6 +51,7 @@
     "//base",
     "//base/test:test_support",
     "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/fullscreen:feature_flags",
     "//ios/chrome/test:eg_test_support+eg2",
     "//ios/chrome/test/earl_grey:eg_test_support+eg2",
     "//ios/testing/earl_grey:eg_test_support+eg2",
diff --git a/ios/chrome/browser/ui/thumb_strip/thumb_strip_egtest.mm b/ios/chrome/browser/ui/thumb_strip/thumb_strip_egtest.mm
index d317681..dc426c8 100644
--- a/ios/chrome/browser/ui/thumb_strip/thumb_strip_egtest.mm
+++ b/ios/chrome/browser/ui/thumb_strip/thumb_strip_egtest.mm
@@ -4,6 +4,7 @@
 
 #include "base/ios/ios_util.h"
 #import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/ui/fullscreen/fullscreen_features.h"
 #import "ios/chrome/browser/ui/thumb_strip/thumb_strip_feature.h"
 #import "ios/chrome/browser/ui/ui_feature_flags.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -55,6 +56,8 @@
   // See crbug.com/1143299.
   if (base::ios::IsRunningOnIOS13OrLater()) {
     config.features_enabled.push_back(kExpandedTabStrip);
+    config.features_disabled.push_back(
+        fullscreen::features::kSmoothScrollingDefault);
   }
   return config;
 }
diff --git a/ios/chrome/browser/ui/util/keyboard_observer_helper.mm b/ios/chrome/browser/ui/util/keyboard_observer_helper.mm
index 5deb1b8..dd36ff15 100644
--- a/ios/chrome/browser/ui/util/keyboard_observer_helper.mm
+++ b/ios/chrome/browser/ui/util/keyboard_observer_helper.mm
@@ -118,11 +118,13 @@
 
 - (void)keyboardWillHide:(NSNotification*)notification {
   self.keyboardOnScreen = NO;
+#if !defined(__IPHONE_13_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
   dispatch_async(dispatch_get_main_queue(), ^{
     if (self.keyboardOnScreen) {
       [self.consumer keyboardDidStayOnScreen];
     }
   });
+#endif
 }
 
 #pragma mark - Private
diff --git a/ios/chrome/browser/web/error_page_egtest.mm b/ios/chrome/browser/web/error_page_egtest.mm
index 5ff05c3a..4771c75 100644
--- a/ios/chrome/browser/web/error_page_egtest.mm
+++ b/ios/chrome/browser/web/error_page_egtest.mm
@@ -101,6 +101,27 @@
   [ChromeEarlGrey waitForWebStateContainingText:"bar"];
 }
 
+// Loads the URL which fails to load, then sucessfully navigates back/forward to
+// the page.
+- (void)testNavigateForwardToErrorPage {
+  self.serverRespondsWithContent = YES;
+  [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo-query?bar")];
+  [ChromeEarlGrey waitForWebStateContainingText:"bar"];
+
+  // No response leads to ERR_CONNECTION_CLOSED error.
+  self.serverRespondsWithContent = NO;
+  [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo-query?foo")];
+  [ChromeEarlGrey waitForWebStateContainingText:GetErrorMessage()];
+
+  self.serverRespondsWithContent = YES;
+  [ChromeEarlGrey goBack];
+  [ChromeEarlGrey waitForWebStateContainingText:"bar"];
+
+  // Navigate forward to the error page, which should load without errors.
+  [ChromeEarlGrey goForward];
+  [ChromeEarlGrey waitForWebStateContainingText:"foo"];
+}
+
 // Loads the URL which fails to load, then sucessfully reloads the page.
 - (void)testReloadErrorPage {
   // No response leads to ERR_CONNECTION_CLOSED error.
diff --git a/ios/chrome/browser/web_state_list/BUILD.gn b/ios/chrome/browser/web_state_list/BUILD.gn
index f78f880..a0cab940 100644
--- a/ios/chrome/browser/web_state_list/BUILD.gn
+++ b/ios/chrome/browser/web_state_list/BUILD.gn
@@ -69,6 +69,7 @@
     "//ios/chrome/browser/main:public",
     "//ios/chrome/browser/sessions:restoration_agent",
     "//ios/chrome/browser/sessions:restoration_observer",
+    "//ios/chrome/browser/ui/util",
     "//ios/web/public",
   ]
   frameworks = [ "Foundation.framework" ]
diff --git a/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.mm b/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.mm
index c30f0db..bc61828 100644
--- a/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.mm
+++ b/ios/chrome/browser/web_state_list/web_state_list_metrics_browser_agent.mm
@@ -14,6 +14,7 @@
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "ios/chrome/browser/crash_report/crash_loop_detection_util.h"
 #import "ios/chrome/browser/sessions/session_restoration_browser_agent.h"
+#include "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #include "ios/chrome/browser/web_state_list/session_metrics.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #include "ios/web/public/browser_state.h"
@@ -157,7 +158,7 @@
 void WebStateListMetricsBrowserAgent::PageLoaded(
     web::WebState* web_state,
     web::PageLoadCompletionStatus load_completion_status) {
-  switch ([[UIApplication sharedApplication] statusBarOrientation]) {
+  switch (GetInterfaceOrientation()) {
     case UIInterfaceOrientationPortrait:
     case UIInterfaceOrientationPortraitUpsideDown:
       UMA_HISTOGRAM_BOOLEAN("Tab.PageLoadInPortrait", YES);
diff --git a/ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h b/ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h
index e95226e..c4026725 100644
--- a/ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h
+++ b/ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.h
@@ -13,6 +13,16 @@
 // TODO (crbug.com/981889): Remove along with iOS 12.
 @interface UIColor (CRSemanticColors)
 
+// System Background Color
+@property(class, nonatomic, readonly) UIColor* cr_systemBackgroundColor;
+@property(class, nonatomic, readonly)
+    UIColor* cr_secondarySystemBackgroundColor;
+
+// System Grouped Background Colors
+@property(class, nonatomic, readonly) UIColor* cr_systemGroupedBackgroundColor;
+@property(class, nonatomic, readonly)
+    UIColor* cr_secondarySystemGroupedBackgroundColor;
+
 // Label Colors
 @property(class, nonatomic, readonly) UIColor* cr_labelColor;
 @property(class, nonatomic, readonly) UIColor* cr_secondaryLabelColor;
diff --git a/ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.mm b/ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.mm
index 70f20f4..39ced8a 100644
--- a/ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.mm
+++ b/ios/chrome/common/ui/colors/UIColor+cr_semantic_colors.mm
@@ -10,6 +10,42 @@
 
 @implementation UIColor (CRSemanticColors)
 
+#pragma mark - System Background Colors
+
++ (UIColor*)cr_systemBackgroundColor {
+  if (@available(iOS 13, *)) {
+    return UIColor.systemBackgroundColor;
+  }
+  return UIColor.whiteColor;
+}
+
++ (UIColor*)cr_secondarySystemBackgroundColor {
+  if (@available(iOS 13, *)) {
+    return UIColor.secondarySystemBackgroundColor;
+  }
+  // This is the value for secondarySystemBackgroundColor in light mode.
+  return [UIColor colorWithRed:244 / (CGFloat)0xFF
+                         green:244 / (CGFloat)0xFF
+                          blue:248 / (CGFloat)0xFF
+                         alpha:1];
+}
+
+#pragma mark - System Grouped Background Colors
+
++ (UIColor*)cr_systemGroupedBackgroundColor {
+  if (@available(iOS 13, *)) {
+    return UIColor.systemGroupedBackgroundColor;
+  }
+  return UIColor.groupTableViewBackgroundColor;
+}
+
++ (UIColor*)cr_secondarySystemGroupedBackgroundColor {
+  if (@available(iOS 13, *)) {
+    return UIColor.secondarySystemGroupedBackgroundColor;
+  }
+  return UIColor.whiteColor;
+}
+
 #pragma mark - Label Colors
 
 + (UIColor*)cr_labelColor {
diff --git a/ios/chrome/common/ui/colors/resources/BUILD.gn b/ios/chrome/common/ui/colors/resources/BUILD.gn
index 59ca80c..c6935c0 100644
--- a/ios/chrome/common/ui/colors/resources/BUILD.gn
+++ b/ios/chrome/common/ui/colors/resources/BUILD.gn
@@ -27,12 +27,9 @@
     ":grey_700_color",
     ":grey_800_color",
     ":grey_900_color",
-    ":grouped_primary_background_color",
-    ":grouped_secondary_background_color",
     ":mdc_ink_color",
     ":mdc_secondary_ink_color",
     ":placeholder_image_tint_color",
-    ":primary_background_color",
     ":red_color",
     ":red_dark_color",
     ":scrim_background_color",
@@ -226,15 +223,3 @@
 colorset("secondary_background_color") {
   sources = [ "secondary_background_color.colorset/Contents.json" ]
 }
-
-colorset("primary_background_color") {
-  sources = [ "primary_background_color.colorset/Contents.json" ]
-}
-
-colorset("grouped_secondary_background_color") {
-  sources = [ "grouped_secondary_background_color.colorset/Contents.json" ]
-}
-
-colorset("grouped_primary_background_color") {
-  sources = [ "grouped_primary_background_color.colorset/Contents.json" ]
-}
diff --git a/ios/chrome/common/ui/colors/resources/grouped_primary_background_color.colorset/Contents.json b/ios/chrome/common/ui/colors/resources/grouped_primary_background_color.colorset/Contents.json
deleted file mode 100644
index f1f5bfa..0000000
--- a/ios/chrome/common/ui/colors/resources/grouped_primary_background_color.colorset/Contents.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "info": {
-    "version": 1,
-    "author": "xcode"
-  },
-  "colors": [
-    {
-      "idiom": "universal",
-      "color": {
-        "color-space": "display-p3",
-        "components": {
-          "red": "0xF1",
-          "alpha": "1.000",
-          "blue": "0xF4",
-          "green": "0xF3"
-        }
-      }
-    },
-    {
-      "idiom": "universal",
-      "appearances": [
-        {
-          "appearance": "luminosity",
-          "value": "dark"
-        }
-      ],
-      "color": {
-        "color-space": "display-p3",
-        "components": {
-          "red": "0x20",
-          "alpha": "1.000",
-          "blue": "0x24",
-          "green": "0x21"
-        }
-      }
-    }
-  ]
-}
\ No newline at end of file
diff --git a/ios/chrome/common/ui/colors/resources/grouped_secondary_background_color.colorset/Contents.json b/ios/chrome/common/ui/colors/resources/grouped_secondary_background_color.colorset/Contents.json
deleted file mode 100644
index 0d5ff736..0000000
--- a/ios/chrome/common/ui/colors/resources/grouped_secondary_background_color.colorset/Contents.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "info": {
-    "version": 1,
-    "author": "xcode"
-  },
-  "colors": [
-    {
-      "idiom": "universal",
-      "color": {
-        "color-space": "display-p3",
-        "components": {
-          "red": "0xFF",
-          "alpha": "1.000",
-          "blue": "0xFF",
-          "green": "0xFF"
-        }
-      }
-    },
-    {
-      "idiom": "universal",
-      "appearances": [
-        {
-          "appearance": "luminosity",
-          "value": "dark"
-        }
-      ],
-      "color": {
-        "color-space": "display-p3",
-        "components": {
-          "red": "0x35",
-          "alpha": "1.000",
-          "blue": "0x39",
-          "green": "0x37"
-        }
-      }
-    }
-  ]
-}
\ No newline at end of file
diff --git a/ios/chrome/common/ui/colors/resources/primary_background_color.colorset/Contents.json b/ios/chrome/common/ui/colors/resources/primary_background_color.colorset/Contents.json
deleted file mode 100644
index a66621c8..0000000
--- a/ios/chrome/common/ui/colors/resources/primary_background_color.colorset/Contents.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
-  "info": {
-    "version": 1,
-    "author": "xcode"
-  },
-  "colors": [
-    {
-      "idiom": "universal",
-      "color": {
-        "color-space": "display-p3",
-        "components": {
-          "red": "0xFF",
-          "alpha": "1.000",
-          "blue": "0xFF",
-          "green": "0xFF"
-        }
-      }
-    },
-    {
-      "idiom": "universal",
-      "appearances": [
-        {
-          "appearance": "luminosity",
-          "value": "dark"
-        }
-      ],
-      "color": {
-        "color-space": "display-p3",
-        "components": {
-          "red": "0x20",
-          "alpha": "1.000",
-          "blue": "0x24",
-          "green": "0x21"
-        }
-      }
-    }
-  ]
-}
\ No newline at end of file
diff --git a/ios/chrome/common/ui/colors/semantic_color_names.h b/ios/chrome/common/ui/colors/semantic_color_names.h
index d66f212..ce13cb6 100644
--- a/ios/chrome/common/ui/colors/semantic_color_names.h
+++ b/ios/chrome/common/ui/colors/semantic_color_names.h
@@ -14,10 +14,6 @@
 extern NSString* const kDisabledTintColor;
 // Background color used in the rounded squares behind favicons.
 extern NSString* const kFaviconBackgroundColor;
-// Primary grouped background color.
-extern NSString* const kGroupedPrimaryBackgroundColor;
-// Secondary grouped background color.
-extern NSString* const kGroupedSecondaryBackgroundColor;
 // Ink color for an MDC button.
 extern NSString* const kMDCInkColor;
 // Ink color for a secondary style MDC button (button with transparent
@@ -25,8 +21,6 @@
 extern NSString* const kMDCSecondaryInkColor;
 // Color used to tint placeholder images and icons.
 extern NSString* const kPlaceholderImageTintColor;
-// Primary background color.
-extern NSString* const kPrimaryBackgroundColor;
 extern NSString* const kScrimBackgroundColor;
 // Secondary background color.
 extern NSString* const kSecondaryBackgroundColor;
diff --git a/ios/chrome/common/ui/colors/semantic_color_names.mm b/ios/chrome/common/ui/colors/semantic_color_names.mm
index 22d8e42..74d8960 100644
--- a/ios/chrome/common/ui/colors/semantic_color_names.mm
+++ b/ios/chrome/common/ui/colors/semantic_color_names.mm
@@ -13,14 +13,9 @@
 NSString* const kCloseButtonColor = @"close_button_color";
 NSString* const kDisabledTintColor = @"disabled_tint_color";
 NSString* const kFaviconBackgroundColor = @"favicon_background_color";
-NSString* const kGroupedPrimaryBackgroundColor =
-    @"grouped_primary_background_color";
-NSString* const kGroupedSecondaryBackgroundColor =
-    @"grouped_secondary_background_color";
 NSString* const kMDCInkColor = @"mdc_ink_color";
 NSString* const kMDCSecondaryInkColor = @"mdc_secondary_ink_color";
 NSString* const kPlaceholderImageTintColor = @"placeholder_image_tint_color";
-NSString* const kPrimaryBackgroundColor = @"primary_background_color";
 NSString* const kScrimBackgroundColor = @"scrim_background_color";
 NSString* const kSecondaryBackgroundColor = @"secondary_background_color";
 NSString* const kSeparatorColor = @"separator_color";
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index f0f8507..fc2cbc7c 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-df0c5af3c01e211e892dfa7202abbd97923f42c8
\ No newline at end of file
+d35eaa7488bab6b76b1e2cbcc9a79813ee380c2c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index eb566b2..61f2d16 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-479a5cdbe222d03b81f46ad57802cbabe8d85142
\ No newline at end of file
+2a03c2ba1c14490e70ec58326debef58891bec38
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index d96943a..96c2f6cca 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-6304c71654ccc27f78f770f348c50d9343ecc014
\ No newline at end of file
+32575c02bd0089bc0c16100ab1851cc6006b7fa0
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index c550be42..b38e9cc 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-fd43d9d0bdf5b608a75bde850d771f551137244e
\ No newline at end of file
+1f1b56e21aa2be7a392e03cd1bd0cedadc11b6ef
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index 3e33356..4592cd67 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-2f23f78fcdc06953931ecefc1454539aa68c35cb
\ No newline at end of file
+123b2c131d978512700b9560bfef6a298ad14567
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 601bf34..eb1a1e5e 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-2cc660c0e8df8ad03130b81e30dc6cd86ca71d59
\ No newline at end of file
+671a339206310696c276bdb0be0cb912d87585eb
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 69ab0b8..33896b8 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-d1a3948e1471f0d3eafef0d042257fce2406cbe1
\ No newline at end of file
+8ecc877a8f1ea68933b0f3253f7060f5c3e8f123
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index 643fca35..8e73bca 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-cad27834b77d0b6819db2ecd2a9db07b1c386f06
\ No newline at end of file
+49c07444d6372ee239e8a74f7573cbfc07f4cc8b
\ No newline at end of file
diff --git a/ios/public/provider/chrome/browser/signin/chrome_identity.h b/ios/public/provider/chrome/browser/signin/chrome_identity.h
index c755a07..3b7028a 100644
--- a/ios/public/provider/chrome/browser/signin/chrome_identity.h
+++ b/ios/public/provider/chrome/browser/signin/chrome_identity.h
@@ -24,6 +24,10 @@
 // Could be nil if no full name has been fetched for this account yet.
 @property(strong, nonatomic, readonly) NSString* userFullName;
 
+// Returns the primary given name of the identity, usually the user's first
+// name. Could be nil if no name has been fetched for this account yet.
+@property(strong, nonatomic, readonly) NSString* userGivenName;
+
 // Cached Hashed Gaia ID. This is used to pass the currently signed in account
 // between apps.
 @property(strong, nonatomic, readonly) NSString* hashedGaiaID;
diff --git a/ios/public/provider/chrome/browser/signin/chrome_identity.mm b/ios/public/provider/chrome/browser/signin/chrome_identity.mm
index 6b6794f..fae2376 100644
--- a/ios/public/provider/chrome/browser/signin/chrome_identity.mm
+++ b/ios/public/provider/chrome/browser/signin/chrome_identity.mm
@@ -27,6 +27,11 @@
   return nil;
 }
 
+- (NSString*)userGivenName {
+  NOTREACHED();
+  return nil;
+}
+
 - (NSString*)hashedGaiaID {
   NOTREACHED();
   return nil;
diff --git a/ios/public/provider/chrome/browser/signin/fake_chrome_identity.h b/ios/public/provider/chrome/browser/signin/fake_chrome_identity.h
index 9085b568..bc0a810 100644
--- a/ios/public/provider/chrome/browser/signin/fake_chrome_identity.h
+++ b/ios/public/provider/chrome/browser/signin/fake_chrome_identity.h
@@ -12,6 +12,8 @@
 
 // Returns a ChromeIdentity based on |email|, |gaiaID| and |name|.
 // The |hashedGaiaID| property will be derived from |name|.
+// For simplicity, both |userGivenName| and |userFullName| properties use
+// |name|.
 + (FakeChromeIdentity*)identityWithEmail:(NSString*)email
                                   gaiaID:(NSString*)gaiaID
                                     name:(NSString*)name;
diff --git a/ios/public/provider/chrome/browser/signin/fake_chrome_identity.mm b/ios/public/provider/chrome/browser/signin/fake_chrome_identity.mm
index a588fc70..724ba23 100644
--- a/ios/public/provider/chrome/browser/signin/fake_chrome_identity.mm
+++ b/ios/public/provider/chrome/browser/signin/fake_chrome_identity.mm
@@ -14,6 +14,7 @@
 NSString* const kCoderUserEmailKey = @"UserEmail";
 NSString* const kCoderGaiaIDKey = @"GaiaID";
 NSString* const kCoderUserFullNameKey = @"UserFullName";
+NSString* const kCoderUserGivenNameKey = @"UserGivenName";
 NSString* const kCoderHashedGaiaIDKey = @"HashedGaiaID";
 }  // namespace
 
@@ -21,6 +22,7 @@
   NSString* _userEmail;
   NSString* _gaiaID;
   NSString* _userFullName;
+  NSString* _userGivenName;
   NSString* _hashedGaiaID;
 }
 
@@ -40,6 +42,7 @@
     _userEmail = [email copy];
     _gaiaID = [gaiaID copy];
     _userFullName = [name copy];
+    _userGivenName = [name copy];
     _hashedGaiaID = [NSString stringWithFormat:@"%@_hashID", name];
   }
   return self;
@@ -57,6 +60,10 @@
   return _userFullName;
 }
 
+- (NSString*)userGivenName {
+  return _userGivenName;
+}
+
 - (NSString*)hashedGaiaID {
   return _hashedGaiaID;
 }
@@ -79,6 +86,7 @@
     return [_userEmail isEqualToString:other.userEmail] &&
            [_gaiaID isEqualToString:other.gaiaID] &&
            [_userFullName isEqualToString:other.userFullName] &&
+           [_userGivenName isEqualToString:other.userGivenName] &&
            [_hashedGaiaID isEqualToString:other.hashedGaiaID];
   }
   return NO;
@@ -94,6 +102,7 @@
   [coder encodeObject:_userEmail forKey:kCoderUserEmailKey];
   [coder encodeObject:_gaiaID forKey:kCoderGaiaIDKey];
   [coder encodeObject:_userFullName forKey:kCoderUserFullNameKey];
+  [coder encodeObject:_userGivenName forKey:kCoderUserGivenNameKey];
   [coder encodeObject:_hashedGaiaID forKey:kCoderHashedGaiaIDKey];
 }
 
@@ -105,6 +114,8 @@
                                   forKey:kCoderGaiaIDKey];
     _userFullName = [coder decodeObjectOfClass:[NSString class]
                                         forKey:kCoderUserFullNameKey];
+    _userGivenName = [coder decodeObjectOfClass:[NSString class]
+                                         forKey:kCoderUserGivenNameKey];
     _hashedGaiaID = [coder decodeObjectOfClass:[NSString class]
                                         forKey:kCoderHashedGaiaIDKey];
   }
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index a0258e2..c4b52c0 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -478,11 +478,11 @@
   ]
 
   sources = [
+    "web_state/ui/crw_html_element_fetch_request_unittest.mm",
     "web_state/ui/crw_web_controller_unittest.mm",
     "web_state/ui/crw_web_view_content_view_unittest.mm",
     "web_state/ui/crw_web_view_proxy_impl_unittest.mm",
     "web_state/ui/crw_web_view_scroll_view_proxy_unittest.mm",
-    "web_state/ui/html_element_fetch_request_unittest.mm",
     "web_state/ui/web_view_js_utils_unittest.mm",
     "web_state/ui/wk_content_rule_list_util_unittest.mm",
     "web_state/ui/wk_web_view_configuration_provider_unittest.mm",
@@ -527,6 +527,7 @@
     ":web",
     "//base/test:test_support",
     "//ios/net",
+    "//ios/testing:block_swizzler",
     "//ios/testing:embedded_test_server_support",
     "//ios/testing:http_server_bundle_data",
     "//ios/web:resources_grit",
@@ -554,6 +555,8 @@
     "//ios/web/test:test_constants",
     "//ios/web/test:test_support",
     "//ios/web/web_state",
+    "//ios/web/web_state:context_menu",
+    "//ios/web/web_state/ui:crw_context_menu_controller",
     "//mojo/core/embedder",
     "//net:test_support",
     "//services/network/public/cpp",
@@ -578,6 +581,7 @@
     "web_state/error_page_inttest.mm",
     "web_state/http_auth_inttest.mm",
     "web_state/keep_render_process_alive_inttest.mm",
+    "web_state/ui/crw_context_menu_element_fetcher_inttest.mm",
     "web_state/web_state_observer_inttest.mm",
     "webui/web_ui_inttest.mm",
     "webui/web_ui_mojo_inttest.mm",
diff --git a/ios/web/common/uikit_ui_util.h b/ios/web/common/uikit_ui_util.h
index 3ae8df7..9c3e40b 100644
--- a/ios/web/common/uikit_ui_util.h
+++ b/ios/web/common/uikit_ui_util.h
@@ -11,4 +11,8 @@
 // Use only if the context of which window doesn't matter.
 UIWindow* GetAnyKeyWindow();
 
+// Returns interface orientation for the current window, returned by
+// GetAnyKeyWindow().
+UIInterfaceOrientation GetInterfaceOrientation();
+
 #endif  // IOS_WEB_COMMON_UIKIT_UI_UTIL_H_
diff --git a/ios/web/common/uikit_ui_util.mm b/ios/web/common/uikit_ui_util.mm
index 0ecd597..855669f 100644
--- a/ios/web/common/uikit_ui_util.mm
+++ b/ios/web/common/uikit_ui_util.mm
@@ -22,3 +22,11 @@
   return nil;
 #endif
 }
+
+UIInterfaceOrientation GetInterfaceOrientation() {
+#if !defined(__IPHONE_13_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
+  return [[UIApplication sharedApplication] statusBarOrientation];
+#else
+  return GetAnyKeyWindow().windowScene.interfaceOrientation;
+#endif
+}
diff --git a/ios/web/web_state/context_menu_params_utils.h b/ios/web/web_state/context_menu_params_utils.h
index 44008f4..6a29c33 100644
--- a/ios/web/web_state/context_menu_params_utils.h
+++ b/ios/web/web_state/context_menu_params_utils.h
@@ -11,10 +11,9 @@
 
 namespace web {
 
-// Returns true if the |element| dictionary contains enough information to
-// present a context menu. (A valid url for either kContextMenuElementHyperlink
-// or kContextMenuElementSource must exist in the dicitionary.)
-BOOL CanShowContextMenuForElementDictionary(NSDictionary* element);
+// Returns true if the |params| contain enough information to present a context
+// menu. (A valid url for either link_url or src_url must exist in the params.)
+BOOL CanShowContextMenuForParams(const ContextMenuParams& params);
 
 // creates a ContextMenuParams from a NSDictionary representing an HTML element.
 // The fields "href", "src", "title", "referrerPolicy" and "innerText" will
diff --git a/ios/web/web_state/context_menu_params_utils.mm b/ios/web/web_state/context_menu_params_utils.mm
index 50dc3cb..67e2d9a1 100644
--- a/ios/web/web_state/context_menu_params_utils.mm
+++ b/ios/web/web_state/context_menu_params_utils.mm
@@ -68,13 +68,11 @@
 
 namespace web {
 
-BOOL CanShowContextMenuForElementDictionary(NSDictionary* element) {
-  NSString* href = element[kContextMenuElementHyperlink];
-  if (GURL(base::SysNSStringToUTF8(href)).is_valid()) {
+BOOL CanShowContextMenuForParams(const ContextMenuParams& params) {
+  if (params.link_url.is_valid()) {
     return YES;
   }
-  NSString* src = element[kContextMenuElementSource];
-  if (GURL(base::SysNSStringToUTF8(src)).is_valid()) {
+  if (params.src_url.is_valid()) {
     return YES;
   }
   return NO;
diff --git a/ios/web/web_state/context_menu_params_utils_unittest.mm b/ios/web/web_state/context_menu_params_utils_unittest.mm
index e3622652..9df07ca0 100644
--- a/ios/web/web_state/context_menu_params_utils_unittest.mm
+++ b/ios/web/web_state/context_menu_params_utils_unittest.mm
@@ -113,55 +113,49 @@
   EXPECT_EQ(params.menu_title_origin, ContextMenuTitleOrigin::kURL);
 }
 
-// Tests that a context menu will not be shown for an empty element dictionary.
+// Tests that a context menu will not be shown for empty params.
 TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestEmptyDictionary) {
-  EXPECT_FALSE(CanShowContextMenuForElementDictionary(@{}));
-}
-
-// Tests that a context menu will not be shown for an element dictionary with
-// only a request id.
-TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestRequestIdOnly) {
-  EXPECT_FALSE(CanShowContextMenuForElementDictionary(
-      @{kContextMenuElementRequestId : @"kContextMenuElementRequestId"}));
+  EXPECT_FALSE(CanShowContextMenuForParams(ContextMenuParams()));
 }
 
 // Tests that a context menu will be shown for a link.
 TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestHyperlink) {
-  EXPECT_TRUE(CanShowContextMenuForElementDictionary(@{
-    kContextMenuElementHyperlink : @"http://example.com",
-    kContextMenuElementInnerText : @"Click me."
-  }));
+  ContextMenuParams params;
+  params.link_url = GURL("http://example.com");
+  params.link_text = @"Click me.";
+  EXPECT_TRUE(CanShowContextMenuForParams(params));
 }
 
 // Tests that a context menu will not be shown for an invalid link.
 TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestInvalidHyperlink) {
-  EXPECT_FALSE(CanShowContextMenuForElementDictionary(
-      @{kContextMenuElementHyperlink : @"invalid_url"}));
+  ContextMenuParams params;
+  params.link_url = GURL("invalid_url");
+  EXPECT_FALSE(CanShowContextMenuForParams(params));
 }
 
 // Tests that a context menu will be shown for an image.
 TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestImageWithTitle) {
-  EXPECT_TRUE(CanShowContextMenuForElementDictionary(@{
-    kContextMenuElementSource : @"http://example.com/image.jpeg",
-    kContextMenuElementTitle : @"Image"
-  }));
+  ContextMenuParams params;
+  params.src_url = GURL("http://example.com/image.jpeg");
+  params.menu_title = @"Image";
+  EXPECT_TRUE(CanShowContextMenuForParams(params));
 }
 
 // Tests that a context menu will not be shown for an image with an invalid
 // source url.
 TEST_F(ContextMenuParamsUtilsTest,
        CanShowContextMenuTestImageWithInvalidSource) {
-  EXPECT_FALSE(CanShowContextMenuForElementDictionary(@{
-    kContextMenuElementSource : @"invalid_url",
-  }));
+  ContextMenuParams params;
+  params.src_url = GURL("invalid_url");
+  EXPECT_FALSE(CanShowContextMenuForParams(params));
 }
 
 // Tests that a context menu will be shown for a linked image.
 TEST_F(ContextMenuParamsUtilsTest, CanShowContextMenuTestLinkedImage) {
-  EXPECT_TRUE(CanShowContextMenuForElementDictionary(@{
-    kContextMenuElementHyperlink : @"http://example.com",
-    kContextMenuElementSource : @"http://example.com/image.jpeg"
-  }));
+  ContextMenuParams params;
+  params.link_url = GURL("http://example.com");
+  params.src_url = GURL("http://example.com/image.jpeg");
+  EXPECT_TRUE(CanShowContextMenuForParams(params));
 }
 
 // Tests that the menu title prepends the element's alt text if it is an image
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn
index 0f0eca6..9f14e25 100644
--- a/ios/web/web_state/ui/BUILD.gn
+++ b/ios/web/web_state/ui/BUILD.gn
@@ -104,10 +104,12 @@
   ]
 
   sources = [
+    "crw_context_menu_element_fetcher.h",
+    "crw_context_menu_element_fetcher.mm",
+    "crw_html_element_fetch_request.h",
+    "crw_html_element_fetch_request.mm",
     "crw_legacy_context_menu_controller.h",
     "crw_legacy_context_menu_controller.mm",
-    "html_element_fetch_request.h",
-    "html_element_fetch_request.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/web/web_state/ui/crw_context_menu_element_fetcher.h b/ios/web/web_state/ui/crw_context_menu_element_fetcher.h
new file mode 100644
index 0000000..6896d0a
--- /dev/null
+++ b/ios/web/web_state/ui/crw_context_menu_element_fetcher.h
@@ -0,0 +1,33 @@
+// Copyright 2020 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_WEB_WEB_STATE_UI_CRW_CONTEXT_MENU_ELEMENT_FETCHER_H_
+#define IOS_WEB_WEB_STATE_UI_CRW_CONTEXT_MENU_ELEMENT_FETCHER_H_
+
+#import <Foundation/Foundation.h>
+#import <WebKit/WebKit.h>
+
+namespace web {
+struct ContextMenuParams;
+class WebState;
+}
+
+// Class handling the fetching information about DOM element in a specific
+// position.
+@interface CRWContextMenuElementFetcher : NSObject
+
+- (instancetype)initWithWebView:(WKWebView*)webView
+                       webState:(web::WebState*)webState;
+
+// Asynchronously fetches information about DOM element for the given |point|
+// (in the scroll view coordinates). |handler| can not be nil.
+- (void)fetchDOMElementAtPoint:(CGPoint)point
+             completionHandler:(void (^)(const web::ContextMenuParams&))handler;
+
+// Cancels all the fetches current in progress.
+- (void)cancelFetches;
+
+@end
+
+#endif  // IOS_WEB_WEB_STATE_UI_CRW_CONTEXT_MENU_ELEMENT_FETCHER_H_
diff --git a/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm b/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm
new file mode 100644
index 0000000..464ecf6
--- /dev/null
+++ b/ios/web/web_state/ui/crw_context_menu_element_fetcher.mm
@@ -0,0 +1,157 @@
+// Copyright 2020 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/web/web_state/ui/crw_context_menu_element_fetcher.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "base/unguessable_token.h"
+#include "base/values.h"
+#import "ios/web/js_messaging/crw_wk_script_message_router.h"
+#import "ios/web/public/js_messaging/web_frame.h"
+#import "ios/web/public/js_messaging/web_frame_util.h"
+#import "ios/web/public/ui/context_menu_params.h"
+#import "ios/web/public/web_state.h"
+#import "ios/web/public/web_state_observer_bridge.h"
+#import "ios/web/web_state/context_menu_constants.h"
+#import "ios/web/web_state/context_menu_params_utils.h"
+#import "ios/web/web_state/ui/crw_html_element_fetch_request.h"
+#import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// Javascript function name to obtain element details at a point.
+const char kFindElementAtPointFunctionName[] = "findElementAtPoint";
+
+// JavaScript message handler name installed in WKWebView for found element
+// response.
+NSString* const kFindElementResultHandlerName = @"FindElementResultHandler";
+}  // namespace
+
+@interface CRWContextMenuElementFetcher () <CRWWebStateObserver> {
+  std::unique_ptr<web::WebStateObserverBridge> _observer;
+}
+
+@property(nonatomic, readonly, weak) WKWebView* webView;
+
+@property(nonatomic, assign) web::WebState* webState;
+
+// Details for currently in progress element fetches. The objects are
+// instances of CRWHTMLElementFetchRequest and are keyed by a unique requestId
+// string.
+@property(nonatomic, strong) NSMutableDictionary* pendingElementFetchRequests;
+
+@end
+
+@implementation CRWContextMenuElementFetcher
+
+- (instancetype)initWithWebView:(WKWebView*)webView
+                       webState:(web::WebState*)webState {
+  self = [super init];
+  if (self) {
+    _pendingElementFetchRequests = [[NSMutableDictionary alloc] init];
+
+    _webView = webView;
+
+    _webState = webState;
+    _observer = std::make_unique<web::WebStateObserverBridge>(self);
+    webState->AddObserver(_observer.get());
+
+    // Listen for fetched element response.
+    web::WKWebViewConfigurationProvider& configurationProvider =
+        web::WKWebViewConfigurationProvider::FromBrowserState(
+            webState->GetBrowserState());
+    CRWWKScriptMessageRouter* messageRouter =
+        configurationProvider.GetScriptMessageRouter();
+    __weak __typeof(self) weakSelf = self;
+    [messageRouter
+        setScriptMessageHandler:^(WKScriptMessage* message) {
+          [weakSelf didReceiveScriptMessage:message];
+        }
+                           name:kFindElementResultHandlerName
+                        webView:webView];
+  }
+  return self;
+}
+
+- (void)dealloc {
+  if (self.webState)
+    self.webState->RemoveObserver(_observer.get());
+}
+
+- (void)fetchDOMElementAtPoint:(CGPoint)point
+             completionHandler:
+                 (void (^)(const web::ContextMenuParams&))handler {
+  if (!self.webState) {
+    return;
+  }
+  web::WebFrame* frame = GetMainFrame(self.webState);
+  if (!frame) {
+    // A WebFrame may not exist for certain types of content, like PDFs.
+    return;
+  }
+  DCHECK(handler);
+
+  std::string requestID = base::UnguessableToken::Create().ToString();
+  CRWHTMLElementFetchRequest* fetchRequest =
+      [[CRWHTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
+  _pendingElementFetchRequests[base::SysUTF8ToNSString(requestID)] =
+      fetchRequest;
+
+  CGSize webViewContentSize = self.webView.scrollView.contentSize;
+
+  std::vector<base::Value> args;
+  args.push_back(base::Value(requestID));
+  args.push_back(base::Value(point.x));
+  args.push_back(base::Value(point.y));
+  args.push_back(base::Value(webViewContentSize.width));
+  args.push_back(base::Value(webViewContentSize.height));
+  frame->CallJavaScriptFunction(std::string(kFindElementAtPointFunctionName),
+                                args);
+}
+
+- (void)cancelFetches {
+  for (CRWHTMLElementFetchRequest* fetchRequest in _pendingElementFetchRequests
+           .allValues) {
+    [fetchRequest invalidate];
+  }
+}
+
+#pragma mark - Private
+
+// Called when web controller receives a new message from the web page.
+- (void)didReceiveScriptMessage:(WKScriptMessage*)message {
+  NSMutableDictionary* response =
+      [[NSMutableDictionary alloc] initWithDictionary:message.body];
+
+  NSString* requestID = response[web::kContextMenuElementRequestId];
+  CRWHTMLElementFetchRequest* fetchRequest =
+      _pendingElementFetchRequests[requestID];
+  if (!fetchRequest) {
+    // Do not process the message if a fetch request with a matching |requestID|
+    // was not found. This ensures that the response matches a request made by
+    // this instance.
+    return;
+  }
+
+  web::ContextMenuParams params =
+      web::ContextMenuParamsFromElementDictionary(response);
+  params.is_main_frame = message.frameInfo.mainFrame;
+  params.view = self.webView;
+
+  [_pendingElementFetchRequests removeObjectForKey:requestID];
+  [fetchRequest runHandlerWithResponse:params];
+}
+
+#pragma mark - CRWWebStateObserver
+
+- (void)webStateDestroyed:(web::WebState*)webState {
+  if (self.webState)
+    self.webState->RemoveObserver(_observer.get());
+  self.webState = nullptr;
+}
+
+@end
diff --git a/ios/web/web_state/ui/crw_context_menu_element_fetcher_inttest.mm b/ios/web/web_state/ui/crw_context_menu_element_fetcher_inttest.mm
new file mode 100644
index 0000000..2c41d62
--- /dev/null
+++ b/ios/web/web_state/ui/crw_context_menu_element_fetcher_inttest.mm
@@ -0,0 +1,124 @@
+// Copyright 2020 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/web/web_state/ui/crw_context_menu_element_fetcher.h"
+
+#import <WebKit/WebKit.h>
+
+#include "base/macros.h"
+#import "base/test/ios/wait_util.h"
+#include "ios/testing/scoped_block_swizzler.h"
+#import "ios/web/public/test/web_view_content_test_util.h"
+#import "ios/web/test/web_test_with_web_controller.h"
+#import "ios/web/web_state/context_menu_constants.h"
+#import "ios/web/web_state/ui/crw_legacy_context_menu_controller.h"
+#import "ios/web/web_state/ui/crw_web_controller.h"
+#import "ios/web/web_state/web_state_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+// This is the timeout used while waiting for the JavaScript to complete. The
+// general kWaitForJSCompletionTimeout isn't used because one of the test is
+// supposed to not complete and so will wait for the whole duration of the
+// timeout. This constant is smaller to speed tests up. This constant is used in
+// both the "successful" JavaScript calls and the "failing" JavaScript calls. It
+// ensures that in the context of this test, the JavaScript completes in the
+// given timespan (and so it ensures that if the "failing" JavaScript tests
+// pass, it is because the JavaScript isn't called and not because it didn't
+// have time to complete).
+const CGFloat kFetcherJSTimeout = 1.0;
+}  // namespace
+
+namespace web {
+class CRWContextMenuElementFetcherTest : public WebTestWithWebController {
+ public:
+  CRWContextMenuElementFetcherTest() {
+    // Disable the existing long press handling to avoid duplicating message
+    // handlers.
+    swizzler_ = std::make_unique<ScopedBlockSwizzler>(
+        [CRWLegacyContextMenuController class],
+        @selector(initWithWebView:webState:), ^id(id self) {
+          return nil;
+        });
+  }
+
+  void SetUp() override {
+    WebTestWithWebState::SetUp();
+    WKWebView* web_view = [web_controller() ensureWebViewCreated];
+    fetcher_ =
+        [[CRWContextMenuElementFetcher alloc] initWithWebView:web_view
+                                                     webState:web_state()];
+  }
+
+  // Loads a page containing a link and waits until the link is present on the
+  // page, making sure that the HTML is correctly injected.
+  bool LoadHtmlPage() WARN_UNUSED_RESULT {
+    NSString* html =
+        @"<html><head>"
+         "<style>body { font-size:14em; }</style>"
+         "<meta name=\"viewport\" content=\"user-scalable=no, width=100\">"
+         "</head><body><p><a id=\"linkID\" "
+         "href=\"http://destination/\">link</a></p></body></html>";
+
+    LoadHtml(html);
+
+    bool element_present = test::WaitForWebViewContainingElement(
+        web_state(), [ElementSelector selectorWithElementID:"linkID"]);
+    if (element_present) {
+      // If the element is present, we still need a small delay to let all the
+      // scripts be injected in the page.
+      base::test::ios::SpinRunLoopWithMinDelay(
+          base::TimeDelta::FromSecondsD(0.5));
+    }
+    return element_present;
+  }
+
+  CRWContextMenuElementFetcher* GetFetcher() { return fetcher_; }
+
+ private:
+  std::unique_ptr<ScopedBlockSwizzler> swizzler_;
+  CRWContextMenuElementFetcher* fetcher_;
+};
+
+// Tests that the fetcher is triggering a callback for one element.
+TEST_F(CRWContextMenuElementFetcherTest, FetchOneElement) {
+  EXPECT_TRUE(LoadHtmlPage());
+
+  CRWContextMenuElementFetcher* fetcher = GetFetcher();
+  __block bool callback_called = false;
+  [fetcher fetchDOMElementAtPoint:CGPointMake(10, 10)
+                completionHandler:^(const web::ContextMenuParams&) {
+                  callback_called = true;
+                }];
+
+  EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(kFetcherJSTimeout, ^{
+    return callback_called;
+  }));
+}
+
+// Tests that cancelled fetches don't trigger callback.
+TEST_F(CRWContextMenuElementFetcherTest, CancelFetch) {
+  EXPECT_TRUE(LoadHtmlPage());
+
+  CRWContextMenuElementFetcher* fetcher = GetFetcher();
+  __block bool callback_called = false;
+  [fetcher fetchDOMElementAtPoint:CGPointMake(10, 10)
+                completionHandler:^(const web::ContextMenuParams&) {
+                  callback_called = true;
+                }];
+  [fetcher cancelFetches];
+
+  // The callback should never be called.
+  EXPECT_FALSE(
+      base::test::ios::WaitUntilConditionOrTimeout(kFetcherJSTimeout, ^{
+        return callback_called;
+      }));
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/ui/html_element_fetch_request.h b/ios/web/web_state/ui/crw_html_element_fetch_request.h
similarity index 68%
rename from ios/web/web_state/ui/html_element_fetch_request.h
rename to ios/web/web_state/ui/crw_html_element_fetch_request.h
index 9aa02d3..6d89648 100644
--- a/ios/web/web_state/ui/html_element_fetch_request.h
+++ b/ios/web/web_state/ui/crw_html_element_fetch_request.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_WEB_WEB_STATE_UI_HTML_ELEMENT_FETCH_REQUEST_H_
-#define IOS_WEB_WEB_STATE_UI_HTML_ELEMENT_FETCH_REQUEST_H_
+#ifndef IOS_WEB_WEB_STATE_UI_CRW_HTML_ELEMENT_FETCH_REQUEST_H_
+#define IOS_WEB_WEB_STATE_UI_CRW_HTML_ELEMENT_FETCH_REQUEST_H_
 
 #import <Foundation/Foundation.h>
 
@@ -11,8 +11,12 @@
 class TimeTicks;
 }  // namespace base
 
+namespace web {
+struct ContextMenuParams;
+}
+
 // Tracks request details for fetching attributes of an element.
-@interface HTMLElementFetchRequest : NSObject
+@interface CRWHTMLElementFetchRequest : NSObject
 
 // The time this object was created.
 @property(nonatomic, readonly) base::TimeTicks creationTime;
@@ -21,16 +25,17 @@
 // Designated initializer to create a new object with the given completion
 // handler |foundElementHandler|.
 - (instancetype)initWithFoundElementHandler:
-    (void (^)(NSDictionary*))foundElementHandler NS_DESIGNATED_INITIALIZER;
+    (void (^)(const web::ContextMenuParams&))foundElementHandler
+    NS_DESIGNATED_INITIALIZER;
 
 // Calls the |foundElementHandler| from the receiver's initializer with
 // |response| as the parameter. This method has no effect if |invalidate| has
 // been called.
-- (void)runHandlerWithResponse:(NSDictionary*)response;
+- (void)runHandlerWithResponse:(const web::ContextMenuParams&)response;
 // Removes the stored |foundElementHandler| from the receiver's initializer.
 // |runHandlerWithResponse:| will have no effect if called after |invalidate|.
 - (void)invalidate;
 
 @end
 
-#endif  // IOS_WEB_WEB_STATE_UI_HTML_ELEMENT_FETCH_REQUEST_H_
+#endif  // IOS_WEB_WEB_STATE_UI_CRW_HTML_ELEMENT_FETCH_REQUEST_H_
diff --git a/ios/web/web_state/ui/html_element_fetch_request.mm b/ios/web/web_state/ui/crw_html_element_fetch_request.mm
similarity index 65%
rename from ios/web/web_state/ui/html_element_fetch_request.mm
rename to ios/web/web_state/ui/crw_html_element_fetch_request.mm
index 0835371..209779e 100644
--- a/ios/web/web_state/ui/html_element_fetch_request.mm
+++ b/ios/web/web_state/ui/crw_html_element_fetch_request.mm
@@ -2,26 +2,28 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/web/web_state/ui/html_element_fetch_request.h"
+#import "ios/web/web_state/ui/crw_html_element_fetch_request.h"
 
 #include "base/time/time.h"
+#import "ios/web/public/ui/context_menu_params.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
 #endif
 
-@interface HTMLElementFetchRequest ()
+@interface CRWHTMLElementFetchRequest ()
 // Completion handler to call with found DOM element.
-@property(nonatomic, copy) void (^foundElementHandler)(NSDictionary*);
+@property(nonatomic, copy) void (^foundElementHandler)
+    (const web::ContextMenuParams&);
 @end
 
-@implementation HTMLElementFetchRequest
+@implementation CRWHTMLElementFetchRequest
 
 @synthesize creationTime = _creationTime;
 @synthesize foundElementHandler = _foundElementHandler;
 
 - (instancetype)initWithFoundElementHandler:
-    (void (^)(NSDictionary*))foundElementHandler {
+    (void (^)(const web::ContextMenuParams&))foundElementHandler {
   self = [super init];
   if (self) {
     _creationTime = base::TimeTicks::Now();
@@ -30,7 +32,7 @@
   return self;
 }
 
-- (void)runHandlerWithResponse:(NSDictionary*)response {
+- (void)runHandlerWithResponse:(const web::ContextMenuParams&)response {
   if (_foundElementHandler) {
     _foundElementHandler(response);
   }
diff --git a/ios/web/web_state/ui/crw_html_element_fetch_request_unittest.mm b/ios/web/web_state/ui/crw_html_element_fetch_request_unittest.mm
new file mode 100644
index 0000000..370cf69
--- /dev/null
+++ b/ios/web/web_state/ui/crw_html_element_fetch_request_unittest.mm
@@ -0,0 +1,67 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/web/web_state/ui/crw_html_element_fetch_request.h"
+
+#include "base/time/time.h"
+#import "ios/web/public/ui/context_menu_params.h"
+#import "ios/web/web_state/context_menu_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+
+using CRWHTMLElementFetchRequestTest = PlatformTest;
+
+// Tests that |creationTime| is set at CRWHTMLElementFetchRequest object
+// creation.
+TEST_F(CRWHTMLElementFetchRequestTest, CreationTime) {
+  CRWHTMLElementFetchRequest* request =
+      [[CRWHTMLElementFetchRequest alloc] initWithFoundElementHandler:nil];
+  base::TimeDelta delta = base::TimeTicks::Now() - request.creationTime;
+  // Validate that |request.creationTime| is "now", but only use second
+  // precision to avoid performance induced test flake.
+  EXPECT_GT(1, delta.InSeconds());
+}
+
+// Tests that |runHandlerWithResponse:| runs the handler from the object's
+// initializer with the expected |response|.
+TEST_F(CRWHTMLElementFetchRequestTest, RunHandler) {
+  __block bool handler_called = false;
+  __block web::ContextMenuParams received_params;
+  void (^handler)(const web::ContextMenuParams&) =
+      ^(const web::ContextMenuParams& params) {
+        handler_called = true;
+        received_params = params;
+      };
+  CRWHTMLElementFetchRequest* request =
+      [[CRWHTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
+  web::ContextMenuParams params = web::ContextMenuParams();
+  params.link_text = @"text";
+  [request runHandlerWithResponse:params];
+  EXPECT_TRUE(handler_called);
+  EXPECT_NSEQ(params.link_text, received_params.link_text);
+}
+
+// Tests that |runHandlerWithResponse:| does not run the handler from the
+// object's initializer if |invalidate| has been called.
+TEST_F(CRWHTMLElementFetchRequestTest, Invalidate) {
+  __block bool handler_called = false;
+  void (^handler)(const web::ContextMenuParams&) =
+      ^(const web::ContextMenuParams& params) {
+        handler_called = true;
+      };
+  CRWHTMLElementFetchRequest* request =
+      [[CRWHTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
+  [request invalidate];
+  [request runHandlerWithResponse:web::ContextMenuParams()];
+  EXPECT_FALSE(handler_called);
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/ui/crw_legacy_context_menu_controller.mm b/ios/web/web_state/ui/crw_legacy_context_menu_controller.mm
index a5065ff..6970ccc 100644
--- a/ios/web/web_state/ui/crw_legacy_context_menu_controller.mm
+++ b/ios/web/web_state/ui/crw_legacy_context_menu_controller.mm
@@ -11,21 +11,14 @@
 #include "base/ios/ios_util.h"
 #include "base/mac/foundation_util.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/strings/sys_string_conversions.h"
-#include "base/unguessable_token.h"
 #include "base/values.h"
-#import "ios/web/js_messaging/crw_wk_script_message_router.h"
-#import "ios/web/public/js_messaging/web_frame.h"
-#import "ios/web/public/js_messaging/web_frame_util.h"
 #import "ios/web/public/navigation/navigation_context.h"
 #import "ios/web/public/ui/context_menu_params.h"
 #include "ios/web/public/web_client.h"
 #import "ios/web/public/web_state.h"
 #import "ios/web/public/web_state_observer_bridge.h"
-#import "ios/web/web_state/context_menu_constants.h"
 #import "ios/web/web_state/context_menu_params_utils.h"
-#import "ios/web/web_state/ui/html_element_fetch_request.h"
-#import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
+#import "ios/web/web_state/ui/crw_context_menu_element_fetcher.h"
 #import "ios/web/web_state/web_state_impl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -54,13 +47,6 @@
   }
 }
 
-// Javascript function name to obtain element details at a point.
-const char kFindElementAtPointFunctionName[] = "findElementAtPoint";
-
-// JavaScript message handler name installed in WKWebView for found element
-// response.
-NSString* const kFindElementResultHandlerName = @"FindElementResultHandler";
-
 // Enum used to record element details fetched for the context menu.
 enum class ContextMenuElementFrame {
   // Recorded when the element was found in the main frame.
@@ -89,17 +75,6 @@
   kMaxValue = Cancel
 };
 
-// Struct to track the details of the element at |location| in |webView|.
-struct ContextMenuInfo {
-  // The location of the long press.
-  CGPoint location;
-  // True if the element is in the page's main frame, false if in an iframe.
-  BOOL is_main_frame;
-  // DOM element information. May contain the keys defined in
-  // ios/web/web_state/context_menu_constants.h. All values are strings.
-  NSDictionary* dom_element;
-};
-
 // Returns an array of gesture recognizers with |fragment| in it's description
 // and attached to a subview of |webView|.
 NSArray<UIGestureRecognizer*>* GestureRecognizersWithDescriptionFragment(
@@ -182,6 +157,8 @@
 // WebState associated with this controller.
 @property(nonatomic, assign) web::WebStateImpl* webState;
 
+@property(nonatomic, strong) CRWContextMenuElementFetcher* elementFetcher;
+
 // Called when the |_contextMenuRecognizer| finishes recognizing a long press.
 - (void)longPressDetectedByGestureRecognizer:
     (UIGestureRecognizer*)gestureRecognizer;
@@ -190,21 +167,12 @@
 // Called when the |_contextMenuRecognizer| changes.
 - (void)longPressGestureRecognizerChanged;
 // Show the context menu or allow the system default behavior based on the DOM
-// element details in |_contextMenuInfoForLastTouch.dom_element|.
+// element details in |contextMenuParams|.
 - (void)processReceivedDOMElement;
 // Called when the context menu must be shown.
 - (void)showContextMenu;
 // Cancels all touch events in the web view (long presses, tapping, scrolling).
 - (void)cancelAllTouches;
-// Asynchronously fetches information about DOM element for the given point (in
-// UIView coordinates). |handler| can not be nil. See
-// |_contextMenuInfoForLastTouch.dom_element| for element format description.
-- (void)fetchDOMElementAtPoint:(CGPoint)point
-             completionHandler:(void (^)(NSDictionary*))handler;
-// Sets the value of |_contextMenuInfoForLastTouch.dom_element|.
-- (void)setDOMElementForLastTouch:(NSDictionary*)element;
-// Called to process a message received from JavaScript.
-- (void)didReceiveScriptMessage:(WKScriptMessage*)message;
 // Cancels the display of the context menu and clears associated element fetch
 // request state.
 - (void)cancelContextMenuDisplay;
@@ -214,10 +182,8 @@
   std::unique_ptr<web::WebStateObserverBridge> _observer;
   // Long press recognizer that allows showing context menus.
   UILongPressGestureRecognizer* _contextMenuRecognizer;
-  // DOM element information for the point where the user made the last touch.
-  // Precalculation is necessary because retreiving DOM element relies on async
-  // API so element info can not be built on demand.
-  ContextMenuInfo _contextMenuInfoForLastTouch;
+  // Location of the last touch on the screen.
+  CGPoint _lastTouchLocation;
   // Whether or not the system cotext menu should be displayed. If not, custom
   // context menu should be displayed.
   BOOL _systemContextMenuEnabled;
@@ -229,10 +195,8 @@
   // |_contextMenuRecognizer| finished, but couldn't yet show the context menu
   // becuase the DOM element details were not yet available.
   BOOL _contextMenuNeedsDisplay;
-  // Details for currently in progress element fetches. The objects are
-  // instances of HTMLElementFetchRequest and are keyed by a unique requestId
-  // string.
-  NSMutableDictionary* _pendingElementFetchRequests;
+  // Parameters for the context menu, populated by the element fetcher.
+  base::Optional<web::ContextMenuParams> _contextMenuParams;
 }
 
 @synthesize webView = _webView;
@@ -243,7 +207,10 @@
   self = [super init];
   if (self) {
     _webView = webView;
-    _pendingElementFetchRequests = [[NSMutableDictionary alloc] init];
+
+    _elementFetcher =
+        [[CRWContextMenuElementFetcher alloc] initWithWebView:webView
+                                                     webState:webState];
 
     _webState = webState;
     _observer = std::make_unique<web::WebStateObserverBridge>(self);
@@ -273,20 +240,6 @@
     [_webView addGestureRecognizer:_contextMenuRecognizer];
 
     OverrideGestureRecognizers(_contextMenuRecognizer, _webView);
-
-    // Listen for fetched element response.
-    web::WKWebViewConfigurationProvider& configurationProvider =
-        web::WKWebViewConfigurationProvider::FromBrowserState(
-            webState->GetBrowserState());
-    CRWWKScriptMessageRouter* messageRouter =
-        configurationProvider.GetScriptMessageRouter();
-    __weak CRWLegacyContextMenuController* weakSelf = self;
-    [messageRouter
-        setScriptMessageHandler:^(WKScriptMessage* message) {
-          [weakSelf didReceiveScriptMessage:message];
-        }
-                           name:kFindElementResultHandlerName
-                        webView:webView];
   }
   return self;
 }
@@ -329,7 +282,7 @@
 }
 
 - (void)longPressGestureRecognizerBegan {
-  if (_contextMenuInfoForLastTouch.dom_element) {
+  if (_contextMenuParams.has_value()) {
     [self processReceivedDOMElement];
   } else {
     // Shows the context menu once the DOM element information is set.
@@ -340,7 +293,7 @@
 
 - (void)longPressGestureRecognizerChanged {
   if (!_contextMenuNeedsDisplay ||
-      CGPointEqualToPoint(_contextMenuInfoForLastTouch.location, CGPointZero)) {
+      CGPointEqualToPoint(_lastTouchLocation, CGPointZero)) {
     return;
   }
 
@@ -350,10 +303,8 @@
   // |_contextMenuNeedsDisplay| has already been set to True.
   CGPoint currentTouchLocation =
       [_contextMenuRecognizer locationInView:_webView];
-  float deltaX = std::abs(_contextMenuInfoForLastTouch.location.x -
-                          currentTouchLocation.x);
-  float deltaY = std::abs(_contextMenuInfoForLastTouch.location.y -
-                          currentTouchLocation.y);
+  float deltaX = std::abs(_lastTouchLocation.x - currentTouchLocation.x);
+  float deltaY = std::abs(_lastTouchLocation.y - currentTouchLocation.y);
   if (deltaX > kLongPressMoveDeltaPixels ||
       deltaY > kLongPressMoveDeltaPixels) {
     [self cancelContextMenuDisplay];
@@ -361,8 +312,9 @@
 }
 
 - (void)processReceivedDOMElement {
-  BOOL canShowContextMenu = web::CanShowContextMenuForElementDictionary(
-      _contextMenuInfoForLastTouch.dom_element);
+  BOOL canShowContextMenu =
+      _contextMenuParams.has_value() &&
+      web::CanShowContextMenuForParams(_contextMenuParams.value());
   if (!canShowContextMenu) {
     // There is no link or image under user's gesture. Do not cancel all touches
     // to allow system text selection UI.
@@ -374,30 +326,25 @@
   // intentionally suppress system context menu UI.
   [self cancelAllTouches];
 
-  _contextMenuInfoForLastTouch.location =
-      [_contextMenuRecognizer locationInView:_webView];
+  _lastTouchLocation = [_contextMenuRecognizer locationInView:_webView];
   [self showContextMenu];
 }
 
 - (void)showContextMenu {
-  if (!self.webState) {
+  if (!self.webState || !_contextMenuParams.has_value()) {
     return;
   }
 
   // Log if the element is in the main frame or a child frame.
   UMA_HISTOGRAM_ENUMERATION("ContextMenu.DOMElementFrame",
-                            (_contextMenuInfoForLastTouch.is_main_frame
+                            (_contextMenuParams.value().is_main_frame
                                  ? ContextMenuElementFrame::MainFrame
                                  : ContextMenuElementFrame::Iframe),
                             ContextMenuElementFrame::Count);
 
-  web::ContextMenuParams params = web::ContextMenuParamsFromElementDictionary(
-      _contextMenuInfoForLastTouch.dom_element);
-  params.view = _webView;
-  params.location = _contextMenuInfoForLastTouch.location;
-  params.is_main_frame = _contextMenuInfoForLastTouch.is_main_frame;
+  _contextMenuParams.value().location = _lastTouchLocation;
 
-  self.webState->HandleContextMenu(params);
+  self.webState->HandleContextMenu(_contextMenuParams.value());
 }
 
 - (void)cancelAllTouches {
@@ -415,8 +362,9 @@
   }
 }
 
-- (void)setDOMElementForLastTouch:(NSDictionary*)element {
-  _contextMenuInfoForLastTouch.dom_element = [element copy];
+// Sets the value of |params|.
+- (void)setParamsForLastTouch:(const web::ContextMenuParams&)params {
+  _contextMenuParams = params;
   if (_contextMenuNeedsDisplay) {
     _contextMenuNeedsDisplay = NO;
     UMA_HISTOGRAM_ENUMERATION(kContextMenuDelayedElementDetailsHistogram,
@@ -425,33 +373,14 @@
   }
 }
 
-- (void)didReceiveScriptMessage:(WKScriptMessage*)message {
-  NSMutableDictionary* response =
-      [[NSMutableDictionary alloc] initWithDictionary:message.body];
-  _contextMenuInfoForLastTouch.is_main_frame = message.frameInfo.mainFrame;
-  NSString* requestID = response[web::kContextMenuElementRequestId];
-  HTMLElementFetchRequest* fetchRequest =
-      _pendingElementFetchRequests[requestID];
-  // Do not process the message if a fetch request with a matching |requestID|
-  // was not found. This ensures that the response matches a request made by
-  // this CRWLegacyContextMenuController instance.
-  if (fetchRequest) {
-    [_pendingElementFetchRequests removeObjectForKey:requestID];
-    [fetchRequest runHandlerWithResponse:response];
-  }
-}
-
 - (void)cancelContextMenuDisplay {
   if (_contextMenuNeedsDisplay) {
     UMA_HISTOGRAM_ENUMERATION(kContextMenuDelayedElementDetailsHistogram,
                               DelayedElementDetailsState::Cancel);
   }
   _contextMenuNeedsDisplay = NO;
-  _contextMenuInfoForLastTouch.location = CGPointZero;
-  for (HTMLElementFetchRequest* fetchRequest in _pendingElementFetchRequests
-           .allValues) {
-    [fetchRequest invalidate];
-  }
+  _lastTouchLocation = CGPointZero;
+  [self.elementFetcher cancelFetches];
 }
 
 #pragma mark -
@@ -481,14 +410,15 @@
   // touch. If there a link, the web controller will reject system's context
   // menu and show another one. If for some reason context menu info is not
   // fetched - system context menu will be shown.
-  [self setDOMElementForLastTouch:nil];
+  _contextMenuParams.reset();
   [self cancelContextMenuDisplay];
 
   __weak CRWLegacyContextMenuController* weakSelf = self;
-  [self fetchDOMElementAtPoint:[touch locationInView:_webView]
-             completionHandler:^(NSDictionary* element) {
-               [weakSelf setDOMElementForLastTouch:element];
-             }];
+  [self.elementFetcher
+      fetchDOMElementAtPoint:[touch locationInView:_webView.scrollView]
+           completionHandler:^(const web::ContextMenuParams& params) {
+             [weakSelf setParamsForLastTouch:params];
+           }];
   return YES;
 }
 
@@ -512,39 +442,6 @@
   return YES;
 }
 
-#pragma mark -
-#pragma mark Web Page Features
-
-- (void)fetchDOMElementAtPoint:(CGPoint)point
-             completionHandler:(void (^)(NSDictionary*))handler {
-  if (!self.webState) {
-    return;
-  }
-  web::WebFrame* frame = GetMainFrame(self.webState);
-  if (!frame) {
-    // A WebFrame may not exist for certain types of content, like PDFs.
-    return;
-  }
-  DCHECK(handler);
-
-  std::string requestID = base::UnguessableToken::Create().ToString();
-  HTMLElementFetchRequest* fetchRequest =
-      [[HTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
-  _pendingElementFetchRequests[base::SysUTF8ToNSString(requestID)] =
-      fetchRequest;
-
-  CGSize webViewContentSize = self.webScrollView.contentSize;
-
-  std::vector<base::Value> args;
-  args.push_back(base::Value(requestID));
-  args.push_back(base::Value(point.x + self.scrollPosition.x));
-  args.push_back(base::Value(point.y + self.scrollPosition.y));
-  args.push_back(base::Value(webViewContentSize.width));
-  args.push_back(base::Value(webViewContentSize.height));
-  frame->CallJavaScriptFunction(std::string(kFindElementAtPointFunctionName),
-                                args);
-}
-
 #pragma mark - CRWWebStateObserver
 
 - (void)webStateDestroyed:(web::WebState*)webState {
diff --git a/ios/web/web_state/ui/crw_wk_ui_handler.mm b/ios/web/web_state/ui/crw_wk_ui_handler.mm
index 931c18c..adf24de 100644
--- a/ios/web/web_state/ui/crw_wk_ui_handler.mm
+++ b/ios/web/web_state/ui/crw_wk_ui_handler.mm
@@ -168,6 +168,9 @@
                        }];
 }
 
+#if !defined(__IPHONE_13_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_13_0
+
+// TODO(crbug.com/1131852): Preview depracted is iOS13+
 - (BOOL)webView:(WKWebView*)webView
     shouldPreviewElement:(WKPreviewElementInfo*)elementInfo {
   return self.webStateImpl->ShouldPreviewLink(
@@ -211,6 +214,8 @@
                                      completionHandler);
 }
 
+#endif  // End of >iOS13 deprecated block.
+
 - (void)webView:(WKWebView*)webView
     contextMenuDidEndForElement:(WKContextMenuElementInfo*)elementInfo
     API_AVAILABLE(ios(13.0)) {
diff --git a/ios/web/web_state/ui/html_element_fetch_request_unittest.mm b/ios/web/web_state/ui/html_element_fetch_request_unittest.mm
deleted file mode 100644
index 58b4b563..0000000
--- a/ios/web/web_state/ui/html_element_fetch_request_unittest.mm
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/web/web_state/ui/html_element_fetch_request.h"
-
-#include "base/time/time.h"
-#import "ios/web/web_state/context_menu_constants.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/gtest_mac.h"
-#include "testing/platform_test.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace web {
-
-using HtmlElementFetchRequestTest = PlatformTest;
-
-// Tests that |creationTime| is set at HtmlElementFetchRequest object creation.
-TEST_F(HtmlElementFetchRequestTest, CreationTime) {
-  HTMLElementFetchRequest* request =
-      [[HTMLElementFetchRequest alloc] initWithFoundElementHandler:nil];
-  base::TimeDelta delta = base::TimeTicks::Now() - request.creationTime;
-  // Validate that |request.creationTime| is "now", but only use second
-  // precision to avoid performance induced test flake.
-  EXPECT_GT(1, delta.InSeconds());
-}
-
-// Tests that |runHandlerWithResponse:| runs the handler from the object's
-// initializer with the expected |response|.
-TEST_F(HtmlElementFetchRequestTest, RunHandler) {
-  __block bool handler_called = false;
-  __block NSDictionary* received_response = nil;
-  void (^handler)(NSDictionary*) = ^(NSDictionary* response) {
-    handler_called = true;
-    received_response = response;
-  };
-  HTMLElementFetchRequest* request =
-      [[HTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
-  NSDictionary* response = @{kContextMenuElementInnerText : @"text"};
-  [request runHandlerWithResponse:response];
-  EXPECT_TRUE(handler_called);
-  EXPECT_NSEQ(response, received_response);
-}
-
-// Tests that |runHandlerWithResponse:| does not run the handler from the
-// object's initializer if |invalidate| has been called.
-TEST_F(HtmlElementFetchRequestTest, Invalidate) {
-  __block bool handler_called = false;
-  void (^handler)(NSDictionary*) = ^(NSDictionary* response) {
-    handler_called = true;
-  };
-  HTMLElementFetchRequest* request =
-      [[HTMLElementFetchRequest alloc] initWithFoundElementHandler:handler];
-  [request invalidate];
-  [request runHandlerWithResponse:nil];
-  EXPECT_FALSE(handler_called);
-}
-
-}  // namespace web
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
index c37d30d..f616c17 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -70,9 +70,6 @@
   // Javascript autofill manager associated with |webState|.
   JsAutofillManager* _JSAutofillManager;
 
-  // Javascript suggestion manager associated with |webState|.
-  JsSuggestionManager* _JSSuggestionManager;
-
   // The |webState| which this autofill controller should observe.
   web::WebState* _webState;
 
@@ -95,7 +92,7 @@
   std::unique_ptr<autofill::FormActivityObserverBridge>
       _formActivityObserverBridge;
 
-  NSString* _lastFormActivityWebFrameID;
+  std::string _lastFormActivityWebFrameID;
   NSString* _lastFormActivityTypedValue;
   NSString* _lastFormActivityType;
   FormRendererId _lastFormActivityUniqueFormID;
@@ -110,7 +107,6 @@
                               autofillClient
             autofillAgent:(AutofillAgent*)autofillAgent
         JSAutofillManager:(JsAutofillManager*)JSAutofillManager
-      JSSuggestionManager:(JsSuggestionManager*)JSSuggestionManager
           passwordManager:(std::unique_ptr<password_manager::PasswordManager>)
                               passwordManager
     passwordManagerClient:
@@ -144,8 +140,6 @@
 
     _JSAutofillManager = JSAutofillManager;
 
-    _JSSuggestionManager = JSSuggestionManager;
-
     _passwordManagerClient = std::move(passwordManagerClient);
     _passwordManagerClient->set_bridge(self);
     _passwordManager = std::move(passwordManager);
@@ -301,21 +295,20 @@
 }
 
 - (void)focusPreviousField {
-  [_JSSuggestionManager
-      selectPreviousElementInFrameWithID:_lastFormActivityWebFrameID];
+  autofill::JsSuggestionManager::GetOrCreateForWebState(_webState)
+      ->SelectPreviousElementInFrameWithID(_lastFormActivityWebFrameID);
 }
 
 - (void)focusNextField {
-  [_JSSuggestionManager
-      selectNextElementInFrameWithID:_lastFormActivityWebFrameID];
+  autofill::JsSuggestionManager::GetOrCreateForWebState(_webState)
+      ->SelectNextElementInFrameWithID(_lastFormActivityWebFrameID);
 }
 
 - (void)checkIfPreviousAndNextFieldsAreAvailableForFocusWithCompletionHandler:
     (void (^)(BOOL previous, BOOL next))completionHandler {
-  [_JSSuggestionManager
-      fetchPreviousAndNextElementsPresenceInFrameWithID:
-          _lastFormActivityWebFrameID
-                                      completionHandler:completionHandler];
+  autofill::JsSuggestionManager::GetOrCreateForWebState(_webState)
+      ->FetchPreviousAndNextElementsPresenceInFrameWithID(
+          _lastFormActivityWebFrameID, base::BindOnce(completionHandler));
 }
 
 #pragma mark - CWVAutofillClientIOSBridge
@@ -493,7 +486,7 @@
   NSString* nsType = base::SysUTF8ToNSString(params.type);
   BOOL userInitiated = params.has_user_gesture;
 
-  _lastFormActivityWebFrameID = nsFrameID;
+  _lastFormActivityWebFrameID = GetWebFrameId(frame);
   _lastFormActivityTypedValue = nsValue;
   _lastFormActivityType = nsType;
   if (params.type == "focus") {
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller_internal.h b/ios/web_view/internal/autofill/cwv_autofill_controller_internal.h
index 8643f3a2..31f91166 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller_internal.h
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller_internal.h
@@ -37,7 +37,6 @@
 
 @class AutofillAgent;
 @class JsAutofillManager;
-@class JsSuggestionManager;
 @class SharedPasswordController;
 
 @interface CWVAutofillController () <AutofillDriverIOSBridge,
@@ -53,7 +52,6 @@
                               autofillClient
             autofillAgent:(AutofillAgent*)autofillAgent
         JSAutofillManager:(JsAutofillManager*)JSAutofillManager
-      JSSuggestionManager:(JsSuggestionManager*)JSSuggestionManager
           passwordManager:(std::unique_ptr<password_manager::PasswordManager>)
                               passwordManager
     passwordManagerClient:
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
index 8358b69..0d5c013 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
@@ -19,7 +19,6 @@
 #import "components/autofill/ios/browser/fake_autofill_agent.h"
 #import "components/autofill/ios/browser/fake_js_autofill_manager.h"
 #import "components/autofill/ios/browser/form_suggestion.h"
-#import "components/autofill/ios/browser/js_suggestion_manager.h"
 #include "components/autofill/ios/form_util/form_activity_params.h"
 #import "components/autofill/ios/form_util/form_activity_tab_helper.h"
 #import "components/autofill/ios/form_util/test_form_activity_tab_helper.h"
@@ -88,7 +87,6 @@
     web_state_.SetJSInjectionReceiver(injectionReceiver);
 
     js_autofill_manager_ = [[FakeJSAutofillManager alloc] init];
-    js_suggestion_manager_ = OCMClassMock([JsSuggestionManager class]);
 
     UniqueIDDataTabHelper::CreateForWebState(&web_state_);
 
@@ -125,7 +123,6 @@
                autofillClient:std::move(autofill_client)
                 autofillAgent:autofill_agent_
             JSAutofillManager:js_autofill_manager_
-          JSSuggestionManager:js_suggestion_manager_
               passwordManager:std::move(password_manager)
         passwordManagerClient:std::move(password_manager_client)
         passwordManagerDriver:std::move(password_manager_driver)
@@ -156,7 +153,6 @@
   id password_controller_;
   std::unique_ptr<autofill::TestFormActivityTabHelper>
       form_activity_tab_helper_;
-  id js_suggestion_manager_;
   WebViewPasswordManagerClient* password_manager_client_;
 };
 
@@ -310,33 +306,6 @@
   EXPECT_NSEQ(frame_id_, js_autofill_manager_.lastClearedFrameIdentifier);
 }
 
-// Tests CWVAutofillController focus previous field.
-TEST_F(CWVAutofillControllerTest, FocusPrevious) {
-  [[js_suggestion_manager_ expect] selectPreviousElementInFrameWithID:nil];
-  [autofill_controller_ focusPreviousField];
-  [js_suggestion_manager_ verify];
-}
-
-// Tests CWVAutofillController focus next field.
-TEST_F(CWVAutofillControllerTest, FocusNext) {
-  [[js_suggestion_manager_ expect] selectNextElementInFrameWithID:nil];
-  [autofill_controller_ focusNextField];
-  [js_suggestion_manager_ verify];
-}
-
-// Tests CWVAutofillController checks previous and next focusable state.
-TEST_F(CWVAutofillControllerTest, CheckFocus) {
-  id completionHandler = ^(BOOL previous, BOOL next) {
-  };
-  [[js_suggestion_manager_ expect]
-      fetchPreviousAndNextElementsPresenceInFrameWithID:nil
-                                      completionHandler:completionHandler];
-  [autofill_controller_
-      checkIfPreviousAndNextFieldsAreAvailableForFocusWithCompletionHandler:
-          completionHandler];
-  [js_suggestion_manager_ verify];
-}
-
 // Tests CWVAutofillController delegate focus callback is invoked.
 TEST_F(CWVAutofillControllerTest, FocusCallback) {
     id delegate = OCMProtocolMock(@protocol(CWVAutofillControllerDelegate));
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm
index f368ed5..5ca736ba 100644
--- a/ios/web_view/internal/cwv_web_view.mm
+++ b/ios/web_view/internal/cwv_web_view.mm
@@ -652,11 +652,6 @@
       initWithPrefService:_configuration.browserState->GetPrefs()
                  webState:_webState.get()];
   JsAutofillManager* JSAutofillManager = [[JsAutofillManager alloc] init];
-  JsSuggestionManager* JSSuggestionManager =
-      base::mac::ObjCCastStrict<JsSuggestionManager>(
-          [_webState->GetJSInjectionReceiver()
-              instanceOfClass:[JsSuggestionManager class]]);
-  [JSSuggestionManager setWebFramesManager:_webState->GetWebFramesManager()];
 
   auto passwordManagerClient =
       ios_web_view::WebViewPasswordManagerClient::Create(
@@ -682,7 +677,6 @@
              autofillClient:std::move(autofillClient)
               autofillAgent:autofillAgent
           JSAutofillManager:JSAutofillManager
-        JSSuggestionManager:JSSuggestionManager
             passwordManager:std::move(passwordManager)
       passwordManagerClient:std::move(passwordManagerClient)
       passwordManagerDriver:std::move(passwordManagerDriver)
diff --git a/media/capture/video/chromeos/camera_device_context.cc b/media/capture/video/chromeos/camera_device_context.cc
index 2de1625..6f5f659 100644
--- a/media/capture/video/chromeos/camera_device_context.cc
+++ b/media/capture/video/chromeos/camera_device_context.cc
@@ -153,4 +153,9 @@
   return result == VideoCaptureDevice::Client::ReserveResult::kSucceeded;
 }
 
+bool CameraDeviceContext::HasClient() {
+  base::AutoLock lock(client_lock_);
+  return !clients_.empty();
+}
+
 }  // namespace media
diff --git a/media/capture/video/chromeos/camera_device_delegate.cc b/media/capture/video/chromeos/camera_device_delegate.cc
index b05c2db..b8260fc 100644
--- a/media/capture/video/chromeos/camera_device_delegate.cc
+++ b/media/capture/video/chromeos/camera_device_delegate.cc
@@ -211,6 +211,8 @@
       return StreamType::kYUVInput;
     case 3:
       return StreamType::kYUVOutput;
+    case 4:
+      return StreamType::kRecordingOutput;
     default:
       return StreamType::kUnknown;
   }
@@ -226,6 +228,8 @@
       return std::string("StreamType::kYUVInput");
     case StreamType::kYUVOutput:
       return std::string("StreamType::kYUVOutput");
+    case StreamType::kRecordingOutput:
+      return std::string("StreamType::kRecordingOutput");
     default:
       return std::string("Unknown StreamType value: ") +
              base::NumberToString(static_cast<int32_t>(stream_type));
@@ -272,18 +276,16 @@
     VideoCaptureDeviceDescriptor device_descriptor,
     scoped_refptr<CameraHalDelegate> camera_hal_delegate,
     scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
-    CameraAppDeviceImpl* camera_app_device,
-    ClientType client_type)
+    CameraAppDeviceImpl* camera_app_device)
     : device_descriptor_(device_descriptor),
       camera_hal_delegate_(std::move(camera_hal_delegate)),
       ipc_task_runner_(std::move(ipc_task_runner)),
-      camera_app_device_(camera_app_device),
-      client_type_(client_type) {}
+      camera_app_device_(camera_app_device) {}
 
 CameraDeviceDelegate::~CameraDeviceDelegate() = default;
 
 void CameraDeviceDelegate::AllocateAndStart(
-    const VideoCaptureParams& params,
+    const base::flat_map<ClientType, VideoCaptureParams>& params,
     CameraDeviceContext* device_context) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
 
@@ -593,6 +595,18 @@
   result_metadata_frame_number_for_photo_state_ = current_request_frame_number_;
 }
 
+void CameraDeviceDelegate::ReconfigureStreams(
+    const base::flat_map<ClientType, VideoCaptureParams>& params) {
+  DCHECK(ipc_task_runner_->BelongsToCurrentThread());
+  chrome_capture_params_ = params;
+  if (request_manager_) {
+    // ReconfigureStreams is used for video recording. It does not require
+    // photo.
+    request_manager_->StopPreview(base::BindOnce(
+        &CameraDeviceDelegate::OnFlushed, GetWeakPtr(), false, base::nullopt));
+  }
+}
+
 void CameraDeviceDelegate::SetRotation(int rotation) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
   DCHECK(rotation >= 0 && rotation < 360 && rotation % 90 == 0);
@@ -620,11 +634,11 @@
     gfx::Size new_blob_resolution(static_cast<int32_t>(settings->width),
                                   static_cast<int32_t>(settings->height));
     request_manager_->StopPreview(
-        base::BindOnce(&CameraDeviceDelegate::OnFlushed, GetWeakPtr(),
+        base::BindOnce(&CameraDeviceDelegate::OnFlushed, GetWeakPtr(), true,
                        std::move(new_blob_resolution)));
   } else {
     request_manager_->StopPreview(base::BindOnce(
-        &CameraDeviceDelegate::OnFlushed, GetWeakPtr(), base::nullopt));
+        &CameraDeviceDelegate::OnFlushed, GetWeakPtr(), true, base::nullopt));
   }
   return true;
 }
@@ -645,7 +659,7 @@
   // Trigger the reconfigure process if it not yet triggered.
   if (on_reconfigured_callbacks_.empty()) {
     request_manager_->StopPreview(base::BindOnce(
-        &CameraDeviceDelegate::OnFlushed, GetWeakPtr(), base::nullopt));
+        &CameraDeviceDelegate::OnFlushed, GetWeakPtr(), true, base::nullopt));
   }
   auto on_reconfigured_callback = base::BindOnce(
       [](base::WeakPtr<Camera3AController> controller,
@@ -681,6 +695,7 @@
 }
 
 void CameraDeviceDelegate::OnFlushed(
+    bool require_photo,
     base::Optional<gfx::Size> new_blob_resolution,
     int32_t result) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
@@ -692,7 +707,7 @@
     return;
   }
   device_context_->SetState(CameraDeviceContext::State::kInitialized);
-  ConfigureStreams(true, std::move(new_blob_resolution));
+  ConfigureStreams(require_photo, std::move(new_blob_resolution));
 }
 
 void CameraDeviceDelegate::OnClosed(int32_t result) {
@@ -760,13 +775,15 @@
   DCHECK_EQ(device_context_->GetState(), CameraDeviceContext::State::kStarting);
 
   mojo::PendingRemote<cros::mojom::Camera3CallbackOps> callback_ops;
+  // Assumes the buffer_type will be the same for all |chrome_capture_params|.
   request_manager_ = std::make_unique<RequestManager>(
       callback_ops.InitWithNewPipeAndPassReceiver(),
       std::make_unique<StreamCaptureInterfaceImpl>(GetWeakPtr()),
-      device_context_, chrome_capture_params_.buffer_type,
+      device_context_,
+      chrome_capture_params_[ClientType::kPreviewClient].buffer_type,
       std::make_unique<CameraBufferFactory>(),
       base::BindRepeating(&RotateAndBlobify), ipc_task_runner_,
-      camera_app_device_, client_type_);
+      camera_app_device_);
   camera_3a_controller_ = std::make_unique<Camera3AController>(
       static_metadata_, request_manager_.get(), ipc_task_runner_);
   device_ops_->Initialize(
@@ -820,27 +837,33 @@
   DCHECK_EQ(device_context_->GetState(),
             CameraDeviceContext::State::kInitialized);
 
-  // Set up context for preview stream.
-  cros::mojom::Camera3StreamPtr preview_stream =
-      cros::mojom::Camera3Stream::New();
-  preview_stream->id = static_cast<uint64_t>(StreamType::kPreviewOutput);
-  preview_stream->stream_type =
-      cros::mojom::Camera3StreamType::CAMERA3_STREAM_OUTPUT;
-  preview_stream->width =
-      chrome_capture_params_.requested_format.frame_size.width();
-  preview_stream->height =
-      chrome_capture_params_.requested_format.frame_size.height();
-  preview_stream->format =
-      cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_YCbCr_420_888;
-  preview_stream->usage = cros::mojom::GRALLOC_USAGE_HW_COMPOSER |
-                          cros::mojom::GRALLOC_USAGE_HW_VIDEO_ENCODER;
-  preview_stream->data_space = 0;
-  preview_stream->rotation =
-      cros::mojom::Camera3StreamRotation::CAMERA3_STREAM_ROTATION_0;
-
   cros::mojom::Camera3StreamConfigurationPtr stream_config =
       cros::mojom::Camera3StreamConfiguration::New();
-  stream_config->streams.push_back(std::move(preview_stream));
+  for (const auto& param : chrome_capture_params_) {
+    // Set up context for preview stream and record stream.
+    cros::mojom::Camera3StreamPtr stream = cros::mojom::Camera3Stream::New();
+    StreamType stream_type = (param.first == ClientType::kPreviewClient)
+                                 ? StreamType::kPreviewOutput
+                                 : StreamType::kRecordingOutput;
+    // TODO(henryhsu): PreviewClient should remove HW_VIDEO_ENCODER usage when
+    // multiple streams enabled.
+    auto usage = (param.first == ClientType::kPreviewClient)
+                     ? (cros::mojom::GRALLOC_USAGE_HW_COMPOSER |
+                        cros::mojom::GRALLOC_USAGE_HW_VIDEO_ENCODER)
+                     : cros::mojom::GRALLOC_USAGE_HW_VIDEO_ENCODER;
+    stream->id = static_cast<uint64_t>(stream_type);
+    stream->stream_type = cros::mojom::Camera3StreamType::CAMERA3_STREAM_OUTPUT;
+    stream->width = param.second.requested_format.frame_size.width();
+    stream->height = param.second.requested_format.frame_size.height();
+    stream->format =
+        cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_YCbCr_420_888;
+    stream->usage = usage;
+    stream->data_space = 0;
+    stream->rotation =
+        cros::mojom::Camera3StreamRotation::CAMERA3_STREAM_ROTATION_0;
+
+    stream_config->streams.push_back(std::move(stream));
+  }
 
   // Set up context for still capture stream. We set still capture stream to the
   // JPEG stream configuration with maximum supported resolution.
@@ -961,9 +984,9 @@
   current_blob_resolution_.SetSize(blob_resolution.width(),
                                    blob_resolution.height());
 
-  request_manager_->SetUpStreamsAndBuffers(
-      chrome_capture_params_.requested_format, static_metadata_,
-      std::move(updated_config->streams));
+  request_manager_->SetUpStreamsAndBuffers(chrome_capture_params_,
+                                           static_metadata_,
+                                           std::move(updated_config->streams));
 
   device_context_->SetState(CameraDeviceContext::State::kStreamConfigured);
   // Kick off the preview stream.
@@ -1130,8 +1153,10 @@
     SetFpsRangeInMetadata(&settings, specified_fps_range->GetMin(),
                           specified_fps_range->GetMax());
   } else {
+    // Assumes the frame_rate will be the same for all |chrome_capture_params|.
     int32_t requested_frame_rate =
-        std::round(chrome_capture_params_.requested_format.frame_rate);
+        std::round(chrome_capture_params_[ClientType::kPreviewClient]
+                       .requested_format.frame_rate);
     bool prefer_constant_frame_rate =
         base::FeatureList::IsEnabled(
             chromeos::features::kPreferConstantFrameRate) ||
diff --git a/media/capture/video/chromeos/camera_device_delegate.h b/media/capture/video/chromeos/camera_device_delegate.h
index f11df02..1e2e52f 100644
--- a/media/capture/video/chromeos/camera_device_delegate.h
+++ b/media/capture/video/chromeos/camera_device_delegate.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <queue>
 
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -34,7 +35,19 @@
   kJpegOutput = 1,
   kYUVInput = 2,
   kYUVOutput = 3,
-  kUnknown,
+  kRecordingOutput = 4,
+  kUnknown = 5,
+};
+
+// A map to know that each StreamType belongs to which ClientType.
+// The index is StreamType value.
+constexpr std::array<ClientType, static_cast<int>(StreamType::kUnknown)>
+    kStreamClientTypeMap = {
+        ClientType::kPreviewClient,  // kPreviewOutput
+        ClientType::kPreviewClient,  // kJpegOutput
+        ClientType::kPreviewClient,  // kYUVInput
+        ClientType::kPreviewClient,  // kYUVOutput
+        ClientType::kVideoClient,    // kRecordingOutput
 };
 
 // The metadata might be large so clone a whole metadata might be relatively
@@ -96,6 +109,11 @@
 // AllocateAndStart of VideoCaptureDeviceArcChromeOS runs on.  All the methods
 // in CameraDeviceDelegate run on |ipc_task_runner_| and hence all the
 // access to member variables is sequenced.
+//
+// CameraDeviceDelegate supports multiple clients.
+// It will use the first client for preview stream and photo stream and use
+// second client for recording stream.
+// The second client will be a virtual camera device which is only used in CCA.
 class CAPTURE_EXPORT CameraDeviceDelegate final
     : public CaptureMetadataDispatcher::ResultMetadataObserver {
  public:
@@ -103,20 +121,23 @@
       VideoCaptureDeviceDescriptor device_descriptor,
       scoped_refptr<CameraHalDelegate> camera_hal_delegate,
       scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
-      CameraAppDeviceImpl* camera_app_device,
-      ClientType client_type);
+      CameraAppDeviceImpl* camera_app_device);
 
   ~CameraDeviceDelegate() final;
 
   // Delegation methods for the VideoCaptureDevice interface.
-  void AllocateAndStart(const VideoCaptureParams& params,
-                        CameraDeviceContext* device_context);
+  void AllocateAndStart(
+      const base::flat_map<ClientType, VideoCaptureParams>& params,
+      CameraDeviceContext* device_context);
   void StopAndDeAllocate(base::OnceClosure device_close_callback);
   void TakePhoto(VideoCaptureDevice::TakePhotoCallback callback);
   void GetPhotoState(VideoCaptureDevice::GetPhotoStateCallback callback);
   void SetPhotoOptions(mojom::PhotoSettingsPtr settings,
                        VideoCaptureDevice::SetPhotoOptionsCallback callback);
 
+  void ReconfigureStreams(
+      const base::flat_map<ClientType, VideoCaptureParams>& params);
+
   // Sets the frame rotation angle in |rotation_|.  |rotation_| is clockwise
   // rotation in degrees, and is passed to |client_| along with the captured
   // frames.
@@ -138,8 +159,10 @@
   // Mojo connection error handler.
   void OnMojoConnectionError();
 
-  // Reconfigure streams for picture taking.
-  void OnFlushed(base::Optional<gfx::Size> new_blob_resolution, int32_t result);
+  // Reconfigure streams for picture taking and recording.
+  void OnFlushed(bool require_photo,
+                 base::Optional<gfx::Size> new_blob_resolution,
+                 int32_t result);
 
   // Callback method for the Close Mojo IPC call.  This method resets the Mojo
   // connection and closes the camera device.
@@ -226,7 +249,8 @@
 
   const scoped_refptr<CameraHalDelegate> camera_hal_delegate_;
 
-  VideoCaptureParams chrome_capture_params_;
+  // Map client type to video capture parameter.
+  base::flat_map<ClientType, VideoCaptureParams> chrome_capture_params_;
 
   CameraDeviceContext* device_context_;
 
@@ -281,8 +305,6 @@
   ResultMetadata result_metadata_;
   gfx::Rect active_array_size_;
 
-  ClientType client_type_;
-
   base::WeakPtrFactory<CameraDeviceDelegate> weak_ptr_factory_{this};
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(CameraDeviceDelegate);
diff --git a/media/capture/video/chromeos/camera_device_delegate_unittest.cc b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
index 8ed8f8c..a9489f2 100644
--- a/media/capture/video/chromeos/camera_device_delegate_unittest.cc
+++ b/media/capture/video/chromeos/camera_device_delegate_unittest.cc
@@ -111,11 +111,13 @@
 constexpr size_t kDefaultWidth = 1280, kDefaultHeight = 720;
 constexpr int32_t kDefaultMinFrameRate = 1, kDefaultMaxFrameRate = 30;
 
-VideoCaptureParams GetDefaultCaptureParams() {
+base::flat_map<ClientType, VideoCaptureParams> GetDefaultCaptureParams() {
   VideoCaptureParams params;
+  base::flat_map<ClientType, VideoCaptureParams> capture_params;
   params.requested_format = {gfx::Size(kDefaultWidth, kDefaultHeight),
                              float{kDefaultMaxFrameRate}, PIXEL_FORMAT_I420};
-  return params;
+  capture_params[ClientType::kPreviewClient] = params;
+  return capture_params;
 }
 
 }  // namespace
@@ -162,7 +164,7 @@
 
     camera_device_delegate_ = std::make_unique<CameraDeviceDelegate>(
         devices_info[0].descriptor, camera_hal_delegate_,
-        device_delegate_thread_.task_runner(), nullptr, client_type_);
+        device_delegate_thread_.task_runner(), nullptr);
   }
 
   void GetNumberOfFakeCameras(
diff --git a/media/capture/video/chromeos/camera_hal_delegate.cc b/media/capture/video/chromeos/camera_hal_delegate.cc
index c1041dc..903c535 100644
--- a/media/capture/video/chromeos/camera_hal_delegate.cc
+++ b/media/capture/video/chromeos/camera_hal_delegate.cc
@@ -194,39 +194,16 @@
     return nullptr;
   }
 
-  if (camera_app_device_bridge) {
-    auto* camera_app_device = camera_app_device_bridge->GetCameraAppDevice(
-        device_descriptor.device_id);
-    // Since the cleanup callback will be triggered when VideoCaptureDevice died
-    // and |camera_app_device_bridge| is actually owned by
-    // VideoCaptureServiceImpl, it should be safe to assume
-    // |camera_app_device_bridge| is still valid here.
-    auto cleanup_callback = base::BindOnce(
-        [](const std::string& device_id, CameraAppDeviceBridgeImpl* bridge) {
-          bridge->OnDeviceClosed(device_id);
-        },
-        device_descriptor.device_id, camera_app_device_bridge);
-    auto delegate = std::make_unique<VideoCaptureDeviceChromeOSDelegate>(
-        std::move(task_runner_for_screen_observer), device_descriptor, this,
-        camera_app_device, std::move(cleanup_callback));
-    return std::make_unique<VideoCaptureDeviceChromeOSHalv3>(
-        std::move(delegate));
-  } else {
-    auto delegate = std::make_unique<VideoCaptureDeviceChromeOSDelegate>(
-        std::move(task_runner_for_screen_observer), device_descriptor, this,
-        nullptr, base::DoNothing());
-    return std::make_unique<VideoCaptureDeviceChromeOSHalv3>(
-        std::move(delegate));
-  }
+  auto* delegate = GetVCDDelegate(task_runner_for_screen_observer,
+                                  device_descriptor, camera_app_device_bridge);
+  return std::make_unique<VideoCaptureDeviceChromeOSHalv3>(delegate);
 }
 
 void CameraHalDelegate::GetSupportedFormats(
-    int camera_id,
+    const cros::mojom::CameraInfoPtr& camera_info,
     VideoCaptureFormats* supported_formats) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  const cros::mojom::CameraInfoPtr& camera_info = camera_info_[camera_id];
-
   base::flat_set<int32_t> candidate_fps_set =
       GetAvailableFramerates(camera_info);
 
@@ -371,7 +348,8 @@
       desc.set_control_support(GetControlSupport(camera_info));
       device_id_to_camera_id_[desc.device_id] = camera_id;
       devices_info.emplace_back(desc);
-      GetSupportedFormats(camera_id, &devices_info.back().supported_formats);
+      GetSupportedFormats(camera_info_[camera_id],
+                          &devices_info.back().supported_formats);
     }
   }
 
@@ -464,6 +442,47 @@
   return it->second;
 }
 
+VideoCaptureDeviceChromeOSDelegate* CameraHalDelegate::GetVCDDelegate(
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_screen_observer,
+    const VideoCaptureDeviceDescriptor& device_descriptor,
+    CameraAppDeviceBridgeImpl* camera_app_device_bridge) {
+  auto camera_id = GetCameraIdFromDeviceId(device_descriptor.device_id);
+  auto it = vcd_delegate_map_.find(camera_id);
+  if (it == vcd_delegate_map_.end() || it->second->HasDeviceClient() == 0) {
+    std::unique_ptr<VideoCaptureDeviceChromeOSDelegate> delegate;
+    if (camera_app_device_bridge) {
+      auto* camera_app_device = camera_app_device_bridge->GetCameraAppDevice(
+          device_descriptor.device_id);
+      // Since the cleanup callback will be triggered when VideoCaptureDevice
+      // died and |camera_app_device_bridge| is actually owned by
+      // VideoCaptureServiceImpl, it should be safe to assume
+      // |camera_app_device_bridge| is still valid here.
+      auto cleanup_callback = base::BindOnce(
+          [](const std::string& device_id, int camera_id,
+             CameraAppDeviceBridgeImpl* bridge,
+             base::flat_map<
+                 int, std::unique_ptr<VideoCaptureDeviceChromeOSDelegate>>*
+                 vcd_delegate_map) {
+            bridge->OnDeviceClosed(device_id);
+            vcd_delegate_map->erase(camera_id);
+          },
+          device_descriptor.device_id, camera_id, camera_app_device_bridge,
+          &vcd_delegate_map_);
+
+      delegate = std::make_unique<VideoCaptureDeviceChromeOSDelegate>(
+          std::move(task_runner_for_screen_observer), device_descriptor, this,
+          camera_app_device, std::move(cleanup_callback));
+    } else {
+      delegate = std::make_unique<VideoCaptureDeviceChromeOSDelegate>(
+          std::move(task_runner_for_screen_observer), device_descriptor, this,
+          nullptr, base::DoNothing());
+    }
+    vcd_delegate_map_[camera_id] = std::move(delegate);
+    return vcd_delegate_map_[camera_id].get();
+  }
+  return it->second.get();
+}
+
 void CameraHalDelegate::SetCameraModuleOnIpcThread(
     mojo::PendingRemote<cros::mojom::CameraModule> camera_module) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
@@ -490,6 +509,7 @@
   external_camera_info_updated_.Signal();
 
   // Clear all cached camera info, especially external cameras.
+  base::AutoLock lock(camera_info_lock_);
   camera_info_.clear();
   pending_external_camera_info_.clear();
 }
@@ -523,6 +543,8 @@
 
 void CameraHalDelegate::OnGotNumberOfCamerasOnIpcThread(int32_t num_cameras) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
+
+  base::AutoLock lock(camera_info_lock_);
   if (num_cameras < 0) {
     builtin_camera_info_updated_.Signal();
     LOG(ERROR) << "Failed to get number of cameras: " << num_cameras;
@@ -544,6 +566,8 @@
 
 void CameraHalDelegate::OnSetCallbacksOnIpcThread(int32_t result) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
+
+  base::AutoLock lock(camera_info_lock_);
   if (result) {
     num_builtin_cameras_ = 0;
     builtin_camera_info_updated_.Signal();
@@ -595,6 +619,7 @@
     // |camera_info_| might contain some entries for external cameras as well,
     // we should check all built-in cameras explicitly.
     bool all_updated = [&]() {
+      camera_info_lock_.AssertAcquired();
       for (size_t i = 0; i < num_builtin_cameras_; i++) {
         if (camera_info_.find(i) == camera_info_.end()) {
           return false;
diff --git a/media/capture/video/chromeos/camera_hal_delegate.h b/media/capture/video/chromeos/camera_hal_delegate.h
index ccce7e1..34d4ddd 100644
--- a/media/capture/video/chromeos/camera_hal_delegate.h
+++ b/media/capture/video/chromeos/camera_hal_delegate.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <unordered_map>
 
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/sequence_checker.h"
 #include "base/single_thread_task_runner.h"
@@ -30,6 +31,7 @@
 
 class CameraAppDeviceBridgeImpl;
 class CameraBufferFactory;
+class VideoCaptureDeviceChromeOSDelegate;
 
 // CameraHalDelegate is the component which does Mojo IPCs to the camera HAL
 // process on Chrome OS to access the module-level camera functionalities such
@@ -98,9 +100,15 @@
 
   void OnRegisteredCameraHalClient(int32_t result);
 
-  void GetSupportedFormats(int camera_id,
+  void GetSupportedFormats(const cros::mojom::CameraInfoPtr& camera_info,
                            VideoCaptureFormats* supported_formats);
 
+  VideoCaptureDeviceChromeOSDelegate* GetVCDDelegate(
+      scoped_refptr<base::SingleThreadTaskRunner>
+          task_runner_for_screen_observer,
+      const VideoCaptureDeviceDescriptor& device_descriptor,
+      CameraAppDeviceBridgeImpl* camera_app_device_bridge);
+
   void SetCameraModuleOnIpcThread(
       mojo::PendingRemote<cros::mojom::CameraModule> camera_module);
 
@@ -174,9 +182,10 @@
   // conditions. For external cameras, the |camera_info_| would be read nad
   // updated in CameraDeviceStatusChange, which is also protected by
   // |camera_info_lock_|.
-  size_t num_builtin_cameras_;
   base::Lock camera_info_lock_;
-  std::unordered_map<int, cros::mojom::CameraInfoPtr> camera_info_;
+  size_t num_builtin_cameras_ GUARDED_BY(camera_info_lock_);
+  std::unordered_map<int, cros::mojom::CameraInfoPtr> camera_info_
+      GUARDED_BY(camera_info_lock_);
 
   // A map from |VideoCaptureDeviceDescriptor.device_id| to camera id, which is
   // updated in GetDeviceDescriptors() and queried in
@@ -204,6 +213,9 @@
   // information of vendor tags.  Bound to |ipc_task_runner_|.
   VendorTagOpsDelegate vendor_tag_ops_delegate_;
 
+  base::flat_map<int, std::unique_ptr<VideoCaptureDeviceChromeOSDelegate>>
+      vcd_delegate_map_;
+
   DISALLOW_COPY_AND_ASSIGN(CameraHalDelegate);
 };
 
diff --git a/media/capture/video/chromeos/request_manager.cc b/media/capture/video/chromeos/request_manager.cc
index 7dedadf0..7f7f5b4 100644
--- a/media/capture/video/chromeos/request_manager.cc
+++ b/media/capture/video/chromeos/request_manager.cc
@@ -42,8 +42,7 @@
     std::unique_ptr<CameraBufferFactory> camera_buffer_factory,
     BlobifyCallback blobify_callback,
     scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
-    CameraAppDeviceImpl* camera_app_device,
-    ClientType client_type)
+    CameraAppDeviceImpl* camera_app_device)
     : callback_ops_(this, std::move(callback_ops_receiver)),
       capture_interface_(std::move(capture_interface)),
       device_context_(device_context),
@@ -52,15 +51,13 @@
       stream_buffer_manager_(
           new StreamBufferManager(device_context_,
                                   video_capture_use_gmb_,
-                                  std::move(camera_buffer_factory),
-                                  client_type)),
+                                  std::move(camera_buffer_factory))),
       blobify_callback_(std::move(blobify_callback)),
       ipc_task_runner_(std::move(ipc_task_runner)),
       capturing_(false),
       partial_result_count_(1),
       first_frame_shutter_time_(base::TimeTicks()),
-      camera_app_device_(std::move(camera_app_device)),
-      client_type_(client_type) {
+      camera_app_device_(std::move(camera_app_device)) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
   DCHECK(callback_ops_.is_bound());
   DCHECK(device_context_);
@@ -80,7 +77,7 @@
 RequestManager::~RequestManager() = default;
 
 void RequestManager::SetUpStreamsAndBuffers(
-    VideoCaptureFormat capture_format,
+    base::flat_map<ClientType, VideoCaptureParams> capture_params,
     const cros::mojom::CameraMetadataPtr& static_metadata,
     std::vector<cros::mojom::Camera3StreamPtr> streams) {
   // The partial result count metadata is optional; defaults to 1 in case it
@@ -107,7 +104,7 @@
   }
 
   stream_buffer_manager_->SetUpStreamsAndBuffers(
-      capture_format, static_metadata, std::move(streams));
+      capture_params, static_metadata, std::move(streams));
 }
 
 cros::mojom::Camera3StreamPtr RequestManager::GetStreamConfiguration(
@@ -269,11 +266,13 @@
   // 2. Capture (YuvOutput)
   // 3. Preview + Capture (YuvOutput)
   // 4. Reprocess (YuvInput + BlobOutput)
+  // 5. Preview + Recording (YuvOutput)
   //
   // For device without reprocess capability:
   // 1. Preview
   // 2. Capture (BlobOutput)
   // 3. Preview + Capture (BlobOutput)
+  // 4. Preview + Recording (YuvOutput)
   std::set<StreamType> stream_types;
   cros::mojom::CameraMetadataPtr settings;
   TakePhotoCallback callback = base::NullCallback();
@@ -283,6 +282,7 @@
   bool is_reprocess_request = false;
   bool is_preview_request = false;
   bool is_oneshot_request = false;
+  bool is_recording_request = false;
 
   // First, check if there are pending reprocess tasks.
   is_reprocess_request = TryPrepareReprocessRequest(
@@ -300,7 +300,12 @@
         TryPrepareOneShotRequest(&stream_types, &settings, &callback);
   }
 
-  if (!is_reprocess_request && !is_oneshot_request && !is_preview_request) {
+  if (is_preview_request) {
+    is_recording_request = TryPrepareRecordingRequest(&stream_types);
+  }
+
+  if (!is_reprocess_request && !is_oneshot_request && !is_preview_request &&
+      !is_recording_request) {
     // We have to keep the pipeline full.
     if (preview_buffers_queued_ < pipeline_depth_) {
       ipc_task_runner_->PostTask(
@@ -450,6 +455,17 @@
   return true;
 }
 
+bool RequestManager::TryPrepareRecordingRequest(
+    std::set<StreamType>* stream_types) {
+  if (!stream_buffer_manager_->IsRecordingSupported() ||
+      !stream_buffer_manager_->HasFreeBuffers({StreamType::kRecordingOutput})) {
+    return false;
+  }
+
+  stream_types->insert({StreamType::kRecordingOutput});
+  return true;
+}
+
 void RequestManager::OnProcessedCaptureRequest(int32_t result) {
   DCHECK(ipc_task_runner_->BelongsToCurrentThread());
 
@@ -790,8 +806,10 @@
   // Deliver the captured data to client.
   if (stream_buffer->status ==
       cros::mojom::Camera3BufferStatus::CAMERA3_BUFFER_STATUS_OK) {
-    if (stream_type == StreamType::kPreviewOutput) {
-      SubmitCapturedPreviewBuffer(frame_number, buffer_ipc_id);
+    if (stream_type == StreamType::kPreviewOutput ||
+        stream_type == StreamType::kRecordingOutput) {
+      SubmitCapturedPreviewRecordingBuffer(frame_number, buffer_ipc_id,
+                                           stream_type);
     } else if (stream_type == StreamType::kJpegOutput) {
       SubmitCapturedJpegBuffer(frame_number, buffer_ipc_id);
     } else if (stream_type == StreamType::kYUVOutput) {
@@ -827,14 +845,17 @@
   PrepareCaptureRequest();
 }
 
-void RequestManager::SubmitCapturedPreviewBuffer(uint32_t frame_number,
-                                                 uint64_t buffer_ipc_id) {
+void RequestManager::SubmitCapturedPreviewRecordingBuffer(
+    uint32_t frame_number,
+    uint64_t buffer_ipc_id,
+    StreamType stream_type) {
   const CaptureResult& pending_result = pending_results_[frame_number];
+  auto client_type = kStreamClientTypeMap[static_cast<int>(stream_type)];
   if (video_capture_use_gmb_) {
     VideoCaptureFormat format;
     base::Optional<VideoCaptureDevice::Client::Buffer> buffer =
         stream_buffer_manager_->AcquireBufferForClientById(
-            StreamType::kPreviewOutput, buffer_ipc_id, &format);
+            stream_type, buffer_ipc_id, &format);
     CHECK(buffer);
 
     // TODO: Figure out the right color space for the camera frame.  We may need
@@ -866,22 +887,21 @@
       metadata.rotation = VideoRotation::VIDEO_ROTATION_0;
     }
     device_context_->SubmitCapturedVideoCaptureBuffer(
-        client_type_, std::move(*buffer), format, pending_result.reference_time,
+        client_type, std::move(*buffer), format, pending_result.reference_time,
         pending_result.timestamp, metadata);
     // |buffer| ownership is transferred to client, so we need to reserve a
     // new video buffer.
-    stream_buffer_manager_->ReserveBuffer(StreamType::kPreviewOutput);
+    stream_buffer_manager_->ReserveBuffer(stream_type);
   } else {
     gfx::GpuMemoryBuffer* gmb = stream_buffer_manager_->GetGpuMemoryBufferById(
-        StreamType::kPreviewOutput, buffer_ipc_id);
+        stream_type, buffer_ipc_id);
     CHECK(gmb);
     device_context_->SubmitCapturedGpuMemoryBuffer(
-        client_type_, gmb,
-        stream_buffer_manager_->GetStreamCaptureFormat(
-            StreamType::kPreviewOutput),
+        client_type, gmb,
+        stream_buffer_manager_->GetStreamCaptureFormat(stream_type),
         pending_result.reference_time, pending_result.timestamp);
-    stream_buffer_manager_->ReleaseBufferFromCaptureResult(
-        StreamType::kPreviewOutput, buffer_ipc_id);
+    stream_buffer_manager_->ReleaseBufferFromCaptureResult(stream_type,
+                                                           buffer_ipc_id);
   }
 }
 
diff --git a/media/capture/video/chromeos/request_manager.h b/media/capture/video/chromeos/request_manager.h
index f5d4fdad..3d9a6d0 100644
--- a/media/capture/video/chromeos/request_manager.h
+++ b/media/capture/video/chromeos/request_manager.h
@@ -12,6 +12,7 @@
 #include <set>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
 #include "media/capture/mojom/image_capture.mojom.h"
@@ -107,14 +108,13 @@
                  std::unique_ptr<CameraBufferFactory> camera_buffer_factory,
                  BlobifyCallback blobify_callback,
                  scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner,
-                 CameraAppDeviceImpl* camera_app_device,
-                 ClientType client_type);
+                 CameraAppDeviceImpl* camera_app_device);
   ~RequestManager() override;
 
   // Sets up the stream context and allocate buffers according to the
   // configuration specified in |streams|.
   void SetUpStreamsAndBuffers(
-      VideoCaptureFormat capture_format,
+      base::flat_map<ClientType, VideoCaptureParams> capture_params,
       const cros::mojom::CameraMetadataPtr& static_metadata,
       std::vector<cros::mojom::Camera3StreamPtr> streams);
 
@@ -227,6 +227,8 @@
                                 cros::mojom::CameraMetadataPtr* settings,
                                 TakePhotoCallback* callback);
 
+  bool TryPrepareRecordingRequest(std::set<StreamType>* stream_types);
+
   // Callback for ProcessCaptureRequest().
   void OnProcessedCaptureRequest(int32_t result);
 
@@ -257,8 +259,9 @@
   void SubmitCaptureResult(uint32_t frame_number,
                            StreamType stream_type,
                            cros::mojom::Camera3StreamBufferPtr stream_buffer);
-  void SubmitCapturedPreviewBuffer(uint32_t frame_number,
-                                   uint64_t buffer_ipc_id);
+  void SubmitCapturedPreviewRecordingBuffer(uint32_t frame_number,
+                                            uint64_t buffer_ipc_id,
+                                            StreamType stream_type);
   void SubmitCapturedJpegBuffer(uint32_t frame_number, uint64_t buffer_ipc_id);
 
   // If there are some metadata set by SetCaptureMetadata() or
@@ -364,8 +367,6 @@
 
   CameraAppDeviceImpl* camera_app_device_;  // Weak.
 
-  ClientType client_type_;
-
   base::WeakPtrFactory<RequestManager> weak_ptr_factory_{this};
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(RequestManager);
diff --git a/media/capture/video/chromeos/request_manager_unittest.cc b/media/capture/video/chromeos/request_manager_unittest.cc
index 6227b6f3..429769a 100644
--- a/media/capture/video/chromeos/request_manager_unittest.cc
+++ b/media/capture/video/chromeos/request_manager_unittest.cc
@@ -87,6 +87,9 @@
   void SetUp() override {
     quit_ = false;
     client_type_ = ClientType::kPreviewClient;
+    VideoCaptureParams params;
+    params.requested_format = kDefaultCaptureFormat;
+    capture_params_[client_type_] = params;
     device_context_ = std::make_unique<CameraDeviceContext>();
     if (device_context_->AddClient(
             client_type_,
@@ -100,7 +103,7 @@
               [](const uint8_t* buffer, const uint32_t bytesused,
                  const VideoCaptureFormat& capture_format,
                  const int rotation) { return mojom::Blob::New(); }),
-          base::ThreadTaskRunnerHandle::Get(), nullptr, client_type_);
+          base::ThreadTaskRunnerHandle::Get(), nullptr);
     }
   }
 
@@ -284,6 +287,7 @@
   mojo::Remote<cros::mojom::Camera3CallbackOps> mock_callback_ops_;
   std::unique_ptr<CameraDeviceContext> device_context_;
   ClientType client_type_;
+  base::flat_map<ClientType, VideoCaptureParams> capture_params_;
 
  private:
   std::unique_ptr<base::RunLoop> run_loop_;
@@ -300,8 +304,7 @@
       .WillRepeatedly(Invoke(this, &RequestManagerTest::ProcessCaptureRequest));
 
   request_manager_->SetUpStreamsAndBuffers(
-      kDefaultCaptureFormat,
-      GetFakeStaticMetadata(/* partial_result_count */ 1),
+      capture_params_, GetFakeStaticMetadata(/* partial_result_count */ 1),
       PrepareCaptureStream(/* max_buffers */ 1));
   request_manager_->StartPreview(cros::mojom::CameraMetadata::New());
 
@@ -343,8 +346,7 @@
           }));
 
   request_manager_->SetUpStreamsAndBuffers(
-      kDefaultCaptureFormat,
-      GetFakeStaticMetadata(/* partial_result_count */ 3),
+      capture_params_, GetFakeStaticMetadata(/* partial_result_count */ 3),
       PrepareCaptureStream(/* max_buffers */ 1));
   request_manager_->StartPreview(cros::mojom::CameraMetadata::New());
 
@@ -375,8 +377,7 @@
       }));
 
   request_manager_->SetUpStreamsAndBuffers(
-      kDefaultCaptureFormat,
-      GetFakeStaticMetadata(/* partial_result_count */ 1),
+      capture_params_, GetFakeStaticMetadata(/* partial_result_count */ 1),
       PrepareCaptureStream(/* max_buffers */ 1));
   request_manager_->StartPreview(cros::mojom::CameraMetadata::New());
 
@@ -415,8 +416,7 @@
       .WillRepeatedly(Invoke(this, &RequestManagerTest::ProcessCaptureRequest));
 
   request_manager_->SetUpStreamsAndBuffers(
-      kDefaultCaptureFormat,
-      GetFakeStaticMetadata(/* partial_result_count */ 1),
+      capture_params_, GetFakeStaticMetadata(/* partial_result_count */ 1),
       PrepareCaptureStream(/* max_buffers */ 1));
   request_manager_->StartPreview(cros::mojom::CameraMetadata::New());
 
@@ -456,8 +456,7 @@
       .WillRepeatedly(Invoke(this, &RequestManagerTest::ProcessCaptureRequest));
 
   request_manager_->SetUpStreamsAndBuffers(
-      kDefaultCaptureFormat,
-      GetFakeStaticMetadata(/* partial_result_count */ 2),
+      capture_params_, GetFakeStaticMetadata(/* partial_result_count */ 2),
       PrepareCaptureStream(/* max_buffers */ 1));
   request_manager_->StartPreview(cros::mojom::CameraMetadata::New());
 
@@ -499,8 +498,7 @@
       .WillRepeatedly(Invoke(this, &RequestManagerTest::ProcessCaptureRequest));
 
   request_manager_->SetUpStreamsAndBuffers(
-      kDefaultCaptureFormat,
-      GetFakeStaticMetadata(/* partial_result_count */ 1),
+      capture_params_, GetFakeStaticMetadata(/* partial_result_count */ 1),
       PrepareCaptureStream(/* max_buffers */ 1));
   request_manager_->StartPreview(cros::mojom::CameraMetadata::New());
 
diff --git a/media/capture/video/chromeos/stream_buffer_manager.cc b/media/capture/video/chromeos/stream_buffer_manager.cc
index 611d054..471b1bb 100644
--- a/media/capture/video/chromeos/stream_buffer_manager.cc
+++ b/media/capture/video/chromeos/stream_buffer_manager.cc
@@ -27,12 +27,10 @@
 StreamBufferManager::StreamBufferManager(
     CameraDeviceContext* device_context,
     bool video_capture_use_gmb,
-    std::unique_ptr<CameraBufferFactory> camera_buffer_factory,
-    ClientType client_type)
+    std::unique_ptr<CameraBufferFactory> camera_buffer_factory)
     : device_context_(device_context),
       video_capture_use_gmb_(video_capture_use_gmb),
-      camera_buffer_factory_(std::move(camera_buffer_factory)),
-      client_type_(client_type) {
+      camera_buffer_factory_(std::move(camera_buffer_factory)) {
   if (video_capture_use_gmb_) {
     gmb_support_ = std::make_unique<gpu::GpuMemoryBufferSupport>();
   }
@@ -155,8 +153,9 @@
   } else {
     // We have to reserve a new buffer because the size is different.
     Buffer rotated_buffer;
+    auto client_type = kStreamClientTypeMap[static_cast<int>(stream_type)];
     if (!device_context_->ReserveVideoCaptureBufferFromPool(
-            client_type_, format->frame_size, format->pixel_format,
+            client_type, format->frame_size, format->pixel_format,
             &rotated_buffer)) {
       DLOG(WARNING) << "Failed to reserve video capture buffer";
       original_gmb->Unmap();
@@ -221,7 +220,7 @@
 }
 
 void StreamBufferManager::SetUpStreamsAndBuffers(
-    VideoCaptureFormat capture_format,
+    base::flat_map<ClientType, VideoCaptureParams> capture_params,
     const cros::mojom::CameraMetadataPtr& static_metadata,
     std::vector<cros::mojom::Camera3StreamPtr> streams) {
   DestroyCurrentStreamsAndBuffers();
@@ -249,11 +248,14 @@
     // flags of the stream.
     StreamType stream_type = StreamIdToStreamType(stream->id);
     auto stream_context = std::make_unique<StreamContext>();
-    stream_context->capture_format = capture_format;
+    auto client_type = kStreamClientTypeMap[static_cast<int>(stream_type)];
+    stream_context->capture_format =
+        capture_params[client_type].requested_format;
     stream_context->stream = std::move(stream);
 
     switch (stream_type) {
       case StreamType::kPreviewOutput:
+      case StreamType::kRecordingOutput:
         stream_context->buffer_dimension = gfx::Size(
             stream_context->stream->width, stream_context->stream->height);
         stream_context->buffer_usage =
@@ -378,6 +380,11 @@
   return stream_context_.find(StreamType::kYUVOutput) != stream_context_.end();
 }
 
+bool StreamBufferManager::IsRecordingSupported() {
+  return stream_context_.find(StreamType::kRecordingOutput) !=
+         stream_context_.end();
+}
+
 // static
 uint64_t StreamBufferManager::GetBufferIpcId(StreamType stream_type, int key) {
   uint64_t id = 0;
@@ -441,8 +448,9 @@
     return;
   }
   Buffer vcd_buffer;
+  auto client_type = kStreamClientTypeMap[static_cast<int>(stream_type)];
   if (!device_context_->ReserveVideoCaptureBufferFromPool(
-          client_type_, stream_context->buffer_dimension,
+          client_type, stream_context->buffer_dimension,
           stream_context->capture_format.pixel_format, &vcd_buffer)) {
     DLOG(WARNING) << "Failed to reserve video capture buffer";
     return;
diff --git a/media/capture/video/chromeos/stream_buffer_manager.h b/media/capture/video/chromeos/stream_buffer_manager.h
index 50ed6a3..ba62fbe 100644
--- a/media/capture/video/chromeos/stream_buffer_manager.h
+++ b/media/capture/video/chromeos/stream_buffer_manager.h
@@ -14,6 +14,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/containers/queue.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
@@ -51,8 +52,7 @@
   StreamBufferManager(
       CameraDeviceContext* device_context,
       bool video_capture_use_gmb,
-      std::unique_ptr<CameraBufferFactory> camera_buffer_factory,
-      ClientType client_type);
+      std::unique_ptr<CameraBufferFactory> camera_buffer_factory);
   ~StreamBufferManager();
 
   void ReserveBuffer(StreamType stream_type);
@@ -84,7 +84,7 @@
   // Sets up the stream context and allocate buffers according to the
   // configuration specified in |stream|.
   void SetUpStreamsAndBuffers(
-      VideoCaptureFormat capture_format,
+      base::flat_map<ClientType, VideoCaptureParams> capture_params,
       const cros::mojom::CameraMetadataPtr& static_metadata,
       std::vector<cros::mojom::Camera3StreamPtr> streams);
 
@@ -105,6 +105,8 @@
 
   bool IsReprocessSupported();
 
+  bool IsRecordingSupported();
+
  private:
   friend class RequestManagerTest;
 
@@ -164,8 +166,6 @@
 
   std::unique_ptr<CameraBufferFactory> camera_buffer_factory_;
 
-  ClientType client_type_;
-
   base::WeakPtrFactory<StreamBufferManager> weak_ptr_factory_{this};
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(StreamBufferManager);
diff --git a/media/capture/video/chromeos/vendor_tag_ops_delegate.cc b/media/capture/video/chromeos/vendor_tag_ops_delegate.cc
index 2ce163e..013b267 100644
--- a/media/capture/video/chromeos/vendor_tag_ops_delegate.cc
+++ b/media/capture/video/chromeos/vendor_tag_ops_delegate.cc
@@ -13,7 +13,7 @@
 
 VendorTagOpsDelegate::VendorTagOpsDelegate(
     scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner)
-    : ipc_task_runner_(ipc_task_runner) {}
+    : ipc_task_runner_(ipc_task_runner), is_initializing_(false) {}
 
 VendorTagOpsDelegate::~VendorTagOpsDelegate() = default;
 
@@ -28,17 +28,29 @@
 
 void VendorTagOpsDelegate::Initialize() {
   DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
+
+  base::AutoLock lock(lock_);
+  is_initializing_ = true;
   vendor_tag_ops_->GetTagCount(base::BindOnce(
       &VendorTagOpsDelegate::OnGotTagCount, base::Unretained(this)));
 }
 
 void VendorTagOpsDelegate::Reset() {
   DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
+
+  base::AutoLock lock(lock_);
   vendor_tag_ops_.reset();
   pending_info_.clear();
   name_map_.clear();
   tag_map_.clear();
   initialized_.Reset();
+  is_initializing_ = false;
+}
+
+void VendorTagOpsDelegate::StopInitialization() {
+  base::AutoLock lock(lock_);
+  initialized_.Signal();
+  is_initializing_ = false;
 }
 
 void VendorTagOpsDelegate::RemovePending(uint32_t tag) {
@@ -47,7 +59,7 @@
   DCHECK_EQ(removed, 1u);
   if (pending_info_.empty()) {
     DVLOG(1) << "VendorTagOpsDelegate initialized";
-    initialized_.Signal();
+    StopInitialization();
   }
 }
 
@@ -55,13 +67,13 @@
   DCHECK(ipc_task_runner_->RunsTasksInCurrentSequence());
   if (tag_count == -1) {
     LOG(ERROR) << "Failed to get tag count";
-    initialized_.Signal();
+    StopInitialization();
     return;
   }
 
   if (tag_count == 0) {
     // There is no vendor tag, we are done here.
-    initialized_.Signal();
+    StopInitialization();
     return;
   }
 
@@ -134,6 +146,13 @@
 
 const VendorTagInfo* VendorTagOpsDelegate::GetInfoByName(
     const std::string& full_name) {
+  {
+    base::AutoLock lock(lock_);
+    if (!is_initializing_ && !initialized_.IsSignaled()) {
+      LOG(WARNING) << "VendorTagOps is accessed before calling Initialize()";
+      return nullptr;
+    }
+  }
   initialized_.Wait();
   auto it = name_map_.find(full_name);
   if (it == name_map_.end()) {
@@ -144,6 +163,13 @@
 
 const VendorTagInfo* VendorTagOpsDelegate::GetInfoByTag(
     cros::mojom::CameraMetadataTag tag) {
+  {
+    base::AutoLock lock(lock_);
+    if (!is_initializing_ && !initialized_.IsSignaled()) {
+      LOG(WARNING) << "VendorTagOps is accessed before calling Initialize()";
+      return nullptr;
+    }
+  }
   initialized_.Wait();
   auto it = tag_map_.find(tag);
   if (it == tag_map_.end()) {
diff --git a/media/capture/video/chromeos/vendor_tag_ops_delegate.h b/media/capture/video/chromeos/vendor_tag_ops_delegate.h
index 206394f..cd963e0 100644
--- a/media/capture/video/chromeos/vendor_tag_ops_delegate.h
+++ b/media/capture/video/chromeos/vendor_tag_ops_delegate.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/synchronization/lock.h"
 #include "media/capture/video/chromeos/mojom/camera_common.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -41,6 +42,7 @@
   const VendorTagInfo* GetInfoByTag(cros::mojom::CameraMetadataTag tag);
 
  private:
+  void StopInitialization();
   void RemovePending(uint32_t tag);
 
   void OnGotTagCount(int32_t tag_count);
@@ -63,6 +65,9 @@
   std::map<cros::mojom::CameraMetadataTag, VendorTagInfo> tag_map_;
 
   base::WaitableEvent initialized_;
+
+  base::Lock lock_;
+  bool is_initializing_ GUARDED_BY(lock_);
 };
 
 }  // namespace media
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_delegate.cc b/media/capture/video/chromeos/video_capture_device_chromeos_delegate.cc
index b8b8a3b..9d5ef8da 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_delegate.cc
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_delegate.cc
@@ -123,60 +123,76 @@
       camera_app_device_(camera_app_device),
       cleanup_callback_(std::move(cleanup_callback)),
       power_manager_client_proxy_(
-          base::MakeRefCounted<PowerManagerClientProxy>()),
-      client_type_(ClientType::kPreviewClient) {
+          base::MakeRefCounted<PowerManagerClientProxy>()) {
   power_manager_client_proxy_->Init(weak_ptr_factory_.GetWeakPtr(),
                                     capture_task_runner_,
                                     std::move(ui_task_runner));
 }
 
-VideoCaptureDeviceChromeOSDelegate::~VideoCaptureDeviceChromeOSDelegate() {
+VideoCaptureDeviceChromeOSDelegate::~VideoCaptureDeviceChromeOSDelegate() {}
+
+void VideoCaptureDeviceChromeOSDelegate::Shutdown() {
   DCHECK(capture_task_runner_->BelongsToCurrentThread());
-  DCHECK(!camera_device_ipc_thread_.IsRunning());
-  screen_observer_delegate_->RemoveObserver();
-  power_manager_client_proxy_->Shutdown();
-  std::move(cleanup_callback_).Run();
+  if (!HasDeviceClient()) {
+    DCHECK(!camera_device_ipc_thread_.IsRunning());
+    screen_observer_delegate_->RemoveObserver();
+    power_manager_client_proxy_->Shutdown();
+    std::move(cleanup_callback_).Run();
+  }
 }
 
-// VideoCaptureDevice implementation.
+bool VideoCaptureDeviceChromeOSDelegate::HasDeviceClient() {
+  return device_context_ && device_context_->HasClient();
+}
+
 void VideoCaptureDeviceChromeOSDelegate::AllocateAndStart(
     const VideoCaptureParams& params,
-    std::unique_ptr<Client> client) {
+    std::unique_ptr<VideoCaptureDevice::Client> client,
+    ClientType client_type) {
   DCHECK(capture_task_runner_->BelongsToCurrentThread());
   DCHECK(!camera_device_delegate_);
-  TRACE_EVENT0("camera", "Start Device");
-  if (!camera_device_ipc_thread_.Start()) {
-    std::string error_msg = "Failed to start device thread";
-    LOG(ERROR) << error_msg;
-    client->OnError(
-        media::VideoCaptureError::kCrosHalV3FailedToStartDeviceThread,
-        FROM_HERE, error_msg);
-    return;
-  }
-  capture_params_ = params;
-  device_context_ = std::make_unique<CameraDeviceContext>();
-  if (device_context_->AddClient(client_type_, std::move(client))) {
-    camera_device_delegate_ = std::make_unique<CameraDeviceDelegate>(
-        device_descriptor_, camera_hal_delegate_,
-        camera_device_ipc_thread_.task_runner(), camera_app_device_,
-        client_type_);
-    OpenDevice();
+  if (!HasDeviceClient()) {
+    TRACE_EVENT0("camera", "Start Device");
+    if (!camera_device_ipc_thread_.Start()) {
+      std::string error_msg = "Failed to start device thread";
+      LOG(ERROR) << error_msg;
+      client->OnError(
+          media::VideoCaptureError::kCrosHalV3FailedToStartDeviceThread,
+          FROM_HERE, error_msg);
+      return;
+    }
+
+    device_context_ = std::make_unique<CameraDeviceContext>();
+    if (device_context_->AddClient(client_type, std::move(client))) {
+      capture_params_[client_type] = params;
+      camera_device_delegate_ = std::make_unique<CameraDeviceDelegate>(
+          device_descriptor_, camera_hal_delegate_,
+          camera_device_ipc_thread_.task_runner(), camera_app_device_);
+      OpenDevice();
+    }
+  } else {
+    if (device_context_->AddClient(client_type, std::move(client))) {
+      capture_params_[client_type] = params;
+      ReconfigureStreams();
+    }
   }
 }
 
-void VideoCaptureDeviceChromeOSDelegate::StopAndDeAllocate() {
+void VideoCaptureDeviceChromeOSDelegate::StopAndDeAllocate(
+    ClientType client_type) {
   DCHECK(capture_task_runner_->BelongsToCurrentThread());
-  if (!camera_device_delegate_) {
-    return;
+  DCHECK(!camera_device_delegate_);
+  device_context_->RemoveClient(client_type);
+  if (!HasDeviceClient()) {
+    CloseDevice(base::UnguessableToken());
+    camera_device_ipc_thread_.Stop();
+    camera_device_delegate_.reset();
+    device_context_.reset();
   }
-  CloseDevice(base::UnguessableToken());
-  camera_device_ipc_thread_.Stop();
-  camera_device_delegate_.reset();
-  device_context_->RemoveClient(client_type_);
-  device_context_.reset();
 }
 
-void VideoCaptureDeviceChromeOSDelegate::TakePhoto(TakePhotoCallback callback) {
+void VideoCaptureDeviceChromeOSDelegate::TakePhoto(
+    VideoCaptureDevice::TakePhotoCallback callback) {
   DCHECK(capture_task_runner_->BelongsToCurrentThread());
   DCHECK(camera_device_delegate_);
   camera_device_ipc_thread_.task_runner()->PostTask(
@@ -186,7 +202,7 @@
 }
 
 void VideoCaptureDeviceChromeOSDelegate::GetPhotoState(
-    GetPhotoStateCallback callback) {
+    VideoCaptureDevice::GetPhotoStateCallback callback) {
   DCHECK(capture_task_runner_->BelongsToCurrentThread());
   camera_device_ipc_thread_.task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&CameraDeviceDelegate::GetPhotoState,
@@ -196,7 +212,7 @@
 
 void VideoCaptureDeviceChromeOSDelegate::SetPhotoOptions(
     mojom::PhotoSettingsPtr settings,
-    SetPhotoOptionsCallback callback) {
+    VideoCaptureDevice::SetPhotoOptionsCallback callback) {
   DCHECK(capture_task_runner_->BelongsToCurrentThread());
   camera_device_ipc_thread_.task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&CameraDeviceDelegate::SetPhotoOptions,
@@ -224,6 +240,20 @@
                      camera_device_delegate_->GetWeakPtr(), rotation_));
 }
 
+void VideoCaptureDeviceChromeOSDelegate::ReconfigureStreams() {
+  DCHECK(capture_task_runner_->BelongsToCurrentThread());
+  DCHECK(camera_device_delegate_);
+
+  camera_device_ipc_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraDeviceDelegate::ReconfigureStreams,
+                     camera_device_delegate_->GetWeakPtr(), capture_params_));
+  camera_device_ipc_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&CameraDeviceDelegate::SetRotation,
+                     camera_device_delegate_->GetWeakPtr(), rotation_));
+}
+
 void VideoCaptureDeviceChromeOSDelegate::CloseDevice(
     base::UnguessableToken unblock_suspend_token) {
   DCHECK(capture_task_runner_->BelongsToCurrentThread());
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_delegate.h b/media/capture/video/chromeos/video_capture_device_chromeos_delegate.h
index 2b82e40..80aaac3d 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_delegate.h
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_delegate.h
@@ -7,9 +7,11 @@
 
 #include <memory>
 
+#include "base/containers/flat_map.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
 #include "base/threading/thread.h"
 #include "media/capture/video/chromeos/camera_device_context.h"
 #include "media/capture/video/chromeos/display_rotation_observer.h"
@@ -29,10 +31,9 @@
 class CameraHalDelegate;
 class CameraDeviceDelegate;
 
-// Implementation of VideoCaptureDevice for ChromeOS with CrOS camera HALv3.
+// Implementation of delegate for ChromeOS with CrOS camera HALv3.
 class CAPTURE_EXPORT VideoCaptureDeviceChromeOSDelegate final
-    : public VideoCaptureDevice,
-      public DisplayRotationObserver {
+    : public DisplayRotationObserver {
  public:
   VideoCaptureDeviceChromeOSDelegate(
       scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
@@ -41,22 +42,25 @@
       CameraAppDeviceImpl* camera_app_device,
       base::OnceClosure cleanup_callback);
 
-  ~VideoCaptureDeviceChromeOSDelegate() final;
+  ~VideoCaptureDeviceChromeOSDelegate();
+  void Shutdown();
+  bool HasDeviceClient();
 
-  // VideoCaptureDevice implementation.
   void AllocateAndStart(const VideoCaptureParams& params,
-                        std::unique_ptr<Client> client) final;
-  void StopAndDeAllocate() final;
-  void TakePhoto(TakePhotoCallback callback) final;
-  void GetPhotoState(GetPhotoStateCallback callback) final;
+                        std::unique_ptr<VideoCaptureDevice::Client> client,
+                        ClientType client_type);
+  void StopAndDeAllocate(ClientType client_type);
+  void TakePhoto(VideoCaptureDevice::TakePhotoCallback callback);
+  void GetPhotoState(VideoCaptureDevice::GetPhotoStateCallback callback);
   void SetPhotoOptions(mojom::PhotoSettingsPtr settings,
-                       SetPhotoOptionsCallback callback) final;
+                       VideoCaptureDevice::SetPhotoOptionsCallback callback);
 
  private:
   // Helper to interact with PowerManagerClient on DBus original thread.
   class PowerManagerClientProxy;
 
   void OpenDevice();
+  void ReconfigureStreams();
   void CloseDevice(base::UnguessableToken unblock_suspend_token);
 
   // DisplayRotationDelegate implementation.
@@ -79,7 +83,9 @@
   // |capture_task_runner_|.
   base::Thread camera_device_ipc_thread_;
 
-  VideoCaptureParams capture_params_;
+  // Map client type to VideoCaptureParams.
+  base::flat_map<ClientType, VideoCaptureParams> capture_params_;
+
   // |device_context_| is created and owned by
   // VideoCaptureDeviceChromeOSDelegate and is only accessed by
   // |camera_device_delegate_|.
@@ -103,9 +109,6 @@
 
   scoped_refptr<PowerManagerClientProxy> power_manager_client_proxy_;
 
-  // The client type in CameraDeviceContext.
-  ClientType client_type_;
-
   base::WeakPtrFactory<VideoCaptureDeviceChromeOSDelegate> weak_ptr_factory_{
       this};
 
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
index 34a6eaef..7e24d63a4 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.cc
@@ -9,21 +9,22 @@
 namespace media {
 
 VideoCaptureDeviceChromeOSHalv3::VideoCaptureDeviceChromeOSHalv3(
-    std::unique_ptr<VideoCaptureDeviceChromeOSDelegate> delegate)
-    : vcd_delegate_(std::move(delegate)) {}
+    VideoCaptureDeviceChromeOSDelegate* delegate)
+    : vcd_delegate_(delegate), client_type_(ClientType::kPreviewClient) {}
 
 VideoCaptureDeviceChromeOSHalv3::~VideoCaptureDeviceChromeOSHalv3() {
+  vcd_delegate_->Shutdown();
 }
 
 // VideoCaptureDevice implementation.
 void VideoCaptureDeviceChromeOSHalv3::AllocateAndStart(
     const VideoCaptureParams& params,
     std::unique_ptr<Client> client) {
-  vcd_delegate_->AllocateAndStart(params, std::move(client));
+  vcd_delegate_->AllocateAndStart(params, std::move(client), client_type_);
 }
 
 void VideoCaptureDeviceChromeOSHalv3::StopAndDeAllocate() {
-  vcd_delegate_->StopAndDeAllocate();
+  vcd_delegate_->StopAndDeAllocate(client_type_);
 }
 
 void VideoCaptureDeviceChromeOSHalv3::TakePhoto(TakePhotoCallback callback) {
diff --git a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.h b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.h
index b20f76f2..fde6525 100644
--- a/media/capture/video/chromeos/video_capture_device_chromeos_halv3.h
+++ b/media/capture/video/chromeos/video_capture_device_chromeos_halv3.h
@@ -7,7 +7,9 @@
 
 #include <memory>
 
+#include "media/capture/video/chromeos/camera_device_context.h"
 #include "media/capture/video/video_capture_device.h"
+#include "media/capture/video/video_capture_device_descriptor.h"
 
 namespace media {
 
@@ -18,7 +20,7 @@
     : public VideoCaptureDevice {
  public:
   explicit VideoCaptureDeviceChromeOSHalv3(
-      std::unique_ptr<VideoCaptureDeviceChromeOSDelegate> delegate);
+      VideoCaptureDeviceChromeOSDelegate* delegate);
 
   ~VideoCaptureDeviceChromeOSHalv3() final;
 
@@ -32,7 +34,9 @@
                        SetPhotoOptionsCallback callback) final;
 
  private:
-  std::unique_ptr<VideoCaptureDeviceChromeOSDelegate> vcd_delegate_;
+  VideoCaptureDeviceChromeOSDelegate* vcd_delegate_;
+
+  ClientType client_type_;
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureDeviceChromeOSHalv3);
 };
diff --git a/media/gpu/vaapi/vaapi_unittest.cc b/media/gpu/vaapi/vaapi_unittest.cc
index 791cbdd0..f4870021 100644
--- a/media/gpu/vaapi/vaapi_unittest.cc
+++ b/media/gpu/vaapi/vaapi_unittest.cc
@@ -22,9 +22,11 @@
 #include "base/strings/pattern.h"
 #include "base/strings/string_split.h"
 #include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/test_suite.h"
 #include "build/chromeos_buildflags.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
+#include "media/base/media_switches.h"
 #include "media/gpu/vaapi/vaapi_wrapper.h"
 #include "media/media_buildflags.h"
 
@@ -104,12 +106,23 @@
              ? base::make_optional<VAEntrypoint>(it->second)
              : base::nullopt;
 }
+
+std::unique_ptr<base::test::ScopedFeatureList> CreateScopedFeatureList() {
+  auto scoped_feature_list = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_list->InitWithFeatures(
+      /*enabled_features=*/{media::kVaapiAV1Decoder},
+      /*disabled_features=*/{});
+  return scoped_feature_list;
+}
 }  // namespace
 
 class VaapiTest : public testing::Test {
  public:
-  VaapiTest() = default;
+  VaapiTest() : scoped_feature_list_(CreateScopedFeatureList()) {}
   ~VaapiTest() override = default;
+
+ private:
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
 };
 
 std::map<VAProfile, std::vector<VAEntrypoint>> ParseVainfo(
@@ -334,10 +347,16 @@
 
 int main(int argc, char** argv) {
   base::TestSuite test_suite(argc, argv);
-
-  // PreSandboxInitialization() loads and opens the driver, queries its
-  // capabilities and fills in the VASupportedProfiles.
-  media::VaapiWrapper::PreSandboxInitialization();
+  {
+    // Enables/Disables features during PreSandboxInitialization(). We have to
+    // destruct ScopedFeatureList after it because base::TestSuite::Run()
+    // creates a ScopedFeatureList and multiple concurrent ScopedFeatureLists
+    // are not allowed.
+    auto scoped_feature_list = media::CreateScopedFeatureList();
+    // PreSandboxInitialization() loads and opens the driver, queries its
+    // capabilities and fills in the VASupportedProfiles.
+    media::VaapiWrapper::PreSandboxInitialization();
+  }
 
   return base::LaunchUnitTests(
       argc, argv,
diff --git a/net/quic/quic_transport_client.h b/net/quic/quic_transport_client.h
index eaa79b6..9f873d1 100644
--- a/net/quic/quic_transport_client.h
+++ b/net/quic/quic_transport_client.h
@@ -156,6 +156,11 @@
   }
   void OnStopSendingReceived(
       const quic::QuicStopSendingFrame& /*frame*/) override {}
+  void OnNewConnectionIdSent(
+      const quic::QuicConnectionId& /*server_connection_id*/,
+      const quic::QuicConnectionId& /*new_connecition_id*/) override {}
+  void OnConnectionIdRetired(
+      const quic::QuicConnectionId& /*server_connection_id*/) override {}
 
  private:
   // State of the connection establishment process.
diff --git a/services/device/time_zone_monitor/time_zone_monitor_win.cc b/services/device/time_zone_monitor/time_zone_monitor_win.cc
index 4bcdc5f5d..bbe92ad 100644
--- a/services/device/time_zone_monitor/time_zone_monitor_win.cc
+++ b/services/device/time_zone_monitor/time_zone_monitor_win.cc
@@ -12,6 +12,7 @@
 #include "base/callback_helpers.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "third_party/icu/source/i18n/unicode/timezone.h"
 #include "ui/gfx/win/singleton_hwnd_observer.h"
@@ -24,7 +25,8 @@
       : TimeZoneMonitor(),
         singleton_hwnd_observer_(new gfx::SingletonHwndObserver(
             base::BindRepeating(&TimeZoneMonitorWin::OnWndProc,
-                                base::Unretained(this)))) {}
+                                base::Unretained(this)))),
+        current_platform_timezone_(GetPlatformTimeZone()) {}
   TimeZoneMonitorWin(const TimeZoneMonitorWin&) = delete;
   TimeZoneMonitorWin& operator=(const TimeZoneMonitorWin&) = delete;
 
@@ -52,14 +54,39 @@
     }
   }
 
+  // Returns the platform specific string for the time zone. Do not rely on the
+  // ICU library since it's taking into account other sources for time zone like
+  // the TZ environment. This avoid loading the ICU library if not required.
+  std::string GetPlatformTimeZone() {
+    std::string timezone;
+    TIME_ZONE_INFORMATION time_zone_information;
+    if (::GetTimeZoneInformation(&time_zone_information)) {
+      // StandardName field may be empty.
+      timezone = base::WideToUTF8(time_zone_information.StandardName);
+    }
+    return timezone;
+  }
+
   void OnWmTimechangeReceived() {
     TRACE_EVENT0("device", "TimeZoneMonitorWin::OnTimechangeReceived");
-    UpdateIcuAndNotifyClients(DetectHostTimeZoneFromIcu());
+
+    // Only dispatch time zone notifications when the platform time zone has
+    // changed. Windows API is sending WM_TIMECHANGE messages each time a
+    // time property has changed which is common during a power suspend/resume
+    // transition even if the time zone stayed the same. As a good example, any
+    // NTP update may trigger a WM_TIMECHANGE message.
+    const std::string timezone = GetPlatformTimeZone();
+    if (timezone.empty() || current_platform_timezone_ != timezone) {
+      UpdateIcuAndNotifyClients(DetectHostTimeZoneFromIcu());
+      current_platform_timezone_ = timezone;
+    }
+
     pending_update_notification_tasks_ = false;
   }
 
   std::unique_ptr<gfx::SingletonHwndObserver> singleton_hwnd_observer_;
   bool pending_update_notification_tasks_ = false;
+  std::string current_platform_timezone_;
   base::WeakPtrFactory<TimeZoneMonitorWin> weak_ptr_factory_{this};
 };
 
diff --git a/services/network/url_loader_factory.cc b/services/network/url_loader_factory.cc
index df541bd..9d75573 100644
--- a/services/network/url_loader_factory.cc
+++ b/services/network/url_loader_factory.cc
@@ -278,7 +278,7 @@
     DCHECK(url_request.web_bundle_token_params.has_value());
     base::WeakPtr<WebBundleURLLoaderFactory> web_bundle_url_loader_factory =
         context_->GetWebBundleManager().CreateWebBundleURLLoaderFactory(
-            url_request.url, *url_request.web_bundle_token_params);
+            url_request.url, *url_request.web_bundle_token_params, params_);
     client =
         web_bundle_url_loader_factory->WrapURLLoaderClient(std::move(client));
   }
diff --git a/services/network/web_bundle_manager.cc b/services/network/web_bundle_manager.cc
index d980753..3538c889 100644
--- a/services/network/web_bundle_manager.cc
+++ b/services/network/web_bundle_manager.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "services/network/network_context.h"
 #include "services/network/public/mojom/web_bundle_handle.mojom.h"
 #include "services/network/web_bundle_url_loader_factory.h"
 
@@ -18,7 +19,8 @@
 base::WeakPtr<WebBundleURLLoaderFactory>
 WebBundleManager::CreateWebBundleURLLoaderFactory(
     const GURL& bundle_url,
-    const ResourceRequest::WebBundleTokenParams& web_bundle_token_params) {
+    const ResourceRequest::WebBundleTokenParams& web_bundle_token_params,
+    const mojom::URLLoaderFactoryParamsPtr& factory_params) {
   DCHECK(factories_.find(web_bundle_token_params.token) == factories_.end());
 
   mojo::Remote<mojom::WebBundleHandle> remote(
@@ -32,8 +34,9 @@
                      // |this| outlives |remote|.
                      base::Unretained(this), web_bundle_token_params.token));
 
-  auto factory = std::make_unique<WebBundleURLLoaderFactory>(bundle_url,
-                                                             std::move(remote));
+  auto factory = std::make_unique<WebBundleURLLoaderFactory>(
+      bundle_url, std::move(remote),
+      factory_params->request_initiator_origin_lock);
   auto weak_factory = factory->GetWeakPtr();
   factories_.insert({web_bundle_token_params.token, std::move(factory)});
 
diff --git a/services/network/web_bundle_manager.h b/services/network/web_bundle_manager.h
index 02b14a9..50e7320 100644
--- a/services/network/web_bundle_manager.h
+++ b/services/network/web_bundle_manager.h
@@ -11,6 +11,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/mojom/network_context.mojom-forward.h"
 
 namespace network {
 
@@ -28,7 +29,8 @@
 
   base::WeakPtr<WebBundleURLLoaderFactory> CreateWebBundleURLLoaderFactory(
       const GURL& bundle_url,
-      const ResourceRequest::WebBundleTokenParams& params);
+      const ResourceRequest::WebBundleTokenParams& params,
+      const mojom::URLLoaderFactoryParamsPtr& factory_params);
 
   base::WeakPtr<WebBundleURLLoaderFactory> GetWebBundleURLLoaderFactory(
       const base::UnguessableToken& token);
diff --git a/services/network/web_bundle_manager_unittest.cc b/services/network/web_bundle_manager_unittest.cc
index 0eb18bcb..af804aea 100644
--- a/services/network/web_bundle_manager_unittest.cc
+++ b/services/network/web_bundle_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/test/task_environment.h"
 #include "base/unguessable_token.h"
 #include "services/network/public/cpp/resource_request.h"
+#include "services/network/public/mojom/network_context.mojom.h"
 #include "services/network/public/mojom/web_bundle_handle.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -37,8 +38,8 @@
     auto token_params =
         ResourceRequest::WebBundleTokenParams(token, std::move(handle));
 
-    auto factory =
-        manager.CreateWebBundleURLLoaderFactory(GURL(kBundleUrl), token_params);
+    auto factory = manager.CreateWebBundleURLLoaderFactory(
+        GURL(kBundleUrl), token_params, mojom::URLLoaderFactoryParams::New());
     ASSERT_TRUE(factory);
     ASSERT_TRUE(manager.GetWebBundleURLLoaderFactory(token));
     // Getting out of scope to delete |receiver|.
diff --git a/services/network/web_bundle_url_loader_factory.cc b/services/network/web_bundle_url_loader_factory.cc
index 941d1eb..caf2cb4 100644
--- a/services/network/web_bundle_url_loader_factory.cc
+++ b/services/network/web_bundle_url_loader_factory.cc
@@ -4,6 +4,7 @@
 
 #include "services/network/web_bundle_url_loader_factory.h"
 
+#include "base/optional.h"
 #include "components/web_package/web_bundle_parser.h"
 #include "components/web_package/web_bundle_utils.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
@@ -11,6 +12,7 @@
 #include "mojo/public/cpp/system/data_pipe_drainer.h"
 #include "mojo/public/cpp/system/data_pipe_producer.h"
 #include "net/http/http_status_code.h"
+#include "services/network/public/cpp/cross_origin_read_blocking.h"
 
 namespace network {
 
@@ -128,8 +130,12 @@
  public:
   URLLoader(mojo::PendingReceiver<mojom::URLLoader> loader,
             const ResourceRequest& request,
-            mojo::PendingRemote<mojom::URLLoaderClient> client)
+            mojo::PendingRemote<mojom::URLLoaderClient> client,
+            const base::Optional<url::Origin>& request_initiator_origin_lock)
       : url_(request.url),
+        request_mode_(request.mode),
+        request_initiator_(request.request_initiator),
+        request_initiator_origin_lock_(request_initiator_origin_lock),
         receiver_(this, std::move(loader)),
         client_(std::move(client)) {
     receiver_.set_disconnect_handler(
@@ -139,6 +145,15 @@
   URLLoader& operator=(const URLLoader&) = delete;
 
   const GURL& url() const { return url_; }
+  const mojom::RequestMode& request_mode() const { return request_mode_; }
+
+  const base::Optional<url::Origin>& request_initiator() const {
+    return request_initiator_;
+  }
+
+  const base::Optional<url::Origin>& request_initiator_origin_lock() const {
+    return request_initiator_origin_lock_;
+  }
 
   base::WeakPtr<URLLoader> GetWeakPtr() {
     return weak_ptr_factory_.GetWeakPtr();
@@ -164,6 +179,39 @@
     delete this;
   }
 
+  void BlockResponseForCorb(mojom::URLResponseHeadPtr response_head) {
+    // A minimum implementation to block CORB-protected resources.
+    //
+    // TODO(crbug.com/1082020): Re-use
+    // network::URLLoader::BlockResponseForCorb(), instead of copying
+    // essential parts from there, so that the two implementations won't
+    // diverge further. That requires non-trivial refactoring.
+    CrossOriginReadBlocking::SanitizeBlockedResponse(response_head.get());
+    client_->OnReceiveResponse(std::move(response_head));
+
+    // Send empty body to the URLLoaderClient.
+    mojo::ScopedDataPipeProducerHandle producer;
+    mojo::ScopedDataPipeConsumerHandle consumer;
+    if (CreateDataPipe(nullptr, &producer, &consumer) != MOJO_RESULT_OK) {
+      OnFail(net::ERR_INSUFFICIENT_RESOURCES);
+      return;
+    }
+    producer.reset();
+    client_->OnStartLoadingResponseBody(std::move(consumer));
+
+    URLLoaderCompletionStatus status;
+    status.error_code = net::OK;
+    status.completion_time = base::TimeTicks::Now();
+    status.encoded_data_length = 0;
+    status.encoded_body_length = 0;
+    status.decoded_body_length = 0;
+    client_->OnComplete(status);
+
+    // Reset the connection to the URLLoaderClient.  This helps ensure that we
+    // won't accidentally leak any data to the renderer from this point on.
+    client_.reset();
+  }
+
  private:
   // mojom::URLLoader
   void FollowRedirect(
@@ -185,6 +233,15 @@
   void OnMojoDisconnect() { delete this; }
 
   const GURL url_;
+  mojom::RequestMode request_mode_;
+  base::Optional<url::Origin> request_initiator_;
+  // It is safe to hold |request_initiator_origin_lock_| in this factory because
+  // 1). |request_initiator_origin_lock| is a property of |URLLoaderFactory|
+  // (or, more accurately a property of |URLLoaderFactoryParams|), and
+  // 2) |WebURLLoader| is always associated with the same URLLoaderFactory
+  // (via URLLoaderFactory -> WebBundleManager -> WebBundleURLLoaderFactory
+  // -> WebBundleURLLoader).
+  const base::Optional<url::Origin> request_initiator_origin_lock_;
   mojo::Receiver<mojom::URLLoader> receiver_;
   mojo::Remote<mojom::URLLoaderClient> client_;
   base::WeakPtrFactory<URLLoader> weak_ptr_factory_{this};
@@ -317,9 +374,11 @@
 
 WebBundleURLLoaderFactory::WebBundleURLLoaderFactory(
     const GURL& bundle_url,
-    mojo::Remote<mojom::WebBundleHandle> web_bundle_handle)
+    mojo::Remote<mojom::WebBundleHandle> web_bundle_handle,
+    const base::Optional<url::Origin>& request_initiator_origin_lock)
     : bundle_url_(bundle_url),
-      web_bundle_handle_(std::move(web_bundle_handle)) {}
+      web_bundle_handle_(std::move(web_bundle_handle)),
+      request_initiator_origin_lock_(request_initiator_origin_lock) {}
 
 WebBundleURLLoaderFactory::~WebBundleURLLoaderFactory() {
   for (auto loader : pending_loaders_) {
@@ -368,7 +427,8 @@
     const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
   TRACE_EVENT0("loading", "WebBundleURLLoaderFactory::CreateLoaderAndStart");
   URLLoader* loader =
-      new URLLoader(std::move(receiver), url_request, std::move(client));
+      new URLLoader(std::move(receiver), url_request, std::move(client),
+                    request_initiator_origin_lock_);
   if (metadata_error_) {
     loader->OnFail(net::ERR_INVALID_WEB_BUNDLE);
     return;
@@ -459,6 +519,21 @@
   mojom::URLResponseHeadPtr response_head =
       web_package::CreateResourceResponse(response);
   response_head->web_bundle_url = bundle_url_;
+  // Add an artifical "X-Content-Type-Options: "nosniff" header, which is
+  // explained at
+  // https://wicg.github.io/webpackage/draft-yasskin-wpack-bundled-exchanges.html#name-responses.
+  response_head->headers->SetHeader("X-Content-Type-Options", "nosniff");
+
+  auto corb_analyzer =
+      std::make_unique<CrossOriginReadBlocking::ResponseAnalyzer>(
+          loader->url(), loader->request_initiator(), *response_head,
+          loader->request_initiator_origin_lock(), loader->request_mode());
+
+  if (corb_analyzer->ShouldBlock()) {
+    loader->BlockResponseForCorb(std::move(response_head));
+    return;
+  }
+
   loader->OnResponse(std::move(response_head));
 
   mojo::ScopedDataPipeProducerHandle producer;
diff --git a/services/network/web_bundle_url_loader_factory.h b/services/network/web_bundle_url_loader_factory.h
index 8c22c08c..12de0a2d 100644
--- a/services/network/web_bundle_url_loader_factory.h
+++ b/services/network/web_bundle_url_loader_factory.h
@@ -20,7 +20,8 @@
  public:
   WebBundleURLLoaderFactory(
       const GURL& bundle_url,
-      mojo::Remote<mojom::WebBundleHandle> web_bundle_handle);
+      mojo::Remote<mojom::WebBundleHandle> web_bundle_handle,
+      const base::Optional<url::Origin>& request_initiator_origin_lock);
   ~WebBundleURLLoaderFactory() override;
   WebBundleURLLoaderFactory(const WebBundleURLLoaderFactory&) = delete;
   WebBundleURLLoaderFactory& operator=(const WebBundleURLLoaderFactory&) =
@@ -57,6 +58,7 @@
 
   GURL bundle_url_;
   mojo::Remote<mojom::WebBundleHandle> web_bundle_handle_;
+  const base::Optional<::url::Origin> request_initiator_origin_lock_;
   std::unique_ptr<BundleDataSource> source_;
   mojo::Remote<web_package::mojom::WebBundleParser> parser_;
   web_package::mojom::BundleMetadataPtr metadata_;
diff --git a/services/network/web_bundle_url_loader_factory_unittest.cc b/services/network/web_bundle_url_loader_factory_unittest.cc
index 7a21b96..5da2855 100644
--- a/services/network/web_bundle_url_loader_factory_unittest.cc
+++ b/services/network/web_bundle_url_loader_factory_unittest.cc
@@ -18,11 +18,16 @@
 
 namespace {
 
+const char kInitiatorUrl[] = "https://example.com/";
 const char kBundleUrl[] = "https://example.com/bundle.wbn";
 const char kResourceUrl[] = "https://example.com/";
 const char kResourceUrl2[] = "https://example.com/another";
 const char kResourceUrl3[] = "https://example.com/yetanother";
 
+// Cross origin resources
+const char kCrossOriginJsonUrl[] = "https://other.com/resource.json";
+const char kCrossOriginJsUrl[] = "https://other.com/resource.js";
+
 std::vector<uint8_t> CreateSmallBundle() {
   web_package::test::WebBundleBuilder builder(kResourceUrl,
                                               "" /* manifest_url */);
@@ -47,6 +52,19 @@
   return builder.CreateBundle();
 }
 
+std::vector<uint8_t> CreateCrossOriginBundle() {
+  web_package::test::WebBundleBuilder builder(kCrossOriginJsonUrl,
+                                              "" /* manifest_url */);
+  builder.AddExchange(
+      kCrossOriginJsonUrl,
+      {{":status", "200"}, {"content-type", "application/json"}},
+      "{ secret: 1 }");
+  builder.AddExchange(kCrossOriginJsUrl,
+                      {{":status", "200"}, {"content-type", "application/js"}},
+                      "const not_secret = 1;");
+  return builder.CreateBundle();
+}
+
 class TestWebBundleHandle : public mojom::WebBundleHandle {
  public:
   explicit TestWebBundleHandle(
@@ -96,8 +114,8 @@
     mojo::Remote<mojom::WebBundleHandle> handle;
     handle_ = std::make_unique<TestWebBundleHandle>(
         handle.BindNewPipeAndPassReceiver());
-    factory_ = std::make_unique<WebBundleURLLoaderFactory>(GURL(kBundleUrl),
-                                                           std::move(handle));
+    factory_ = std::make_unique<WebBundleURLLoaderFactory>(
+        GURL(kBundleUrl), std::move(handle), base::nullopt);
     factory_->SetBundleStream(std::move(consumer));
   }
 
@@ -118,6 +136,8 @@
     network::ResourceRequest request;
     request.url = url;
     request.method = "GET";
+    request.request_initiator = url::Origin::Create(GURL(kInitiatorUrl));
+
     StartRequestResult result;
     result.client = std::make_unique<network::TestURLLoaderClient>();
     factory_->CreateLoaderAndStart(
@@ -340,4 +360,36 @@
   EXPECT_EQ(last_bundle_error()->second, "Error reading response header.");
 }
 
+TEST_F(WebBundleURLLoaderFactoryTest, CrossOiginJson) {
+  WriteBundle(CreateCrossOriginBundle());
+  FinishWritingBundle();
+
+  auto request = StartRequest(GURL(kCrossOriginJsonUrl));
+  request.client->RunUntilComplete();
+
+  EXPECT_EQ(net::OK, request.client->completion_status().error_code);
+  EXPECT_FALSE(last_bundle_error().has_value());
+  std::string body;
+  ASSERT_TRUE(mojo::BlockingCopyToString(
+      request.client->response_body_release(), &body));
+  EXPECT_TRUE(body.empty())
+      << "body should be empty because JSON is a CORB-protected resource";
+}
+
+TEST_F(WebBundleURLLoaderFactoryTest, CrossOriginJs) {
+  WriteBundle(CreateCrossOriginBundle());
+  FinishWritingBundle();
+
+  auto request = StartRequest(GURL(kCrossOriginJsUrl));
+  request.client->RunUntilComplete();
+
+  EXPECT_EQ(net::OK, request.client->completion_status().error_code);
+  EXPECT_FALSE(last_bundle_error().has_value());
+  std::string body;
+  ASSERT_TRUE(mojo::BlockingCopyToString(
+      request.client->response_body_release(), &body));
+  EXPECT_EQ("const not_secret = 1;", body)
+      << "body should be valid one because JS is not a CORB protected resource";
+}
+
 }  // namespace network
diff --git a/testing/buildbot/chromium.goma.fyi.json b/testing/buildbot/chromium.goma.fyi.json
index 81e3447..730d138 100644
--- a/testing/buildbot/chromium.goma.fyi.json
+++ b/testing/buildbot/chromium.goma.fyi.json
@@ -229,69 +229,6 @@
       }
     ]
   },
-  "Win7 Builder (dbg) Goma Canary": {
-    "additional_compile_targets": [
-      "all"
-    ],
-    "gtest_tests": [
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "base_unittests",
-        "test_id_prefix": "ninja://base:base_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_unittests",
-        "test_id_prefix": "ninja://content/test:content_unittests/"
-      }
-    ]
-  },
-  "Win7 Builder Goma Canary": {
-    "additional_compile_targets": [
-      "all",
-      "pdf_fuzzers"
-    ],
-    "gtest_tests": [
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "base_unittests",
-        "test_id_prefix": "ninja://base:base_unittests/"
-      },
-      {
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_unittests",
-        "test_id_prefix": "ninja://content/test:content_unittests/"
-      }
-    ]
-  },
   "android-archive-dbg-goma-canary": {
     "additional_compile_targets": [
       "all",
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 311d4b6..78c590a 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -3452,23 +3452,6 @@
           'gtest_tests': 'goma_gtests',
         },
       },
-      'Win7 Builder (dbg) Goma Canary': {
-        'additional_compile_targets': [
-          'all',
-        ],
-        'test_suites': {
-          'gtest_tests': 'goma_gtests',
-        },
-      },
-      'Win7 Builder Goma Canary': {
-        'additional_compile_targets': [
-          'all',
-          'pdf_fuzzers',
-        ],
-        'test_suites': {
-          'gtest_tests': 'goma_gtests',
-        },
-      },
       'android-archive-dbg-goma-canary': {
         'additional_compile_targets': [
           'all',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 47ad760..50a0007 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1374,9 +1374,12 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "Enabled_Stage2",
                     "enable_features": [
                         "AutofillEnableAccountWalletStorage"
+                    ],
+                    "disable_features": [
+                        "WalletRequiresFirstSyncSetupComplete"
                     ]
                 }
             ]
@@ -3753,21 +3756,6 @@
             ]
         }
     ],
-    "IOSPreloadDelayWebStateReset": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "PreloadDelayWebStateReset"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSRequestDesktopByDefault": [
         {
             "platforms": [
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 8fdbafe..dbbad849 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -1815,6 +1815,8 @@
       backdrop
       selection
       target-text
+      spelling-error
+      grammar-error
       first-line-inherited
       scrollbar
       scrollbar-thumb
diff --git a/third_party/blink/public/mojom/frame/user_activation_notification_type.mojom b/third_party/blink/public/mojom/frame/user_activation_notification_type.mojom
index 2d3878b2..4f576ff 100644
--- a/third_party/blink/public/mojom/frame/user_activation_notification_type.mojom
+++ b/third_party/blink/public/mojom/frame/user_activation_notification_type.mojom
@@ -31,8 +31,8 @@
   // A media API caused the notification call.
   kMedia,
 
-  // An NFS API caused the notification call.
-  kNativeFileSystem,
+  // A File System Access API caused the notification call.
+  kFileSystemAccess,
 
   // A plugin API caused the notification call.  Deprecated, preserved only for
   // UMA consistency.
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 4bfd95b..ed8e453 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2670,7 +2670,7 @@
   kV8Window_ShowDirectoryPicker_Method = 3342,
   kRTCConstraintEnableRtpDataChannelsTrue = 3344,
   kRTCConstraintEnableRtpDataChannelsFalse = 3345,
-  kNativeFileSystemDragAndDrop = 3346,
+  kFileSystemAccessDragAndDrop = 3346,
   kRTCAdaptivePtime = 3347,
   kHTMLMetaElementReferrerPolicyMultipleTokensAffectingRequest = 3348,
   kNavigationTimingL2 = 3349,
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index cce6ef82..46bdf2e6 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -387,6 +387,12 @@
   // once CreateAndSetCompositorThread() is called.
   scoped_refptr<base::SingleThreadTaskRunner> CompositorThreadTaskRunner();
 
+  // Returns the task runner of the media thread.
+  // This method should only be called on the main thread, or it crashes.
+  virtual scoped_refptr<base::SingleThreadTaskRunner> MediaThreadTaskRunner() {
+    return nullptr;
+  }
+
   // This is called after the compositor thread is created, so the embedder
   // can initiate an IPC to change its thread priority (on Linux we can't
   // increase the nice value, so we need to ask the browser process). This
diff --git a/third_party/blink/public/platform/web_common.h b/third_party/blink/public/platform/web_common.h
index c467fdb..3494c23 100644
--- a/third_party/blink/public/platform/web_common.h
+++ b/third_party/blink/public/platform/web_common.h
@@ -89,7 +89,7 @@
 #if defined(WIN32)
 typedef wchar_t WebUChar;
 #else
-typedef uint16_t WebUChar;
+typedef char16_t WebUChar;
 #endif
 
 // Latin-1 character type
diff --git a/third_party/blink/public/platform/web_drag_data.h b/third_party/blink/public/platform/web_drag_data.h
index 940a4c53..b3f7f89 100644
--- a/third_party/blink/public/platform/web_drag_data.h
+++ b/third_party/blink/public/platform/web_drag_data.h
@@ -45,7 +45,7 @@
 template <typename T>
 class WebVector;
 
-using NativeFileSystemDropData =
+using FileSystemAccessDropData =
     base::RefCountedData<blink::CrossVariantMojoRemote<
         mojom::FileSystemAccessDragDropTokenInterfaceBase>>;
 
@@ -84,7 +84,7 @@
     // Only valid when storage_type == kStorageTypeFilename.
     WebString filename_data;
     WebString display_name_data;
-    scoped_refptr<NativeFileSystemDropData> native_file_system_entry;
+    scoped_refptr<FileSystemAccessDropData> file_system_access_entry;
 
     // Only valid when storage_type == kStorageTypeBinaryData.
     WebData binary_data;
diff --git a/third_party/blink/public/web/web_testing_support.h b/third_party/blink/public/web/web_testing_support.h
index 324b8607..0281ba65 100644
--- a/third_party/blink/public/web/web_testing_support.h
+++ b/third_party/blink/public/web/web_testing_support.h
@@ -42,9 +42,12 @@
   // Injects the |internals| object into the frame for Javascript to use.
   static void InjectInternalsObject(WebLocalFrame*);
   static void InjectInternalsObject(v8::Local<v8::Context>);
-  // Resets the state of the |internals| object, and things that it modifies,
-  // back to their starting state at the end of a test.
-  static void ResetInternalsObject(WebLocalFrame*);
+  // Resets state on the main frame once a test is complete, including:
+  // - disposing any isolated worlds created by the test.
+  // - resetting the state of the |internals| object, and things that it
+  // modifies,
+  //   back to their starting state
+  static void ResetMainFrame(WebLocalFrame*);
 
   // Use this to install a mock scrollbar theme for tests. To use, simply
   // inherit your test class from this or instantiate it manually. The
diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
index 46d24bd..be5cf445 100644
--- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
+++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc
@@ -169,8 +169,8 @@
 
   scoped_refptr<const SecurityOrigin> origin;
   if (world_->IsMainWorld()) {
-    // ActivityLogger for main world is updated within updateDocumentInternal().
-    UpdateDocumentInternal();
+    // This also updates the ActivityLogger for the main world.
+    UpdateDocumentForMainWorld();
     origin = GetFrame()->DomWindow()->GetSecurityOrigin();
   } else {
     UpdateActivityLogger();
@@ -433,7 +433,6 @@
 }
 
 void LocalWindowProxy::UpdateDocument() {
-  DCHECK(world_->IsMainWorld());
   // For an uninitialized main window proxy, there's nothing we need
   // to update. The update is done when the window proxy gets initialized later.
   if (lifecycle_ == Lifecycle::kContextIsUninitialized)
@@ -449,10 +448,14 @@
     return;
   }
 
-  UpdateDocumentInternal();
+  if (!world_->IsMainWorld())
+    return;
+
+  UpdateDocumentForMainWorld();
 }
 
-void LocalWindowProxy::UpdateDocumentInternal() {
+void LocalWindowProxy::UpdateDocumentForMainWorld() {
+  DCHECK(world_->IsMainWorld());
   UpdateActivityLogger();
   UpdateDocumentProperty();
   UpdateSecurityOrigin(GetFrame()->DomWindow()->GetSecurityOrigin());
diff --git a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.h b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.h
index 5fa12678..f23c503 100644
--- a/third_party/blink/renderer/bindings/core/v8/local_window_proxy.h
+++ b/third_party/blink/renderer/bindings/core/v8/local_window_proxy.h
@@ -92,9 +92,10 @@
 
   // Triggers updates of objects that are associated with a Document:
   // - the activity logger
-  // - the document DOM wrapper
+  // - the document DOM wrapper (performance optimization for accessing
+  //   window.document in the main world)
   // - the security origin
-  void UpdateDocumentInternal();
+  void UpdateDocumentForMainWorld();
 
   // The JavaScript wrapper for the document object is cached on the global
   // object for fast access. UpdateDocumentProperty sets the wrapper
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.cc b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
index f33d32c2..105e583 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
@@ -177,7 +177,7 @@
 }
 
 void ScriptController::UpdateDocument() {
-  window_proxy_manager_->MainWorldProxyMaybeUninitialized()->UpdateDocument();
+  window_proxy_manager_->UpdateDocument();
 }
 
 void ScriptController::ExecuteJavaScriptURL(
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
index d173618..1eb157a3 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialization_tag.h
@@ -43,16 +43,15 @@
                           // transferred MojoHandle.
   kBlobTag = 'b',  // uuid:WebCoreString, type:WebCoreString, size:uint64_t ->
                    // Blob (ref)
-  kBlobIndexTag = 'i',      // index:int32_t -> Blob (ref)
-  kFileTag = 'f',           // file:RawFile -> File (ref)
-  kFileIndexTag = 'e',      // index:int32_t -> File (ref)
-  kDOMFileSystemTag = 'd',  // type:int32_t, name:WebCoreString,
-                            // uuid:WebCoreString -> FileSystem (ref)
-  kNativeFileSystemFileHandleTag = 'n',  // name:WebCoreString, index:uint32_t
-                                         // -> NativeFileSystemFileHandle (ref)
-  kNativeFileSystemDirectoryHandleTag =
-      'N',  // name:WebCoreString, index:uint32_t ->
-            // NativeFileSystemDirectoryHandle (ref)
+  kBlobIndexTag = 'i',             // index:int32_t -> Blob (ref)
+  kFileTag = 'f',                  // file:RawFile -> File (ref)
+  kFileIndexTag = 'e',             // index:int32_t -> File (ref)
+  kDOMFileSystemTag = 'd',         // type:int32_t, name:WebCoreString,
+                                   // uuid:WebCoreString -> FileSystem (ref)
+  kFileSystemFileHandleTag = 'n',  // name:WebCoreString, index:uint32_t
+                                   // -> FileSystemFileHandle (ref)
+  kFileSystemDirectoryHandleTag = 'N',  // name:WebCoreString, index:uint32_t ->
+                                        // FileSystemDirectoryHandle (ref)
   kFileListTag =
       'l',  // length:uint32_t, files:RawFile[length] -> FileList (ref)
   kFileListIndexTag =
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.cc b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.cc
index ce93815..0d18b67 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.cc
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.cc
@@ -793,7 +793,7 @@
               "Update WebSerializedScriptValueVersion.h.");
 
 bool SerializedScriptValue::IsOriginCheckRequired() const {
-  return native_file_system_tokens_.size() > 0;
+  return file_system_access_tokens_.size() > 0;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
index 08ef4b8..8c05985 100644
--- a/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
+++ b/third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h
@@ -102,7 +102,7 @@
   using TransferredWasmModulesArray = WTF::Vector<v8::CompiledWasmModule>;
   using MessagePortChannelArray = Vector<MessagePortChannel>;
   using StreamArray = Vector<Stream>;
-  using NativeFileSystemTokensArray =
+  using FileSystemAccessTokensArray =
       Vector<mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>>;
 
   // Increment this for each incompatible change to the wire format.
@@ -275,8 +275,8 @@
     return shared_array_buffers_contents_;
   }
   BlobDataHandleMap& BlobDataHandles() { return blob_data_handles_; }
-  NativeFileSystemTokensArray& NativeFileSystemTokens() {
-    return native_file_system_tokens_;
+  FileSystemAccessTokensArray& FileSystemAccessTokens() {
+    return file_system_access_tokens_;
   }
   MojoScopedHandleArray& MojoHandles() { return mojo_handles_; }
   ArrayBufferContentsArray& GetArrayBufferContentsArray() {
@@ -396,7 +396,7 @@
   BlobDataHandleMap blob_data_handles_;
   MojoScopedHandleArray mojo_handles_;
   SharedArrayBufferContentsArray shared_array_buffers_contents_;
-  NativeFileSystemTokensArray native_file_system_tokens_;
+  FileSystemAccessTokensArray file_system_access_tokens_;
   HashMap<const void* const*, std::unique_ptr<Attachment>> attachments_;
 
   bool has_registered_external_allocation_;
diff --git a/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.cc b/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.cc
index 66bc944..1032a80 100644
--- a/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.cc
+++ b/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.cc
@@ -68,6 +68,13 @@
     WindowProxyMaybeUninitialized(*entry.first)->InitializeIfNeeded();
 }
 
+void WindowProxyManager::ResetIsolatedWorldsForTesting() {
+  for (auto& world_info : isolated_worlds_) {
+    world_info.value->ClearForClose();
+  }
+  isolated_worlds_.clear();
+}
+
 WindowProxyManager::WindowProxyManager(Frame& frame, FrameType frame_type)
     : isolate_(V8PerIsolateData::MainThreadIsolate()),
       frame_(&frame),
@@ -114,6 +121,16 @@
   return window_proxy;
 }
 
+void LocalWindowProxyManager::UpdateDocument() {
+  MainWorldProxyMaybeUninitialized()->UpdateDocument();
+
+  for (auto& entry : isolated_worlds_) {
+    auto* isolated_window_proxy =
+        static_cast<LocalWindowProxy*>(entry.value.Get());
+    isolated_window_proxy->UpdateDocument();
+  }
+}
+
 void LocalWindowProxyManager::UpdateSecurityOrigin(
     const SecurityOrigin* security_origin) {
   static_cast<LocalWindowProxy*>(window_proxy_.Get())
diff --git a/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h b/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h
index ec27fcd..53e1fd7 100644
--- a/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h
+++ b/third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h
@@ -27,7 +27,7 @@
   v8::Isolate* GetIsolate() const { return isolate_; }
 
   void ClearForClose();
-  void CORE_EXPORT ClearForNavigation();
+  CORE_EXPORT void ClearForNavigation();
   void ClearForSwap();
   void ClearForV8MemoryPurge();
 
@@ -45,6 +45,8 @@
     return window_proxy;
   }
 
+  CORE_EXPORT void ResetIsolatedWorldsForTesting();
+
  protected:
   using IsolatedWorldMap = HeapHashMap<int, Member<WindowProxy>>;
   enum class FrameType { kLocal, kRemote };
@@ -93,6 +95,8 @@
     return static_cast<LocalWindowProxy*>(window_proxy_.Get());
   }
 
+  void UpdateDocument();
+
   // Sets the given security origin to the main world's context.  Also updates
   // the security origin of the context for each isolated world.
   void UpdateSecurityOrigin(const SecurityOrigin*);
diff --git a/third_party/blink/renderer/bindings/core/v8/window_proxy_test.cc b/third_party/blink/renderer/bindings/core/v8/window_proxy_test.cc
index 6faf1d5..ca16c31 100644
--- a/third_party/blink/renderer/bindings/core/v8/window_proxy_test.cc
+++ b/third_party/blink/renderer/bindings/core/v8/window_proxy_test.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "third_party/blink/public/web/web_script_source.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
@@ -49,4 +50,46 @@
   ASSERT_GT(ConsoleMessages().size(), 0U);
   EXPECT_EQ("PASSED", ConsoleMessages()[0]);
 }
+
+TEST_F(WindowProxyTest, IsolatedWorldReinitializedAfterNavigation) {
+  SimRequest main_resource("https://example.com/index.html", "text/html");
+  LoadURL("https://example.com/index.html");
+  main_resource.Complete(R"HTML(
+    <!DOCTYPE html>
+    <html><body><iframe></iframe></body></html>
+  )HTML");
+
+  ASSERT_TRUE(MainFrame().FirstChild());
+
+  v8::HandleScope scope(v8::Isolate::GetCurrent());
+
+  const int32_t kIsolatedWorldId = 42;
+
+  // Save a reference to the top `window` in the isolated world.
+  v8::Local<v8::Value> window_top =
+      MainFrame().ExecuteScriptInIsolatedWorldAndReturnValue(
+          kIsolatedWorldId, WebScriptSource("window"));
+  ASSERT_TRUE(window_top->IsObject());
+
+  // Save a reference to the child frame's window proxy in the isolated world.
+  v8::Local<v8::Value> saved_child_window =
+      MainFrame().ExecuteScriptInIsolatedWorldAndReturnValue(
+          kIsolatedWorldId, WebScriptSource("saved = window[0]"));
+  ASSERT_TRUE(saved_child_window->IsObject());
+
+  frame_test_helpers::LoadFrame(MainFrame().FirstChild()->ToWebLocalFrame(),
+                                "data:text/html,<body><p>Hello</p></body>");
+  ASSERT_TRUE(MainFrame().FirstChild());
+
+  // Test if the window proxy of the navigated frame was reinitialized. The
+  // `top` attribute of the saved child frame's window proxy reference should
+  // refer to the same object as the top-level window proxy reference that was
+  // cached earlier.
+  v8::Local<v8::Value> top_via_saved =
+      MainFrame().ExecuteScriptInIsolatedWorldAndReturnValue(
+          kIsolatedWorldId, WebScriptSource("saved.top"));
+  EXPECT_TRUE(top_via_saved->IsObject());
+  EXPECT_TRUE(window_top->StrictEquals(top_via_saved));
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index 7e3bb39d..9425e65 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -1745,8 +1745,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_mime_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_mime_type_array.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_mime_type_array.h",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_file_system_directory_iterator.cc",
-  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_file_system_directory_iterator.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_system_directory_iterator.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_file_system_directory_iterator.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_native_io_file_manager.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 4c8f79e..9a37733 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -206,7 +206,7 @@
           "//third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.idl",
           "//third_party/blink/renderer/modules/eventsource/event_source.idl",
           "//third_party/blink/renderer/modules/eventsource/event_source_init.idl",
-          "//third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.idl",
+          "//third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.idl",
           "//third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl",
           "//third_party/blink/renderer/modules/file_system_access/file_picker_accept_type.idl",
           "//third_party/blink/renderer/modules/file_system_access/file_picker_options.idl",
@@ -219,11 +219,11 @@
           "//third_party/blink/renderer/modules/file_system_access/file_system_handle_permission_descriptor.idl",
           "//third_party/blink/renderer/modules/file_system_access/file_system_remove_options.idl",
           "//third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.idl",
-          "//third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.idl",
+          "//third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.idl",
           "//third_party/blink/renderer/modules/file_system_access/open_file_picker_options.idl",
           "//third_party/blink/renderer/modules/file_system_access/save_file_picker_options.idl",
-          "//third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.idl",
-          "//third_party/blink/renderer/modules/file_system_access/window_native_file_system.idl",
+          "//third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.idl",
+          "//third_party/blink/renderer/modules/file_system_access/window_file_system_access.idl",
           "//third_party/blink/renderer/modules/file_system_access/write_params.idl",
           "//third_party/blink/renderer/modules/filesystem/data_transfer_item_file_system.idl",
           "//third_party/blink/renderer/modules/filesystem/dedicated_worker_global_scope_file_system.idl",
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 ed3f252..327c0e8 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
@@ -13,8 +13,8 @@
 #include "third_party/blink/renderer/bindings/modules/v8/serialization/web_crypto_sub_tags.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/crypto/crypto_key.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h"
 #include "third_party/blink/renderer/modules/filesystem/dom_file_system.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate.h"
 #include "third_party/blink/renderer/modules/peerconnection/rtc_certificate_generator.h"
@@ -52,9 +52,9 @@
           ExecutionContext::From(GetScriptState()), name,
           static_cast<mojom::blink::FileSystemType>(raw_type), KURL(root_url));
     }
-    case kNativeFileSystemFileHandleTag:
-    case kNativeFileSystemDirectoryHandleTag:
-      return ReadNativeFileSystemHandle(tag);
+    case kFileSystemFileHandleTag:
+    case kFileSystemDirectoryHandleTag:
+      return ReadFileSystemHandle(tag);
     case kRTCCertificateTag: {
       String pem_private_key;
       String pem_certificate;
@@ -311,10 +311,9 @@
   return MakeGarbageCollected<CryptoKey>(key);
 }
 
-NativeFileSystemHandle*
-V8ScriptValueDeserializerForModules::ReadNativeFileSystemHandle(
+FileSystemHandle* V8ScriptValueDeserializerForModules::ReadFileSystemHandle(
     SerializationTag tag) {
-  if (!RuntimeEnabledFeatures::NativeFileSystemEnabled(
+  if (!RuntimeEnabledFeatures::FileSystemAccessEnabled(
           ExecutionContext::From(GetScriptState()))) {
     return nullptr;
   }
@@ -326,8 +325,8 @@
   }
 
   // Find the FileSystemHandle's token.
-  SerializedScriptValue::NativeFileSystemTokensArray& tokens_array =
-      GetSerializedScriptValue()->NativeFileSystemTokens();
+  SerializedScriptValue::FileSystemAccessTokensArray& tokens_array =
+      GetSerializedScriptValue()->FileSystemAccessTokens();
   if (token_index >= tokens_array.size()) {
     return nullptr;
   }
@@ -344,34 +343,34 @@
   token->Clone(token_clone.InitWithNewPipeAndPassReceiver());
   tokens_array[token_index] = std::move(token_clone);
 
-  // Use the NativeFileSystemManager to redeem the token to clone the
+  // Use the FileSystemAccessManager to redeem the token to clone the
   // FileSystemHandle.
   ExecutionContext* execution_context =
       ExecutionContext::From(GetScriptState());
   mojo::Remote<mojom::blink::FileSystemAccessManager>
-      native_file_system_manager;
+      file_system_access_manager;
   execution_context->GetBrowserInterfaceBroker().GetInterface(
-      native_file_system_manager.BindNewPipeAndPassReceiver());
+      file_system_access_manager.BindNewPipeAndPassReceiver());
 
   // Clone the FileSystemHandle object.
   switch (tag) {
-    case kNativeFileSystemFileHandleTag: {
+    case kFileSystemFileHandleTag: {
       mojo::PendingRemote<mojom::blink::FileSystemAccessFileHandle> file_handle;
 
-      native_file_system_manager->GetFileHandleFromToken(
+      file_system_access_manager->GetFileHandleFromToken(
           token.Unbind(), file_handle.InitWithNewPipeAndPassReceiver());
 
-      return MakeGarbageCollected<NativeFileSystemFileHandle>(
-          execution_context, name, std::move(file_handle));
+      return MakeGarbageCollected<FileSystemFileHandle>(execution_context, name,
+                                                        std::move(file_handle));
     }
-    case kNativeFileSystemDirectoryHandleTag: {
+    case kFileSystemDirectoryHandleTag: {
       mojo::PendingRemote<mojom::blink::FileSystemAccessDirectoryHandle>
           directory_handle;
 
-      native_file_system_manager->GetDirectoryHandleFromToken(
+      file_system_access_manager->GetDirectoryHandleFromToken(
           token.Unbind(), directory_handle.InitWithNewPipeAndPassReceiver());
 
-      return MakeGarbageCollected<NativeFileSystemDirectoryHandle>(
+      return MakeGarbageCollected<FileSystemDirectoryHandle>(
           execution_context, name, std::move(directory_handle));
     }
     default: {
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
index ccc7f4e..90408ee 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_deserializer_for_modules.h
@@ -11,7 +11,7 @@
 namespace blink {
 
 class CryptoKey;
-class NativeFileSystemHandle;
+class FileSystemHandle;
 class RTCEncodedAudioFrame;
 class RTCEncodedVideoFrame;
 class VideoFrame;
@@ -46,7 +46,7 @@
     return true;
   }
   CryptoKey* ReadCryptoKey();
-  NativeFileSystemHandle* ReadNativeFileSystemHandle(SerializationTag tag);
+  FileSystemHandle* ReadFileSystemHandle(SerializationTag tag);
   RTCEncodedAudioFrame* ReadRTCEncodedAudioFrame();
   RTCEncodedVideoFrame* ReadRTCEncodedVideoFrame();
   VideoFrame* ReadVideoFrame();
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
index 8cb08ef3..fe674e9 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.cc
@@ -62,18 +62,16 @@
     return true;
   }
   if (wrapper_type_info == V8FileSystemFileHandle::GetWrapperTypeInfo() &&
-      RuntimeEnabledFeatures::NativeFileSystemEnabled(
+      RuntimeEnabledFeatures::FileSystemAccessEnabled(
           ExecutionContext::From(GetScriptState()))) {
-    return WriteNativeFileSystemHandle(
-        kNativeFileSystemFileHandleTag,
-        wrappable->ToImpl<NativeFileSystemHandle>());
+    return WriteFileSystemHandle(kFileSystemFileHandleTag,
+                                 wrappable->ToImpl<FileSystemHandle>());
   }
   if (wrapper_type_info == V8FileSystemDirectoryHandle::GetWrapperTypeInfo() &&
-      RuntimeEnabledFeatures::NativeFileSystemEnabled(
+      RuntimeEnabledFeatures::FileSystemAccessEnabled(
           ExecutionContext::From(GetScriptState()))) {
-    return WriteNativeFileSystemHandle(
-        kNativeFileSystemDirectoryHandleTag,
-        wrappable->ToImpl<NativeFileSystemHandle>());
+    return WriteFileSystemHandle(kFileSystemDirectoryHandleTag,
+                                 wrappable->ToImpl<FileSystemHandle>());
   }
   if (wrapper_type_info == V8RTCCertificate::GetWrapperTypeInfo()) {
     RTCCertificate* certificate = wrappable->ToImpl<RTCCertificate>();
@@ -301,20 +299,20 @@
   return true;
 }
 
-bool V8ScriptValueSerializerForModules::WriteNativeFileSystemHandle(
+bool V8ScriptValueSerializerForModules::WriteFileSystemHandle(
     SerializationTag tag,
-    NativeFileSystemHandle* native_file_system_handle) {
+    FileSystemHandle* file_system_handle) {
   mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken> token =
-      native_file_system_handle->Transfer();
+      file_system_handle->Transfer();
 
-  SerializedScriptValue::NativeFileSystemTokensArray& tokens_array =
-      GetSerializedScriptValue()->NativeFileSystemTokens();
+  SerializedScriptValue::FileSystemAccessTokensArray& tokens_array =
+      GetSerializedScriptValue()->FileSystemAccessTokens();
 
   tokens_array.push_back(std::move(token));
   const uint32_t token_index = static_cast<uint32_t>(tokens_array.size() - 1);
 
   WriteTag(tag);
-  WriteUTF8String(native_file_system_handle->name());
+  WriteUTF8String(file_system_handle->name());
   WriteUint32(token_index);
   return true;
 }
diff --git a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.h b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.h
index d15725e..2bee9de 100644
--- a/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.h
+++ b/third_party/blink/renderer/bindings/modules/v8/serialization/v8_script_value_serializer_for_modules.h
@@ -11,7 +11,7 @@
 
 namespace blink {
 
-class NativeFileSystemHandle;
+class FileSystemHandle;
 class RTCEncodedAudioFrame;
 class RTCEncodedVideoFrame;
 class VideoFrame;
@@ -32,9 +32,8 @@
  private:
   void WriteOneByte(uint8_t byte) { WriteRawBytes(&byte, 1); }
   bool WriteCryptoKey(const WebCryptoKey&, ExceptionState&);
-  bool WriteNativeFileSystemHandle(
-      SerializationTag tag,
-      NativeFileSystemHandle* native_file_system_handle);
+  bool WriteFileSystemHandle(SerializationTag tag,
+                             FileSystemHandle* file_system_handle);
   bool WriteRTCEncodedAudioFrame(RTCEncodedAudioFrame*);
   bool WriteRTCEncodedVideoFrame(RTCEncodedVideoFrame*);
   bool WriteVideoFrame(VideoFrame*);
diff --git a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
index 94aebbec..1ec4526 100644
--- a/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
+++ b/third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.cc
@@ -542,8 +542,8 @@
   scoped_refptr<SerializedScriptValue> serialized_value =
       value->CreateSerializedValue();
 
-  serialized_value->NativeFileSystemTokens() =
-      std::move(const_cast<IDBValue*>(value)->NativeFileSystemTokens());
+  serialized_value->FileSystemAccessTokens() =
+      std::move(const_cast<IDBValue*>(value)->FileSystemAccessTokens());
 
   SerializedScriptValue::DeserializeOptions options;
   options.blob_info = &value->BlobInfo();
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 0feb3e6..ca8faf0 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -5222,7 +5222,7 @@
 }
 """))
 
-    if class_like.identifier == "NativeFileSystemDirectoryIterator":
+    if class_like.identifier == "FileSystemDirectoryIterator":
         body.append(
             T("""\
 // Temporary @@asyncIterator support for FileSystemDirectoryHandle
diff --git a/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl b/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
index 8473ac9..8f75aea 100644
--- a/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
+++ b/third_party/blink/renderer/bindings/templates/interface_base.cc.tmpl
@@ -586,7 +586,7 @@
   interface_template->Inherit(intrinsic_iterator_prototype_interface_template);
   {% endif %}
 
-  {% if interface_name == 'NativeFileSystemDirectoryIterator' %}
+  {% if interface_name == 'FileSystemDirectoryIterator' %}
   // Temporary @@asyncIterator support for FileSystemDirectoryHandle
   // TODO(https://crbug.com/1087157): Replace with proper bindings support.
   v8::Local<v8::FunctionTemplate> intrinsic_iterator_prototype_interface_template =
diff --git a/third_party/blink/renderer/core/clipboard/data_object.cc b/third_party/blink/renderer/core/clipboard/data_object.cc
index 763ca44..e21047f 100644
--- a/third_party/blink/renderer/core/clipboard/data_object.cc
+++ b/third_party/blink/renderer/core/clipboard/data_object.cc
@@ -230,10 +230,10 @@
     const String& filename,
     const String& display_name,
     const String& file_system_id,
-    scoped_refptr<NativeFileSystemDropData> native_file_system_entry) {
+    scoped_refptr<FileSystemAccessDropData> file_system_access_entry) {
   InternalAddFileItem(DataObjectItem::CreateFromFileWithFileSystemId(
       File::CreateForUserProvidedFile(filename, display_name), file_system_id,
-      std::move(native_file_system_entry)));
+      std::move(file_system_access_entry)));
 }
 
 void DataObject::AddSharedBuffer(scoped_refptr<SharedBuffer> buffer,
@@ -308,7 +308,7 @@
         has_file_system = true;
         data_object->AddFilename(item.filename_data, item.display_name_data,
                                  data.FilesystemId(),
-                                 item.native_file_system_entry);
+                                 item.file_system_access_entry);
         break;
       case WebDragData::Item::kStorageTypeBinaryData:
         // This should never happen when dragging in.
diff --git a/third_party/blink/renderer/core/clipboard/data_object.h b/third_party/blink/renderer/core/clipboard/data_object.h
index b621b8e4..d7982fb 100644
--- a/third_party/blink/renderer/core/clipboard/data_object.h
+++ b/third_party/blink/renderer/core/clipboard/data_object.h
@@ -99,8 +99,8 @@
   void AddFilename(const String& filename,
                    const String& display_name,
                    const String& file_system_id,
-                   scoped_refptr<NativeFileSystemDropData>
-                       native_file_system_entry = nullptr);
+                   scoped_refptr<FileSystemAccessDropData>
+                       file_system_access_entry = nullptr);
 
   // Used for dragging in filesystem from the desktop.
   void SetFilesystemId(const String& file_system_id) {
diff --git a/third_party/blink/renderer/core/clipboard/data_object_item.cc b/third_party/blink/renderer/core/clipboard/data_object_item.cc
index a4aa6c7..030a7c0 100644
--- a/third_party/blink/renderer/core/clipboard/data_object_item.cc
+++ b/third_party/blink/renderer/core/clipboard/data_object_item.cc
@@ -63,12 +63,12 @@
 DataObjectItem* DataObjectItem::CreateFromFileWithFileSystemId(
     File* file,
     const String& file_system_id,
-    scoped_refptr<NativeFileSystemDropData> native_file_entry) {
+    scoped_refptr<FileSystemAccessDropData> file_system_access_entry) {
   DataObjectItem* item =
       MakeGarbageCollected<DataObjectItem>(kFileKind, file->type());
   item->file_ = file;
   item->file_system_id_ = file_system_id;
-  item->native_file_system_entry_ = native_file_entry;
+  item->file_system_access_entry_ = file_system_access_entry;
   return item;
 }
 
@@ -226,17 +226,17 @@
 }
 
 bool DataObjectItem::HasFileSystemAccessEntry() const {
-  return static_cast<bool>(native_file_system_entry_);
+  return static_cast<bool>(file_system_access_entry_);
 }
 
 mojo::PendingRemote<mojom::blink::FileSystemAccessDragDropToken>
 DataObjectItem::CloneFileSystemAccessEntryToken() const {
   DCHECK(HasFileSystemAccessEntry());
   mojo::Remote<mojom::blink::FileSystemAccessDragDropToken> token_cloner(
-      std::move(native_file_system_entry_->data));
+      std::move(file_system_access_entry_->data));
   mojo::PendingRemote<mojom::blink::FileSystemAccessDragDropToken> token_clone;
   token_cloner->Clone(token_clone.InitWithNewPipeAndPassReceiver());
-  native_file_system_entry_->data = token_cloner.Unbind();
+  file_system_access_entry_->data = token_cloner.Unbind();
   return token_clone;
 }
 
diff --git a/third_party/blink/renderer/core/clipboard/data_object_item.h b/third_party/blink/renderer/core/clipboard/data_object_item.h
index 71706e5..4ac9450 100644
--- a/third_party/blink/renderer/core/clipboard/data_object_item.h
+++ b/third_party/blink/renderer/core/clipboard/data_object_item.h
@@ -59,7 +59,8 @@
   static DataObjectItem* CreateFromFileWithFileSystemId(
       File*,
       const String& file_system_id,
-      scoped_refptr<NativeFileSystemDropData> native_file_entry = nullptr);
+      scoped_refptr<FileSystemAccessDropData> file_system_access_entry =
+          nullptr);
   static DataObjectItem* CreateFromURL(const String& url, const String& title);
   static DataObjectItem* CreateFromHTML(const String& html,
                                         const KURL& base_url);
@@ -106,7 +107,7 @@
     kInternalSource,
   };
 
-  scoped_refptr<NativeFileSystemDropData> native_file_system_entry_;
+  scoped_refptr<FileSystemAccessDropData> file_system_access_entry_;
   DataSource source_;
   ItemKind kind_;
   String type_;
diff --git a/third_party/blink/renderer/core/css/css_selector.cc b/third_party/blink/renderer/core/css/css_selector.cc
index 74a7f48..a1068ac 100644
--- a/third_party/blink/renderer/core/css/css_selector.cc
+++ b/third_party/blink/renderer/core/css/css_selector.cc
@@ -245,6 +245,10 @@
       return kPseudoIdResizer;
     case kPseudoTargetText:
       return kPseudoIdTargetText;
+    case kPseudoSpellingError:
+      return kPseudoIdSpellingError;
+    case kPseudoGrammarError:
+      return kPseudoIdGrammarError;
     case kPseudoUnknown:
     case kPseudoEmpty:
     case kPseudoFirstChild:
@@ -411,6 +415,7 @@
     {"focus-within", CSSSelector::kPseudoFocusWithin},
     {"fullscreen", CSSSelector::kPseudoFullscreen},
     {"future", CSSSelector::kPseudoFutureCue},
+    {"grammar-error", CSSSelector::kPseudoGrammarError},
     {"horizontal", CSSSelector::kPseudoHorizontal},
     {"host", CSSSelector::kPseudoHost},
     {"hover", CSSSelector::kPseudoHover},
@@ -440,6 +445,7 @@
     {"scope", CSSSelector::kPseudoScope},
     {"selection", CSSSelector::kPseudoSelection},
     {"single-button", CSSSelector::kPseudoSingleButton},
+    {"spelling-error", CSSSelector::kPseudoSpellingError},
     {"start", CSSSelector::kPseudoStart},
     {"target", CSSSelector::kPseudoTarget},
     {"target-text", CSSSelector::kPseudoTargetText},
@@ -521,6 +527,12 @@
     return CSSSelector::kPseudoUnknown;
   }
 
+  if ((match->type == CSSSelector::kPseudoSpellingError ||
+       match->type == CSSSelector::kPseudoGrammarError) &&
+      !RuntimeEnabledFeatures::CSSSpellingGrammarErrorsEnabled()) {
+    return CSSSelector::kPseudoUnknown;
+  }
+
   return static_cast<CSSSelector::PseudoType>(match->type);
 }
 
@@ -630,6 +642,8 @@
     case kPseudoWebKitCustomElement:
     case kPseudoSlotted:
     case kPseudoTargetText:
+    case kPseudoSpellingError:
+    case kPseudoGrammarError:
       if (match_ != kPseudoElement)
         pseudo_type_ = kPseudoUnknown;
       break;
@@ -1121,6 +1135,8 @@
     case kPseudoFirstLetter:
     case kPseudoSelection:
     case kPseudoTargetText:
+    case kPseudoSpellingError:
+    case kPseudoGrammarError:
       return true;
     default:
       return false;
diff --git a/third_party/blink/renderer/core/css/css_selector.h b/third_party/blink/renderer/core/css/css_selector.h
index a60e66d4..9287a7c 100644
--- a/third_party/blink/renderer/core/css/css_selector.h
+++ b/third_party/blink/renderer/core/css/css_selector.h
@@ -285,6 +285,8 @@
     kPseudoVideoPersistentAncestor,
     kPseudoTargetText,
     kPseudoDir,
+    kPseudoSpellingError,
+    kPseudoGrammarError,
   };
 
   enum class AttributeMatchType {
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
index 55c709a2..b280d8e 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster.cc
@@ -734,10 +734,11 @@
     AdjustOverflow(style);
 
   // overflow-clip-margin only applies if 'overflow: clip' is set along both
-  // axis.
-  if (style.OverflowX() != EOverflow::kClip ||
-      style.OverflowY() != EOverflow::kClip) {
-    style.SetOverflowClipMargin(LayoutUnit());
+  // axis or 'contain: paint'.
+  if (!style.ContainsPaint() && !(style.OverflowX() == EOverflow::kClip &&
+                                  style.OverflowY() == EOverflow::kClip)) {
+    style.SetOverflowClipMargin(
+        ComputedStyleInitialValues::InitialOverflowClipMargin());
   }
 
   if (StopPropagateTextDecorations(style, element))
diff --git a/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc b/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc
index 5af052b..f7dbe27b9 100644
--- a/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_adjuster_test.cc
@@ -128,6 +128,7 @@
          overflow-clip-margin: 1px;'>
     <div id='vishidden' style='overflow-x: visible; overflow-y: hidden;
          overflow-clip-margin: 1px;'>
+    <div id='containpaint' style='contain: paint; overflow-clip-margin: 1px;'>
     </div>
   )HTML");
   UpdateAllLifecyclePhasesForTest();
@@ -173,6 +174,11 @@
   EXPECT_EQ(EOverflow::kHidden, target->GetComputedStyle()->OverflowX());
   EXPECT_EQ(EOverflow::kAuto, target->GetComputedStyle()->OverflowY());
   EXPECT_EQ(LayoutUnit(), target->GetComputedStyle()->OverflowClipMargin());
+
+  target = GetDocument().getElementById("containpaint");
+  ASSERT_TRUE(target);
+  EXPECT_TRUE(target->GetComputedStyle()->ContainsPaint());
+  EXPECT_EQ(LayoutUnit(1), target->GetComputedStyle()->OverflowClipMargin());
 }
 
 TEST_F(StyleAdjusterTest, TouchActionContentEditableArea) {
diff --git a/third_party/blink/renderer/core/css/rule_feature_set.cc b/third_party/blink/renderer/core/css/rule_feature_set.cc
index d27e923..0c26747 100644
--- a/third_party/blink/renderer/core/css/rule_feature_set.cc
+++ b/third_party/blink/renderer/core/css/rule_feature_set.cc
@@ -180,6 +180,8 @@
     case CSSSelector::kPseudoIs:
     case CSSSelector::kPseudoWhere:
     case CSSSelector::kPseudoTargetText:
+    case CSSSelector::kPseudoSpellingError:
+    case CSSSelector::kPseudoGrammarError:
       return true;
     case CSSSelector::kPseudoUnknown:
     case CSSSelector::kPseudoLeftPage:
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 88d90dfa..6928b23 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -723,7 +723,9 @@
   if (print_context_)
     PrintEnd();
   print_client_.reset();
+#if DCHECK_IS_ON()
   is_in_printing_ = false;
+#endif
 }
 
 WebString WebLocalFrameImpl::AssignedName() const {
@@ -1611,10 +1613,12 @@
 
 void WebLocalFrameImpl::DispatchBeforePrintEvent(
     base::WeakPtr<WebPrintClient> print_client) {
-  CHECK(!is_in_printing_) << "DispatchAfterPrintEvent() should have been "
-                             "called after the previous "
-                             "DispatchBeforePrintEvent() call.";
+#if DCHECK_IS_ON()
+  DCHECK(!is_in_printing_) << "DispatchAfterPrintEvent() should have been "
+                              "called after the previous "
+                              "DispatchBeforePrintEvent() call.";
   is_in_printing_ = true;
+#endif
 
   print_client_ = print_client;
 
@@ -1630,9 +1634,11 @@
 }
 
 void WebLocalFrameImpl::DispatchAfterPrintEvent() {
-  CHECK(is_in_printing_) << "DispatchBeforePrintEvent() should be called "
-                            "before DispatchAfterPrintEvent().";
+#if DCHECK_IS_ON()
+  DCHECK(is_in_printing_) << "DispatchBeforePrintEvent() should be called "
+                             "before DispatchAfterPrintEvent().";
   is_in_printing_ = false;
+#endif
 
   print_client_.reset();
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 4e62d1f..5cc6f7a5 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -584,11 +584,11 @@
   // cleared upon close().
   SelfKeepAlive<WebLocalFrameImpl> self_keep_alive_;
 
+#if DCHECK_IS_ON()
   // True if DispatchBeforePrintEvent() was called, and
   // DispatchAfterPrintEvent() is not called yet.
-  // TODO(crbug.com/1121077) After fixing the bug, make this member variable
-  // only available when DCHECK_IS_ON().
   bool is_in_printing_ = false;
+#endif
 
   // Bookkeeping to suppress redundant scroll and focus requests for an already
   // scrolled and focused editable node.
diff --git a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
index dabdd00..0a9daa58 100644
--- a/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_dom_agent.cc
@@ -180,6 +180,10 @@
       return protocol::DOM::PseudoTypeEnum::Selection;
     case kPseudoIdTargetText:
       return protocol::DOM::PseudoTypeEnum::TargetText;
+    case kPseudoIdSpellingError:
+      return protocol::DOM::PseudoTypeEnum::SpellingError;
+    case kPseudoIdGrammarError:
+      return protocol::DOM::PseudoTypeEnum::GrammarError;
     case kPseudoIdFirstLineInherited:
       return protocol::DOM::PseudoTypeEnum::FirstLineInherited;
     case kPseudoIdScrollbar:
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 6116c07..6cdd702 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -369,6 +369,8 @@
     DEFINE_STRING_MAPPING(PseudoXrOverlay)
     DEFINE_STRING_MAPPING(PseudoTargetText)
     DEFINE_STRING_MAPPING(PseudoModal)
+    DEFINE_STRING_MAPPING(PseudoSpellingError)
+    DEFINE_STRING_MAPPING(PseudoGrammarError)
 #undef DEFINE_STRING_MAPPING
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index f4c1983..5b75ed3 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -251,8 +251,12 @@
   NOT_DESTROYED();
   LayoutBox::UpdateFromStyle();
 
-  bool should_clip_overflow = !StyleRef().IsOverflowVisibleAlongBothAxes() &&
-                              AllowsNonVisibleOverflow();
+  // OverflowClipMargin() is only set if overflow is 'clip' along both axis, or
+  // 'contain: paint'. The later implies clipping along both axis.
+  bool should_clip_overflow =
+      (!StyleRef().IsOverflowVisibleAlongBothAxes() ||
+       StyleRef().OverflowClipMargin() != LayoutUnit()) &&
+      AllowsNonVisibleOverflow();
   if (should_clip_overflow != HasNonVisibleOverflow()) {
     if (GetScrollableArea())
       GetScrollableArea()->InvalidateAllStickyConstraints();
diff --git a/third_party/blink/renderer/core/layout/layout_box_test.cc b/third_party/blink/renderer/core/layout/layout_box_test.cc
index 201558a6..134cc6c 100644
--- a/third_party/blink/renderer/core/layout/layout_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_test.cc
@@ -524,6 +524,7 @@
   SetBodyInnerHTML(R"HTML(
     <style>
       .parent { width: 100px; height: 50px; overflow: clip; }
+      .parent2 { width: 100px; height: 50px; contain: paint; }
       .child { width: 110px; height: 55px; }
     </style>
     <div id="clip1" style="overflow-clip-margin: 4px" class="parent">
@@ -532,6 +533,9 @@
     <div id="clip2" style="overflow-clip-margin: 11px" class="parent">
       <div class="child"></div>
     </div>
+    <div id="clip3" style="overflow-clip-margin: 11px" class="parent2">
+      <div class="child"></div>
+    </div>
   )HTML");
 
   LayoutBox* clip1 = GetLayoutBoxByElementId("clip1");
@@ -543,12 +547,18 @@
   EXPECT_FALSE(clip2->IsScrollContainer());
   EXPECT_TRUE(clip2->ShouldClipOverflowAlongBothAxis());
   EXPECT_EQ(LayoutRect(0, 0, 110, 55), clip2->VisualOverflowRect());
+
+  LayoutBox* clip3 = GetLayoutBoxByElementId("clip3");
+  EXPECT_FALSE(clip3->IsScrollContainer());
+  EXPECT_TRUE(clip3->ShouldClipOverflowAlongBothAxis());
+  EXPECT_EQ(LayoutRect(0, 0, 110, 55), clip3->VisualOverflowRect());
 }
 
 TEST_P(LayoutBoxTest, LayoutOverflowRectWithOverflowClipMargin) {
   SetBodyInnerHTML(R"HTML(
     <style>
       .parent { width: 100px; height: 50px; overflow: clip; }
+      .parent2 { width: 100px; height: 50px; contain: paint; }
       .child { position: relative; top: -5px; left: -6px; width: 110px;
                height: 112px; }
     </style>
@@ -558,6 +568,9 @@
     <div id="clip2" style="overflow-clip-margin: 10px" class="parent">
       <div class="child"></div>
     </div>
+    <div id="clip3" style="overflow-clip-margin: 10px" class="parent2">
+      <div class="child"></div>
+    </div>
   )HTML");
 
   LayoutBox* clip1 = GetLayoutBoxByElementId("clip1");
@@ -571,6 +584,12 @@
   EXPECT_TRUE(clip2->ShouldClipOverflowAlongBothAxis());
   EXPECT_EQ(LayoutRect(-6, -5, 110, 65),
             clip2->LayoutOverflowRectForPropagation(clip2->Parent()));
+
+  LayoutBox* clip3 = GetLayoutBoxByElementId("clip3");
+  EXPECT_FALSE(clip3->IsScrollContainer());
+  EXPECT_TRUE(clip3->ShouldClipOverflowAlongBothAxis());
+  EXPECT_EQ(LayoutRect(-6, -5, 110, 65),
+            clip3->LayoutOverflowRectForPropagation(clip3->Parent()));
 }
 
 TEST_P(LayoutBoxTest, ContentsVisualOverflowPropagation) {
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index f68e51fe..2b5982e2 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -117,6 +117,7 @@
           /* containing_block_expects_minmax_without_percentages */ false,
           /* skip_collapsed_columns */ false);
 
+  // Standard: "used width of the table".
   LayoutUnit used_table_inline_size = ComputeUsedInlineSizeForTableFragment(
       space, table, table_border_padding, grid_min_max);
 
@@ -397,18 +398,8 @@
 
 scoped_refptr<const NGLayoutResult> NGTableLayoutAlgorithm::Layout() {
   DCHECK(!BreakToken());
+
   const bool is_fixed_layout = Style().IsFixedTableLayout();
-
-  // TODO(atotic) review autosizer usage in TablesNG.
-  // Legacy has:
-  //  LayoutTable::UpdateLayout
-  //    TextAutosizer::LayoutScope
-  // TableLayoutAlgorithmAuto::ComputeIntrinsicLogicalWidths
-  //    TextAutosizer::TableLayoutScope
-  base::Optional<TextAutosizer::TableLayoutScope> text_autosizer;
-  if (!is_fixed_layout)
-    text_autosizer.emplace(To<LayoutNGTable>(Node().GetLayoutBox()));
-
   const LogicalSize border_spacing = Style().TableBorderSpacing();
   NGTableGroupedChildren grouped_children(Node());
   const scoped_refptr<const NGTableBorders> table_borders =
@@ -628,8 +619,6 @@
     const PhysicalRect& table_grid_rect,
     const LogicalSize& border_spacing,
     const LayoutUnit table_grid_block_size) {
-  // TODO(atotic) SetHasNonCollapsedBorderDecoration should be a fragment
-  // property, not a flag on LayoutObject.
   container_builder_.SetTableGridRect(table_grid_rect);
   container_builder_.SetTableColumnCount(column_locations.size());
   container_builder_.SetHasCollapsedBorders(table_borders.IsCollapsed());
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
index c3add42..f00325c 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.cc
@@ -305,11 +305,4 @@
   }
 }
 
-void LayoutSVGResourceClipper::WillBeDestroyed() {
-  NOT_DESTROYED();
-  MarkAllClientsForInvalidation(SVGResourceClient::kClipCacheInvalidation |
-                                SVGResourceClient::kPaintInvalidation);
-  LayoutSVGResourceContainer::WillBeDestroyed();
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h
index b290b60..1f4355e 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_clipper.h
@@ -57,11 +57,9 @@
   base::Optional<Path> AsPath();
   sk_sp<const PaintRecord> CreatePaintRecord();
 
- protected:
-  void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
-  void WillBeDestroyed() override;
-
  private:
+  void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
+
   void CalculateLocalClipBounds();
   bool FindCycleFromSelf(SVGResourcesCycleSolver&) const override;
 
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
index c3c6fd4..c7091e3 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
@@ -55,6 +55,37 @@
   ClearInvalidationMask();
 }
 
+void LayoutSVGResourceContainer::InvalidateClientsIfActiveResource() {
+  NOT_DESTROYED();
+  // If this is the 'active' resource (the first element with the specified 'id'
+  // in tree order), notify any clients that they need to reevaluate the
+  // resource's contents.
+  const LocalSVGResource* resource = ResourceForContainer(*this);
+  if (!resource || resource->Target() != GetElement())
+    return;
+  // Pass all available flags. This may be performing unnecessary invalidations
+  // in some cases.
+  MarkAllClientsForInvalidation(SVGResourceClient::kInvalidateAll);
+}
+
+void LayoutSVGResourceContainer::WillBeDestroyed() {
+  NOT_DESTROYED();
+  // The resource is being torn down.
+  InvalidateClientsIfActiveResource();
+  LayoutSVGHiddenContainer::WillBeDestroyed();
+}
+
+void LayoutSVGResourceContainer::StyleDidChange(
+    StyleDifference diff,
+    const ComputedStyle* old_style) {
+  NOT_DESTROYED();
+  LayoutSVGHiddenContainer::StyleDidChange(diff, old_style);
+  if (old_style)
+    return;
+  // The resource has been attached.
+  InvalidateClientsIfActiveResource();
+}
+
 bool LayoutSVGResourceContainer::FindCycle(
     SVGResourcesCycleSolver& solver) const {
   NOT_DESTROYED();
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
index c2020e7..5e18e64 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
@@ -97,7 +97,12 @@
   static bool FindCycleInSubtree(SVGResourcesCycleSolver&,
                                  const LayoutObject& root);
 
+  void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
+  void WillBeDestroyed() override;
+
  private:
+  void InvalidateClientsIfActiveResource();
+
   // Track global (markAllClientsForInvalidation) invalidations to avoid
   // redundant crawls.
   unsigned completed_invalidations_mask_ : 8;
diff --git a/third_party/blink/renderer/core/messaging/blink_cloneable_message_mojom_traits.cc b/third_party/blink/renderer/core/messaging/blink_cloneable_message_mojom_traits.cc
index 313cc8c..83db543 100644
--- a/third_party/blink/renderer/core/messaging/blink_cloneable_message_mojom_traits.cc
+++ b/third_party/blink/renderer/core/messaging/blink_cloneable_message_mojom_traits.cc
@@ -52,7 +52,7 @@
   out->locked_agent_cluster_id = locked_agent_cluster_id;
 
   Vector<PendingRemote<blink::mojom::blink::FileSystemAccessTransferToken>>&
-      tokens = out->message->NativeFileSystemTokens();
+      tokens = out->message->FileSystemAccessTokens();
   if (!data.ReadFileSystemAccessTokens(&tokens)) {
     return false;
   }
diff --git a/third_party/blink/renderer/core/messaging/blink_cloneable_message_mojom_traits.h b/third_party/blink/renderer/core/messaging/blink_cloneable_message_mojom_traits.h
index 6e6978a..43dab8a 100644
--- a/third_party/blink/renderer/core/messaging/blink_cloneable_message_mojom_traits.h
+++ b/third_party/blink/renderer/core/messaging/blink_cloneable_message_mojom_traits.h
@@ -62,7 +62,7 @@
   static Vector<
       mojo::PendingRemote<blink::mojom::blink::FileSystemAccessTransferToken>>
   file_system_access_tokens(blink::BlinkCloneableMessage& input) {
-    return std::move(input.message->NativeFileSystemTokens());
+    return std::move(input.message->FileSystemAccessTokens());
   }
 
   static bool Read(blink::mojom::CloneableMessageDataView,
diff --git a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
index 12bd1f45..5408c7d 100644
--- a/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
+++ b/third_party/blink/renderer/core/messaging/blink_transferable_message.cc
@@ -108,9 +108,9 @@
         std::move(image_bitmap_contents_array));
   }
 
-  // Native file system transfer tokens.
-  for (auto& token : serialized_script_value->NativeFileSystemTokens()) {
-    result.message->NativeFileSystemTokens().push_back(std::move(token));
+  // File System Access transfer tokens.
+  for (auto& token : serialized_script_value->FileSystemAccessTokens()) {
+    result.message->FileSystemAccessTokens().push_back(std::move(token));
   }
 
   return result;
@@ -192,7 +192,7 @@
   // Convert the PendingRemote<FileSystemAccessTransferToken> from the
   // blink::mojom namespace to the blink::mojom::blink namespace.
   for (auto& token : message.file_system_access_tokens) {
-    result.message->NativeFileSystemTokens().push_back(
+    result.message->FileSystemAccessTokens().push_back(
         ToCrossVariantMojoType(std::move(token)));
   }
   return result;
diff --git a/third_party/blink/renderer/core/style/computed_style_constants.h b/third_party/blink/renderer/core/style/computed_style_constants.h
index 2fda3cbc..1811443 100644
--- a/third_party/blink/renderer/core/style/computed_style_constants.h
+++ b/third_party/blink/renderer/core/style/computed_style_constants.h
@@ -65,6 +65,8 @@
   kPseudoIdSelection,
   kPseudoIdScrollbar,
   kPseudoIdTargetText,
+  kPseudoIdSpellingError,
+  kPseudoIdGrammarError,
   // Internal IDs follow:
   kPseudoIdFirstLineInherited,
   kPseudoIdScrollbarThumb,
diff --git a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5 b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
index 1a740b1..48612ba6 100644
--- a/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
+++ b/third_party/blink/renderer/core/style/computed_style_extra_fields.json5
@@ -228,7 +228,7 @@
     {
       name: "PseudoBits",
       field_template: "primitive",
-      field_size: 9,
+      field_size: 11,
       default_value: "kPseudoIdNone",
       type_name: "unsigned",
       custom_copy: true,
diff --git a/third_party/blink/renderer/core/style/computed_style_test.cc b/third_party/blink/renderer/core/style/computed_style_test.cc
index 219f8b1..bb184154 100644
--- a/third_party/blink/renderer/core/style/computed_style_test.cc
+++ b/third_party/blink/renderer/core/style/computed_style_test.cc
@@ -155,12 +155,12 @@
 }
 
 TEST(ComputedStyleTest, LastPublicPseudoElementStyle) {
-  static_assert(kFirstInternalPseudoId - 1 == kPseudoIdTargetText,
+  static_assert(kFirstInternalPseudoId - 1 == kPseudoIdGrammarError,
                 "Make sure we are testing the last public pseudo id");
 
   scoped_refptr<ComputedStyle> style = ComputedStyle::Create();
-  style->SetHasPseudoElementStyle(kPseudoIdTargetText);
-  EXPECT_TRUE(style->HasPseudoElementStyle(kPseudoIdTargetText));
+  style->SetHasPseudoElementStyle(kPseudoIdGrammarError);
+  EXPECT_TRUE(style->HasPseudoElementStyle(kPseudoIdGrammarError));
   EXPECT_TRUE(style->HasAnyPseudoElementStyles());
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_resource_client.h b/third_party/blink/renderer/core/svg/svg_resource_client.h
index 19ab994..0de2357 100644
--- a/third_party/blink/renderer/core/svg/svg_resource_client.h
+++ b/third_party/blink/renderer/core/svg/svg_resource_client.h
@@ -28,6 +28,9 @@
     kPaintPropertiesInvalidation = 1 << 3,
     kClipCacheInvalidation = 1 << 4,
     kFilterCacheInvalidation = 1 << 5,
+    kInvalidateAll = kLayoutInvalidation | kBoundariesInvalidation |
+                     kPaintInvalidation | kPaintPropertiesInvalidation |
+                     kClipCacheInvalidation | kFilterCacheInvalidation,
   };
   virtual void ResourceContentChanged(InvalidationModeMask) = 0;
   virtual void ResourceElementChanged() = 0;
diff --git a/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc b/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc
index 3997607..6d8435f 100644
--- a/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc
+++ b/third_party/blink/renderer/core/timing/measure_memory/measure_memory_controller.cc
@@ -166,7 +166,8 @@
 MemoryBreakdownEntry* ConvertBreakdown(
     const WebMemoryBreakdownEntryPtr& breakdown_entry) {
   auto* result = MemoryBreakdownEntry::Create();
-  result->setBytes(breakdown_entry->bytes);
+  DCHECK(breakdown_entry->memory);
+  result->setBytes(breakdown_entry->memory->bytes);
   HeapVector<Member<MemoryAttribution>> attribution;
   for (const auto& entry : breakdown_entry->attribution) {
     attribution.push_back(ConvertAttribution(entry));
@@ -187,7 +188,9 @@
 MemoryMeasurement* ConvertResult(const WebMemoryMeasurementPtr& measurement) {
   HeapVector<Member<MemoryBreakdownEntry>> breakdown;
   for (const auto& entry : measurement->breakdown) {
-    breakdown.push_back(ConvertBreakdown(entry));
+    // Skip breakdowns that didn't get a measurement.
+    if (entry->memory)
+      breakdown.push_back(ConvertBreakdown(entry));
   }
   // Add an empty breakdown entry as required by the spec.
   // See https://github.com/WICG/performance-measure-memory/issues/10.
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_change_event.idl b/third_party/blink/renderer/modules/cookie_store/cookie_change_event.idl
index 049c5bd..0522d29 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_change_event.idl
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_change_event.idl
@@ -3,14 +3,13 @@
 // found in the LICENSE file.
 
 // Used to signal cookie changes to Document contexts.
-// https://github.com/WICG/async-cookies-api/blob/gh-pages/explainer.md
+// https://wicg.github.io/cookie-store/explainer.html
 //
 // See extendable_cookie_change_event.idl for the equivalent event in
 // ServiceWorker contexts.
 
 [
   Exposed=Window,
-  RuntimeEnabled=CookieStoreDocument,
   SecureContext
 ] interface CookieChangeEvent : Event {
   constructor(DOMString type, optional CookieChangeEventInit eventInitDict = {});
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_change_event_init.idl b/third_party/blink/renderer/modules/cookie_store/cookie_change_event_init.idl
index ebce1f1..6b37d54 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_change_event_init.idl
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_change_event_init.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://github.com/WICG/async-cookies-api/blob/gh-pages/explainer.md
+// https://wicg.github.io/cookie-store/explainer.html
 
 dictionary CookieChangeEventInit : EventInit {
   CookieList changed;
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store.idl b/third_party/blink/renderer/modules/cookie_store/cookie_store.idl
index b1bb1b83..c617c11 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store.idl
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store.idl
@@ -6,7 +6,6 @@
 
 [
   Exposed=(ServiceWorker,Window),
-  RuntimeEnabled=CookieStoreDocument,
   SecureContext
 ] interface CookieStore : EventTarget {
   // https://wicg.github.io/cookie-store/explainer.html#the-query-api
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store_delete_options.idl b/third_party/blink/renderer/modules/cookie_store/cookie_store_delete_options.idl
index d3b03cb..a519680 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store_delete_options.idl
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store_delete_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://github.com/WICG/async-cookies-api/blob/gh-pages/explainer.md
+// https://wicg.github.io/cookie-store/explainer.html
 
 dictionary CookieStoreDeleteOptions {
   required USVString name;
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store_get_options.idl b/third_party/blink/renderer/modules/cookie_store/cookie_store_get_options.idl
index 1202deb..c199461 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store_get_options.idl
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store_get_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://github.com/WICG/async-cookies-api/blob/gh-pages/explainer.md
+// https://wicg.github.io/cookie-store/explainer.html
 
 dictionary CookieStoreGetOptions {
   USVString name;
diff --git a/third_party/blink/renderer/modules/cookie_store/cookie_store_manager.idl b/third_party/blink/renderer/modules/cookie_store/cookie_store_manager.idl
index 8876f55..e97ebc5 100644
--- a/third_party/blink/renderer/modules/cookie_store/cookie_store_manager.idl
+++ b/third_party/blink/renderer/modules/cookie_store/cookie_store_manager.idl
@@ -5,7 +5,7 @@
 // https://wicg.github.io/cookie-store/explainer.html#the-change-events-api
 
 [
-  Exposed(ServiceWorker CookieStoreWorker, Window CookieStoreDocument),
+  Exposed=(ServiceWorker,Window),
   SecureContext
 ] interface CookieStoreManager {
   [CallWith=ScriptState, MeasureAs=CookieStoreAPI, RaisesException]
diff --git a/third_party/blink/renderer/modules/cookie_store/extendable_cookie_change_event.idl b/third_party/blink/renderer/modules/cookie_store/extendable_cookie_change_event.idl
index 72f5588a..3d2124a 100644
--- a/third_party/blink/renderer/modules/cookie_store/extendable_cookie_change_event.idl
+++ b/third_party/blink/renderer/modules/cookie_store/extendable_cookie_change_event.idl
@@ -3,13 +3,12 @@
 // found in the LICENSE file.
 
 // Used to signal cookie changes to ServiceWorker contexts.
-// https://github.com/WICG/async-cookies-api/blob/gh-pages/explainer.md
+// https://wicg.github.io/cookie-store/explainer.html
 //
 // See cookie_change_event.idl for the equivalent event in Document contexts.
 
 [
-  Exposed=ServiceWorker,
-  RuntimeEnabled=CookieStoreWorker
+  Exposed=ServiceWorker
 ] interface ExtendableCookieChangeEvent : ExtendableEvent {
   constructor(DOMString type, optional ExtendableCookieChangeEventInit eventInitDict = {});
   [
diff --git a/third_party/blink/renderer/modules/cookie_store/service_worker_global_scope_cookie_store.idl b/third_party/blink/renderer/modules/cookie_store/service_worker_global_scope_cookie_store.idl
index 72c2d0cc..ccc8e5a 100644
--- a/third_party/blink/renderer/modules/cookie_store/service_worker_global_scope_cookie_store.idl
+++ b/third_party/blink/renderer/modules/cookie_store/service_worker_global_scope_cookie_store.idl
@@ -2,10 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://github.com/WICG/async-cookies-api/blob/gh-pages/explainer.md
+// https://wicg.github.io/cookie-store/explainer.html
 
 [
-  RuntimeEnabled=CookieStoreWorker,
   ImplementedAs=GlobalCookieStore
 ] partial interface ServiceWorkerGlobalScope {
   [SameObject] readonly attribute CookieStore cookieStore;
diff --git a/third_party/blink/renderer/modules/cookie_store/service_worker_registration_cookies.idl b/third_party/blink/renderer/modules/cookie_store/service_worker_registration_cookies.idl
index d602e4d..151f2c6 100644
--- a/third_party/blink/renderer/modules/cookie_store/service_worker_registration_cookies.idl
+++ b/third_party/blink/renderer/modules/cookie_store/service_worker_registration_cookies.idl
@@ -4,7 +4,6 @@
 
 [
   Exposed=(ServiceWorker,Window),
-  RuntimeEnabled=CookieStoreWorker,
   ImplementedAs=CookieStoreManager
 ] partial interface ServiceWorkerRegistration {
   [SameObject] readonly attribute CookieStoreManager cookies;
diff --git a/third_party/blink/renderer/modules/cookie_store/window_cookie_store.idl b/third_party/blink/renderer/modules/cookie_store/window_cookie_store.idl
index c86f9a4f..6882018 100644
--- a/third_party/blink/renderer/modules/cookie_store/window_cookie_store.idl
+++ b/third_party/blink/renderer/modules/cookie_store/window_cookie_store.idl
@@ -2,10 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://github.com/WICG/async-cookies-api/blob/gh-pages/explainer.md
+// https://wicg.github.io/cookie-store/explainer.html
 
 [
-    RuntimeEnabled=CookieStoreDocument,
     ImplementedAs=GlobalCookieStore,
     SecureContext
 ] partial interface Window {
diff --git a/third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.cc b/third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.cc
index e567f7e..4fbe937d 100644
--- a/third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.cc
+++ b/third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/csspaint/background_color_paint_worklet.h"
 
 #include "third_party/blink/renderer/core/animation/animation_effect.h"
+#include "third_party/blink/renderer/core/animation/css/compositor_keyframe_double.h"
 #include "third_party/blink/renderer/core/animation/css_color_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/element_animations.h"
 #include "third_party/blink/renderer/core/css/css_color_value.h"
@@ -29,16 +30,20 @@
       const FloatSize& container_size,
       int worklet_id,
       const Vector<Color>& animated_colors,
+      const Vector<double>& offsets,
       cc::PaintWorkletInput::PropertyKeys property_keys)
       : PaintWorkletInput(container_size, worklet_id, std::move(property_keys)),
-        animated_colors_(animated_colors) {}
+        animated_colors_(animated_colors),
+        offsets_(offsets) {}
 
   ~BackgroundColorPaintWorkletInput() override = default;
 
   const Vector<Color>& AnimatedColors() const { return animated_colors_; }
+  const Vector<double>& Offsets() const { return offsets_; }
 
  private:
   Vector<Color> animated_colors_;
+  Vector<double> offsets_;
 };
 
 class BackgroundColorPaintWorkletProxyClient
@@ -64,18 +69,39 @@
         static_cast<const BackgroundColorPaintWorkletInput*>(compositor_input);
     FloatSize container_size = input->ContainerSize();
     Vector<Color> animated_colors = input->AnimatedColors();
+    Vector<double> offsets = input->Offsets();
     DCHECK_GT(animated_colors.size(), 1u);
+    DCHECK_EQ(animated_colors.size(), offsets.size());
 
     DCHECK_EQ(animated_property_values.size(), 1u);
     const auto& entry = animated_property_values.begin();
     float progress = entry->second.float_value.value();
+
+    // Get the start and end color based on the progress and offsets.
+    DCHECK_EQ(offsets.front(), 0);
+    DCHECK_EQ(offsets.back(), 1);
+    unsigned result_index = -1;
+    for (unsigned i = 0; i < offsets.size() - 1; i++) {
+      if (progress <= offsets[i + 1]) {
+        result_index = i;
+        break;
+      }
+    }
+    DCHECK_GE(result_index, 0u);
+    // Because the progress is a global one, we need to adjust it with offsets.
+    float adjusted_progress =
+        (progress - offsets[result_index]) /
+        (offsets[result_index + 1] - offsets[result_index]);
     std::unique_ptr<InterpolableValue> from =
-        CSSColorInterpolationType::CreateInterpolableColor(animated_colors[0]);
+        CSSColorInterpolationType::CreateInterpolableColor(
+            animated_colors[result_index]);
     std::unique_ptr<InterpolableValue> to =
-        CSSColorInterpolationType::CreateInterpolableColor(animated_colors[1]);
+        CSSColorInterpolationType::CreateInterpolableColor(
+            animated_colors[result_index + 1]);
     std::unique_ptr<InterpolableValue> result =
-        CSSColorInterpolationType::CreateInterpolableColor(animated_colors[1]);
-    from->Interpolate(*to, progress, *result);
+        CSSColorInterpolationType::CreateInterpolableColor(
+            animated_colors[result_index + 1]);
+    from->Interpolate(*to, adjusted_progress, *result);
     Color rgba = CSSColorInterpolationType::GetRGBA(*(result.get()));
     SkColor current_color = static_cast<SkColor>(rgba);
 
@@ -88,6 +114,7 @@
   }
 };
 
+// TODO(crbug.com/1163949): Support animation keyframes without 0% or 100%.
 void GetColorsFromStringKeyframe(const PropertySpecificKeyframe* frame,
                                  Vector<Color>* animated_colors,
                                  const Element* element) {
@@ -103,6 +130,13 @@
   animated_colors->push_back(color_value->Value());
 }
 
+void GetCompositorKeyframeOffset(const PropertySpecificKeyframe* frame,
+                                 Vector<double>* offsets) {
+  const CompositorKeyframeDouble& value =
+      To<CompositorKeyframeDouble>(*(frame->GetCompositorKeyframeValue()));
+  offsets->push_back(value.ToDouble());
+}
+
 void GetColorsFromTransitionKeyframe(const PropertySpecificKeyframe* frame,
                                      Vector<Color>* animated_colors,
                                      const Element* element) {
@@ -140,6 +174,7 @@
     const Node* node) {
   DCHECK(node->IsElementNode());
   Vector<Color> animated_colors;
+  Vector<double> offsets;
   const Element* element = static_cast<const Element*>(node);
   ElementAnimations* element_animations = element->GetElementAnimations();
   // TODO(crbug.com/1153672): implement main-thread fall back logic for
@@ -154,16 +189,12 @@
         model->GetPropertySpecificKeyframes(
             PropertyHandle(GetCSSPropertyBackgroundColor()));
     DCHECK_GE(frames->size(), 2u);
-    // TODO(crbug.com/1153671): right now we keep the first and last keyframe
-    // values only, we need to keep all keyframe values.
-    if (model->IsStringKeyframeEffectModel()) {
-      GetColorsFromStringKeyframe(frames->front(), &animated_colors, element);
-      GetColorsFromStringKeyframe(frames->back(), &animated_colors, element);
-    } else {
-      GetColorsFromTransitionKeyframe(frames->front(), &animated_colors,
-                                      element);
-      GetColorsFromTransitionKeyframe(frames->back(), &animated_colors,
-                                      element);
+    for (const auto& frame : *frames) {
+      if (model->IsStringKeyframeEffectModel())
+        GetColorsFromStringKeyframe(frame, &animated_colors, element);
+      else
+        GetColorsFromTransitionKeyframe(frame, &animated_colors, element);
+      GetCompositorKeyframeOffset(frame, &offsets);
     }
   }
 
@@ -178,7 +209,7 @@
       element_id);
   scoped_refptr<BackgroundColorPaintWorkletInput> input =
       base::MakeRefCounted<BackgroundColorPaintWorkletInput>(
-          container_size, worklet_id_, animated_colors,
+          container_size, worklet_id_, animated_colors, offsets,
           std::move(input_property_keys));
   return PaintWorkletDeferredImage::Create(std::move(input), container_size);
 }
diff --git a/third_party/blink/renderer/modules/exported/BUILD.gn b/third_party/blink/renderer/modules/exported/BUILD.gn
index b9ffc21..e48fb21 100644
--- a/third_party/blink/renderer/modules/exported/BUILD.gn
+++ b/third_party/blink/renderer/modules/exported/BUILD.gn
@@ -31,6 +31,7 @@
   deps = [
     "//skia",
     "//third_party/blink/renderer/bindings/modules/v8:testing",
+    "//third_party/blink/renderer/core",
     "//third_party/blink/renderer/core:testing",
     "//third_party/blink/renderer/modules:modules_testing",
     "//third_party/blink/renderer/platform",
diff --git a/third_party/blink/renderer/modules/exported/web_testing_support.cc b/third_party/blink/renderer/modules/exported/web_testing_support.cc
index f31bd370..b553315 100644
--- a/third_party/blink/renderer/modules/exported/web_testing_support.cc
+++ b/third_party/blink/renderer/modules/exported/web_testing_support.cc
@@ -25,7 +25,8 @@
 
 #include "third_party/blink/public/web/web_testing_support.h"
 
-#include "third_party/blink/public/web/web_local_frame.h"
+#include "third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h"
+#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/testing/scoped_mock_overlay_scrollbars.h"
 #include "third_party/blink/renderer/core/testing/v8/web_core_test_support.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -108,9 +109,14 @@
   web_core_test_support::InjectInternalsObject(context);
 }
 
-void WebTestingSupport::ResetInternalsObject(WebLocalFrame* frame) {
+void WebTestingSupport::ResetMainFrame(WebLocalFrame* main_frame) {
+  auto* main_frame_impl = To<WebLocalFrameImpl>(main_frame);
   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
-  web_core_test_support::ResetInternalsObject(frame->MainWorldScriptContext());
+  web_core_test_support::ResetInternalsObject(
+      main_frame_impl->MainWorldScriptContext());
+  main_frame_impl->GetFrame()
+      ->GetWindowProxyManager()
+      ->ResetIsolatedWorldsForTesting();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/file_system_access/BUILD.gn b/third_party/blink/renderer/modules/file_system_access/BUILD.gn
index 38f13af..6727d5d 100644
--- a/third_party/blink/renderer/modules/file_system_access/BUILD.gn
+++ b/third_party/blink/renderer/modules/file_system_access/BUILD.gn
@@ -6,26 +6,26 @@
 
 blink_modules_sources("file_system_access") {
   sources = [
-    "data_transfer_item_native_file_system.cc",
-    "data_transfer_item_native_file_system.h",
-    "global_native_file_system.cc",
-    "global_native_file_system.h",
-    "native_file_system_directory_handle.cc",
-    "native_file_system_directory_handle.h",
-    "native_file_system_directory_iterator.cc",
-    "native_file_system_directory_iterator.h",
-    "native_file_system_error.cc",
-    "native_file_system_error.h",
-    "native_file_system_file_handle.cc",
-    "native_file_system_file_handle.h",
-    "native_file_system_handle.cc",
-    "native_file_system_handle.h",
-    "native_file_system_underlying_sink.cc",
-    "native_file_system_underlying_sink.h",
-    "native_file_system_writable_file_stream.cc",
-    "native_file_system_writable_file_stream.h",
-    "storage_manager_native_file_system.cc",
-    "storage_manager_native_file_system.h",
+    "data_transfer_item_file_system_access.cc",
+    "data_transfer_item_file_system_access.h",
+    "file_system_access_error.cc",
+    "file_system_access_error.h",
+    "file_system_directory_handle.cc",
+    "file_system_directory_handle.h",
+    "file_system_directory_iterator.cc",
+    "file_system_directory_iterator.h",
+    "file_system_file_handle.cc",
+    "file_system_file_handle.h",
+    "file_system_handle.cc",
+    "file_system_handle.h",
+    "file_system_underlying_sink.cc",
+    "file_system_underlying_sink.h",
+    "file_system_writable_file_stream.cc",
+    "file_system_writable_file_stream.h",
+    "global_file_system_access.cc",
+    "global_file_system_access.h",
+    "storage_manager_file_system_access.cc",
+    "storage_manager_file_system_access.h",
   ]
 
   deps = [ "//third_party/blink/renderer/platform" ]
@@ -33,7 +33,7 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "global_native_file_system_test.cc" ]
+  sources = [ "global_file_system_access_test.cc" ]
 
   configs += [
     "//third_party/blink/renderer:config",
diff --git a/third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.cc b/third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.cc
similarity index 93%
rename from third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.cc
rename to third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.cc
index b370f56..3e54366 100644
--- a/third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.cc
+++ b/third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.h"
+#include "third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.h"
 
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
@@ -18,9 +18,9 @@
 #include "third_party/blink/renderer/core/clipboard/data_transfer.h"
 #include "third_party/blink/renderer/core/clipboard/data_transfer_item.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -29,7 +29,7 @@
 namespace blink {
 
 // static
-ScriptPromise DataTransferItemNativeFileSystem::getAsFileSystemHandle(
+ScriptPromise DataTransferItemFileSystemAccess::getAsFileSystemHandle(
     ScriptState* script_state,
     DataTransferItem& data_transfer_item) {
   if (!data_transfer_item.GetDataTransfer()->CanReadData()) {
@@ -72,7 +72,7 @@
             ScriptState* script_state = resolver->GetScriptState();
             if (!script_state)
               return;
-            resolver->Resolve(NativeFileSystemHandle::CreateFromMojoEntry(
+            resolver->Resolve(FileSystemHandle::CreateFromMojoEntry(
                 std::move(entry), ExecutionContext::From(script_state)));
           },
           std::move(nfs_manager), WrapPersistent(resolver)));
diff --git a/third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.h b/third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.h
similarity index 76%
rename from third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.h
rename to third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.h
index e917182..6db9014 100644
--- a/third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.h
+++ b/third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_DATA_TRANSFER_ITEM_NATIVE_FILE_SYSTEM_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_DATA_TRANSFER_ITEM_NATIVE_FILE_SYSTEM_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_DATA_TRANSFER_ITEM_FILE_SYSTEM_ACCESS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_DATA_TRANSFER_ITEM_FILE_SYSTEM_ACCESS_H_
 
 #include "third_party/blink/renderer/core/clipboard/data_transfer_item.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_handle.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 
 namespace blink {
 
-class DataTransferItemNativeFileSystem {
-  STATIC_ONLY(DataTransferItemNativeFileSystem);
+class DataTransferItemFileSystemAccess {
+  STATIC_ONLY(DataTransferItemFileSystemAccess);
 
  public:
   static ScriptPromise getAsFileSystemHandle(ScriptState*, DataTransferItem&);
@@ -21,4 +21,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_DATA_TRANSFER_ITEM_NATIVE_FILE_SYSTEM_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_DATA_TRANSFER_ITEM_FILE_SYSTEM_ACCESS_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.idl b/third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.idl
new file mode 100644
index 0000000..a34eae0
--- /dev/null
+++ b/third_party/blink/renderer/modules/file_system_access/data_transfer_item_file_system_access.idl
@@ -0,0 +1,11 @@
+// Copyright 2020 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.
+
+[
+    ImplementedAs=DataTransferItemFileSystemAccess,
+    RuntimeEnabled=FileSystemAccess
+] partial interface DataTransferItem {
+    [CallWith=ScriptState, MeasureAs=FileSystemAccessDragAndDrop]
+    Promise<FileSystemHandle?> getAsFileSystemHandle();
+};
diff --git a/third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.idl b/third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.idl
deleted file mode 100644
index 48a9f1d..0000000
--- a/third_party/blink/renderer/modules/file_system_access/data_transfer_item_native_file_system.idl
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2020 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.
-
-[
-    ImplementedAs=DataTransferItemNativeFileSystem,
-    RuntimeEnabled=NativeFileSystem
-] partial interface DataTransferItem {
-    [CallWith=ScriptState, MeasureAs=NativeFileSystemDragAndDrop] Promise<FileSystemHandle?> getAsFileSystemHandle();
-};
diff --git a/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl b/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
index c6160d5e..1f6af0e 100644
--- a/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/directory_picker_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#dictdef-directorypickeroptions
+// https://wicg.github.io/file-system-access/#dictdef-directorypickeroptions
 dictionary DirectoryPickerOptions {
   [RuntimeEnabled=FileSystemAccessAPIExperimental] CommonDirectory? startIn;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/file_picker_accept_type.idl b/third_party/blink/renderer/modules/file_system_access/file_picker_accept_type.idl
index dc3f822..9dfee44 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_picker_accept_type.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_picker_accept_type.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#dictdef-filepickeraccepttype
+// https://wicg.github.io/file-system-access/#dictdef-filepickeraccepttype
 dictionary FilePickerAcceptType {
   USVString description;
   record<USVString, (USVString or sequence<USVString>)> accept;
diff --git a/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl b/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
index 3156525..3279e8a 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_picker_options.idl
@@ -12,7 +12,7 @@
   "videos",
 };
 
-// https://wicg.github.io/native-file-system/#dictdef-filepickeroptions
+// https://wicg.github.io/file-system-access/#dictdef-filepickeroptions
 dictionary FilePickerOptions {
   sequence<FilePickerAcceptType> types;
   boolean excludeAcceptAllOption = false;
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_error.cc b/third_party/blink/renderer/modules/file_system_access/file_system_access_error.cc
similarity index 96%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_error.cc
rename to third_party/blink/renderer/modules/file_system_access/file_system_access_error.cc
index 128f63a..541a2355 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_error.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_error.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
 
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
@@ -13,7 +13,7 @@
 #include "third_party/blink/renderer/platform/heap/heap.h"
 
 namespace blink {
-namespace native_file_system_error {
+namespace file_system_access_error {
 
 void Reject(ScriptPromiseResolver* resolver,
             const mojom::blink::FileSystemAccessError& error) {
@@ -65,5 +65,5 @@
   }
 }
 
-}  // namespace native_file_system_error
+}  // namespace file_system_access_error
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_error.h b/third_party/blink/renderer/modules/file_system_access/file_system_access_error.h
similarity index 76%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_error.h
rename to third_party/blink/renderer/modules/file_system_access/file_system_access_error.h
index caa5d2e70f..5165fee2 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_error.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_access_error.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_ERROR_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_ERROR_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_ERROR_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_ERROR_H_
 
 #include "base/files/file.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom-blink-forward.h"
 
 namespace blink {
 class ScriptPromiseResolver;
-namespace native_file_system_error {
+namespace file_system_access_error {
 
 // Rejects |resolver| with an appropriate exception if |status| represents an
 // error. Resolves |resolver| with undefined otherwise.
@@ -22,7 +22,7 @@
 void Reject(ScriptPromiseResolver* resolver,
             const mojom::blink::FileSystemAccessError& error);
 
-}  // namespace native_file_system_error
+}  // namespace file_system_access_error
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_ERROR_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_ACCESS_ERROR_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_create_writer_options.idl b/third_party/blink/renderer/modules/file_system_access/file_system_create_writer_options.idl
index 4897040..5e9d8a4 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_create_writer_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_create_writer_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#dictdef-filesystemcreatewriteroptions
+// https://wicg.github.io/file-system-access/#dictdef-filesystemcreatewriteroptions
 dictionary FileSystemCreateWriterOptions {
   boolean keepExistingData = false;
   [RuntimeEnabled=FileSystemAccessAPIExperimental] boolean autoClose = false;
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.cc b/third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.cc
similarity index 78%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.cc
rename to third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.cc
index ca377b0c..571df52 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h"
 
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -19,9 +19,9 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -30,35 +30,33 @@
 
 using mojom::blink::FileSystemAccessErrorPtr;
 
-NativeFileSystemDirectoryHandle::NativeFileSystemDirectoryHandle(
+FileSystemDirectoryHandle::FileSystemDirectoryHandle(
     ExecutionContext* context,
     const String& name,
     mojo::PendingRemote<mojom::blink::FileSystemAccessDirectoryHandle> mojo_ptr)
-    : NativeFileSystemHandle(context, name), mojo_ptr_(context) {
+    : FileSystemHandle(context, name), mojo_ptr_(context) {
   mojo_ptr_.Bind(std::move(mojo_ptr),
                  context->GetTaskRunner(TaskType::kMiscPlatformAPI));
   DCHECK(mojo_ptr_.is_bound());
 }
 
-NativeFileSystemDirectoryIterator* NativeFileSystemDirectoryHandle::entries() {
-  return MakeGarbageCollected<NativeFileSystemDirectoryIterator>(
-      this, NativeFileSystemDirectoryIterator::Mode::kKeyValue,
+FileSystemDirectoryIterator* FileSystemDirectoryHandle::entries() {
+  return MakeGarbageCollected<FileSystemDirectoryIterator>(
+      this, FileSystemDirectoryIterator::Mode::kKeyValue,
       GetExecutionContext());
 }
 
-NativeFileSystemDirectoryIterator* NativeFileSystemDirectoryHandle::keys() {
-  return MakeGarbageCollected<NativeFileSystemDirectoryIterator>(
-      this, NativeFileSystemDirectoryIterator::Mode::kKey,
-      GetExecutionContext());
+FileSystemDirectoryIterator* FileSystemDirectoryHandle::keys() {
+  return MakeGarbageCollected<FileSystemDirectoryIterator>(
+      this, FileSystemDirectoryIterator::Mode::kKey, GetExecutionContext());
 }
 
-NativeFileSystemDirectoryIterator* NativeFileSystemDirectoryHandle::values() {
-  return MakeGarbageCollected<NativeFileSystemDirectoryIterator>(
-      this, NativeFileSystemDirectoryIterator::Mode::kValue,
-      GetExecutionContext());
+FileSystemDirectoryIterator* FileSystemDirectoryHandle::values() {
+  return MakeGarbageCollected<FileSystemDirectoryIterator>(
+      this, FileSystemDirectoryIterator::Mode::kValue, GetExecutionContext());
 }
 
-ScriptPromise NativeFileSystemDirectoryHandle::getFileHandle(
+ScriptPromise FileSystemDirectoryHandle::getFileHandle(
     ScriptState* script_state,
     const String& name,
     const FileSystemGetFileOptions* options) {
@@ -76,10 +74,10 @@
             if (!context)
               return;
             if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
-              native_file_system_error::Reject(resolver, *result);
+              file_system_access_error::Reject(resolver, *result);
               return;
             }
-            resolver->Resolve(MakeGarbageCollected<NativeFileSystemFileHandle>(
+            resolver->Resolve(MakeGarbageCollected<FileSystemFileHandle>(
                 context, name, std::move(handle)));
           },
           WrapPersistent(resolver), name));
@@ -87,7 +85,7 @@
   return result;
 }
 
-ScriptPromise NativeFileSystemDirectoryHandle::getDirectoryHandle(
+ScriptPromise FileSystemDirectoryHandle::getDirectoryHandle(
     ScriptState* script_state,
     const String& name,
     const FileSystemGetDirectoryOptions* options) {
@@ -111,12 +109,11 @@
             if (!context)
               return;
             if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
-              native_file_system_error::Reject(resolver, *result);
+              file_system_access_error::Reject(resolver, *result);
               return;
             }
-            resolver->Resolve(
-                MakeGarbageCollected<NativeFileSystemDirectoryHandle>(
-                    context, name, std::move(handle)));
+            resolver->Resolve(MakeGarbageCollected<FileSystemDirectoryHandle>(
+                context, name, std::move(handle)));
           },
           WrapPersistent(resolver), name));
 
@@ -131,10 +128,9 @@
 
 }  // namespace
 
-ScriptValue NativeFileSystemDirectoryHandle::getEntries(
-    ScriptState* script_state) {
-  auto* iterator = MakeGarbageCollected<NativeFileSystemDirectoryIterator>(
-      this, NativeFileSystemDirectoryIterator::Mode::kValue,
+ScriptValue FileSystemDirectoryHandle::getEntries(ScriptState* script_state) {
+  auto* iterator = MakeGarbageCollected<FileSystemDirectoryIterator>(
+      this, FileSystemDirectoryIterator::Mode::kValue,
       ExecutionContext::From(script_state));
   auto* isolate = script_state->GetIsolate();
   auto context = script_state->GetContext();
@@ -150,7 +146,7 @@
   return ScriptValue(script_state->GetIsolate(), result);
 }
 
-ScriptPromise NativeFileSystemDirectoryHandle::removeEntry(
+ScriptPromise FileSystemDirectoryHandle::removeEntry(
     ScriptState* script_state,
     const String& name,
     const FileSystemRemoveOptions* options) {
@@ -167,16 +163,16 @@
       name, options->recursive(),
       WTF::Bind(
           [](ScriptPromiseResolver* resolver, FileSystemAccessErrorPtr result) {
-            native_file_system_error::ResolveOrReject(resolver, *result);
+            file_system_access_error::ResolveOrReject(resolver, *result);
           },
           WrapPersistent(resolver)));
 
   return result;
 }
 
-ScriptPromise NativeFileSystemDirectoryHandle::resolve(
+ScriptPromise FileSystemDirectoryHandle::resolve(
     ScriptState* script_state,
-    NativeFileSystemHandle* possible_child) {
+    FileSystemHandle* possible_child) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = resolver->Promise();
 
@@ -192,7 +188,7 @@
           [](ScriptPromiseResolver* resolver, FileSystemAccessErrorPtr result,
              const base::Optional<Vector<String>>& path) {
             if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
-              native_file_system_error::Reject(resolver, *result);
+              file_system_access_error::Reject(resolver, *result);
               return;
             }
             if (!path.has_value()) {
@@ -207,19 +203,19 @@
 }
 
 mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>
-NativeFileSystemDirectoryHandle::Transfer() {
+FileSystemDirectoryHandle::Transfer() {
   mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken> result;
   if (mojo_ptr_.is_bound())
     mojo_ptr_->Transfer(result.InitWithNewPipeAndPassReceiver());
   return result;
 }
 
-void NativeFileSystemDirectoryHandle::Trace(Visitor* visitor) const {
+void FileSystemDirectoryHandle::Trace(Visitor* visitor) const {
   visitor->Trace(mojo_ptr_);
-  NativeFileSystemHandle::Trace(visitor);
+  FileSystemHandle::Trace(visitor);
 }
 
-void NativeFileSystemDirectoryHandle::QueryPermissionImpl(
+void FileSystemDirectoryHandle::QueryPermissionImpl(
     bool writable,
     base::OnceCallback<void(mojom::blink::PermissionStatus)> callback) {
   if (!mojo_ptr_.is_bound()) {
@@ -229,7 +225,7 @@
   mojo_ptr_->GetPermissionStatus(writable, std::move(callback));
 }
 
-void NativeFileSystemDirectoryHandle::RequestPermissionImpl(
+void FileSystemDirectoryHandle::RequestPermissionImpl(
     bool writable,
     base::OnceCallback<void(mojom::blink::FileSystemAccessErrorPtr,
                             mojom::blink::PermissionStatus)> callback) {
@@ -245,7 +241,7 @@
   mojo_ptr_->RequestPermission(writable, std::move(callback));
 }
 
-void NativeFileSystemDirectoryHandle::IsSameEntryImpl(
+void FileSystemDirectoryHandle::IsSameEntryImpl(
     mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken> other,
     base::OnceCallback<void(mojom::blink::FileSystemAccessErrorPtr, bool)>
         callback) {
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h b/third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h
similarity index 79%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h
rename to third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h
index 6f0fd14..0ade0d7 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h
@@ -2,33 +2,33 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_DIRECTORY_HANDLE_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_DIRECTORY_HANDLE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_DIRECTORY_HANDLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_DIRECTORY_HANDLE_H_
 
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_directory_handle.mojom-blink.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_handle.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 
 namespace blink {
 class FileSystemGetDirectoryOptions;
 class FileSystemGetFileOptions;
 class FileSystemRemoveOptions;
-class NativeFileSystemDirectoryIterator;
+class FileSystemDirectoryIterator;
 
-class NativeFileSystemDirectoryHandle final : public NativeFileSystemHandle {
+class FileSystemDirectoryHandle final : public FileSystemHandle {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  NativeFileSystemDirectoryHandle(
+  FileSystemDirectoryHandle(
       ExecutionContext* context,
       const String& name,
       mojo::PendingRemote<mojom::blink::FileSystemAccessDirectoryHandle>);
 
   // FileSystemDirectoryHandle IDL interface:
-  NativeFileSystemDirectoryIterator* entries();
-  NativeFileSystemDirectoryIterator* keys();
-  NativeFileSystemDirectoryIterator* values();
+  FileSystemDirectoryIterator* entries();
+  FileSystemDirectoryIterator* keys();
+  FileSystemDirectoryIterator* values();
 
   bool isDirectory() const override { return true; }
 
@@ -43,7 +43,7 @@
                             const String& name,
                             const FileSystemRemoveOptions*);
 
-  ScriptPromise resolve(ScriptState*, NativeFileSystemHandle* possible_child);
+  ScriptPromise resolve(ScriptState*, FileSystemHandle* possible_child);
 
   mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken> Transfer()
       override;
@@ -74,4 +74,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_DIRECTORY_HANDLE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_DIRECTORY_HANDLE_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.idl b/third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.idl
index c63eac8..bf18156 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.idl
@@ -2,21 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#filesystemdirectoryhandle
+// https://wicg.github.io/file-system-access/#filesystemdirectoryhandle
 [
     Exposed=(Window,Worker),
     SecureContext,
     Serializable,
-    RuntimeEnabled=NativeFileSystem,
-    ImplementedAs=NativeFileSystemDirectoryHandle
+    RuntimeEnabled=FileSystemAccess
 ] interface FileSystemDirectoryHandle : FileSystemHandle {
     // TODO(https://crbug.com/1087157): This interface defines an async
     // iterable, however that isn't supported yet by the bindings. So for now
     // just explicitly define what an async iterable definition implies.
     //async iterable<USVString, FileSystemHandle>;
-    NativeFileSystemDirectoryIterator entries();
-    NativeFileSystemDirectoryIterator keys();
-    NativeFileSystemDirectoryIterator values();
+    FileSystemDirectoryIterator entries();
+    FileSystemDirectoryIterator keys();
+    FileSystemDirectoryIterator values();
 
     [CallWith=ScriptState] Promise<FileSystemFileHandle> getFileHandle(USVString name, optional FileSystemGetFileOptions options = {});
     [CallWith=ScriptState] Promise<FileSystemDirectoryHandle> getDirectoryHandle(USVString name, optional FileSystemGetDirectoryOptions options = {});
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.cc b/third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.cc
similarity index 80%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.cc
rename to third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.cc
index 70e7e11..69d8970 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.cc
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.h"
 
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 
-NativeFileSystemDirectoryIterator::NativeFileSystemDirectoryIterator(
-    NativeFileSystemDirectoryHandle* directory,
+FileSystemDirectoryIterator::FileSystemDirectoryIterator(
+    FileSystemDirectoryHandle* directory,
     Mode mode,
     ExecutionContext* execution_context)
     : ExecutionContextClient(execution_context),
@@ -28,17 +28,16 @@
       execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI)));
 }
 
-ScriptPromise NativeFileSystemDirectoryIterator::next(
-    ScriptState* script_state) {
+ScriptPromise FileSystemDirectoryIterator::next(ScriptState* script_state) {
   if (error_) {
     auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
     auto result = resolver->Promise();
-    native_file_system_error::Reject(resolver, *error_);
+    file_system_access_error::Reject(resolver, *error_);
     return result;
   }
 
   if (!entries_.IsEmpty()) {
-    NativeFileSystemHandle* handle = entries_.TakeFirst();
+    FileSystemHandle* handle = entries_.TakeFirst();
     ScriptValue result;
     switch (mode_) {
       case Mode::kKey:
@@ -68,11 +67,11 @@
   return ScriptPromise::Cast(script_state, V8IteratorResultDone(script_state));
 }
 
-bool NativeFileSystemDirectoryIterator::HasPendingActivity() const {
+bool FileSystemDirectoryIterator::HasPendingActivity() const {
   return pending_next_;
 }
 
-void NativeFileSystemDirectoryIterator::Trace(Visitor* visitor) const {
+void FileSystemDirectoryIterator::Trace(Visitor* visitor) const {
   ScriptWrappable::Trace(visitor);
   ExecutionContextClient::Trace(visitor);
   visitor->Trace(receiver_);
@@ -81,7 +80,7 @@
   visitor->Trace(directory_);
 }
 
-void NativeFileSystemDirectoryIterator::DidReadDirectory(
+void FileSystemDirectoryIterator::DidReadDirectory(
     mojom::blink::FileSystemAccessErrorPtr result,
     Vector<mojom::blink::FileSystemAccessEntryPtr> entries,
     bool has_more_entries) {
@@ -90,13 +89,13 @@
   if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
     error_ = std::move(result);
     if (pending_next_) {
-      native_file_system_error::Reject(pending_next_, *error_);
+      file_system_access_error::Reject(pending_next_, *error_);
       pending_next_ = nullptr;
     }
     return;
   }
   for (auto& e : entries) {
-    entries_.push_back(NativeFileSystemHandle::CreateFromMojoEntry(
+    entries_.push_back(FileSystemHandle::CreateFromMojoEntry(
         std::move(e), GetExecutionContext()));
   }
   waiting_for_more_entries_ = has_more_entries;
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.h b/third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.h
similarity index 71%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.h
rename to third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.h
index 05b9003f..c1dca83 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.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 THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_DIRECTORY_ITERATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_DIRECTORY_ITERATOR_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_DIRECTORY_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_DIRECTORY_ITERATOR_H_
 
 #include "base/files/file.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_directory_handle.mojom-blink.h"
@@ -15,15 +15,15 @@
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
 
 namespace blink {
-class NativeFileSystemDirectoryHandle;
-class NativeFileSystemHandle;
+class FileSystemDirectoryHandle;
+class FileSystemHandle;
 class ScriptPromise;
 class ScriptPromiseResolver;
 class ScriptState;
 
-class NativeFileSystemDirectoryIterator final
+class FileSystemDirectoryIterator final
     : public ScriptWrappable,
-      public ActiveScriptWrappable<NativeFileSystemDirectoryIterator>,
+      public ActiveScriptWrappable<FileSystemDirectoryIterator>,
       public ExecutionContextClient,
       public mojom::blink::FileSystemAccessDirectoryEntriesListener {
   DEFINE_WRAPPERTYPEINFO();
@@ -32,9 +32,9 @@
   // Should this iterator returns keys, values or both?
   enum Mode { kKey, kValue, kKeyValue };
 
-  NativeFileSystemDirectoryIterator(NativeFileSystemDirectoryHandle* directory,
-                                    Mode mode,
-                                    ExecutionContext* execution_context);
+  FileSystemDirectoryIterator(FileSystemDirectoryHandle* directory,
+                              Mode mode,
+                              ExecutionContext* execution_context);
 
   ScriptPromise next(ScriptState*);
 
@@ -51,15 +51,15 @@
   Mode mode_;
   mojom::blink::FileSystemAccessErrorPtr error_;
   bool waiting_for_more_entries_ = true;
-  HeapDeque<Member<NativeFileSystemHandle>> entries_;
+  HeapDeque<Member<FileSystemHandle>> entries_;
   Member<ScriptPromiseResolver> pending_next_;
-  Member<NativeFileSystemDirectoryHandle> directory_;
+  Member<FileSystemDirectoryHandle> directory_;
   HeapMojoReceiver<mojom::blink::FileSystemAccessDirectoryEntriesListener,
-                   NativeFileSystemDirectoryIterator,
+                   FileSystemDirectoryIterator,
                    HeapMojoWrapperMode::kWithoutContextObserver>
       receiver_;
 };
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_DIRECTORY_ITERATOR_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_DIRECTORY_ITERATOR_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.idl b/third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.idl
similarity index 85%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.idl
rename to third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.idl
index 59a4c98..77bca06 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_directory_iterator.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_directory_iterator.idl
@@ -8,7 +8,7 @@
 [
     LegacyNoInterfaceObject,
     ActiveScriptWrappable,
-    RuntimeEnabled=NativeFileSystem
-] interface NativeFileSystemDirectoryIterator {
+    RuntimeEnabled=FileSystemAccess
+] interface FileSystemDirectoryIterator {
   [CallWith=ScriptState] Promise<any> next();
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.cc b/third_party/blink/renderer/modules/file_system_access/file_system_file_handle.cc
similarity index 84%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.cc
rename to third_party/blink/renderer/modules/file_system_access/file_system_file_handle.cc
index 921a1ee1..0f290539 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_file_handle.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h"
 
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom-blink.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_writer.mojom-blink.h"
@@ -12,8 +12,8 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/fileapi/file.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/file_metadata.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -22,17 +22,17 @@
 namespace blink {
 using mojom::blink::FileSystemAccessErrorPtr;
 
-NativeFileSystemFileHandle::NativeFileSystemFileHandle(
+FileSystemFileHandle::FileSystemFileHandle(
     ExecutionContext* context,
     const String& name,
     mojo::PendingRemote<mojom::blink::FileSystemAccessFileHandle> mojo_ptr)
-    : NativeFileSystemHandle(context, name), mojo_ptr_(context) {
+    : FileSystemHandle(context, name), mojo_ptr_(context) {
   mojo_ptr_.Bind(std::move(mojo_ptr),
                  context->GetTaskRunner(TaskType::kMiscPlatformAPI));
   DCHECK(mojo_ptr_.is_bound());
 }
 
-ScriptPromise NativeFileSystemFileHandle::createWritable(
+ScriptPromise FileSystemFileHandle::createWritable(
     ScriptState* script_state,
     const FileSystemCreateWriterOptions* options,
     ExceptionState& exception_state) {
@@ -55,11 +55,11 @@
             if (!script_state)
               return;
             if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
-              native_file_system_error::Reject(resolver, *result);
+              file_system_access_error::Reject(resolver, *result);
               return;
             }
 
-            resolver->Resolve(NativeFileSystemWritableFileStream::Create(
+            resolver->Resolve(FileSystemWritableFileStream::Create(
                 script_state, std::move(writer)));
           },
           WrapPersistent(resolver)));
@@ -67,9 +67,8 @@
   return result;
 }
 
-ScriptPromise NativeFileSystemFileHandle::getFile(
-    ScriptState* script_state,
-    ExceptionState& exception_state) {
+ScriptPromise FileSystemFileHandle::getFile(ScriptState* script_state,
+                                            ExceptionState& exception_state) {
   if (!mojo_ptr_.is_bound()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, "");
     return ScriptPromise();
@@ -83,7 +82,7 @@
          FileSystemAccessErrorPtr result, const base::File::Info& info,
          const scoped_refptr<BlobDataHandle>& blob) {
         if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
-          native_file_system_error::Reject(resolver, *result);
+          file_system_access_error::Reject(resolver, *result);
           return;
         }
         resolver->Resolve(MakeGarbageCollected<File>(
@@ -95,19 +94,19 @@
 }
 
 mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>
-NativeFileSystemFileHandle::Transfer() {
+FileSystemFileHandle::Transfer() {
   mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken> result;
   if (mojo_ptr_.is_bound())
     mojo_ptr_->Transfer(result.InitWithNewPipeAndPassReceiver());
   return result;
 }
 
-void NativeFileSystemFileHandle::Trace(Visitor* visitor) const {
+void FileSystemFileHandle::Trace(Visitor* visitor) const {
   visitor->Trace(mojo_ptr_);
-  NativeFileSystemHandle::Trace(visitor);
+  FileSystemHandle::Trace(visitor);
 }
 
-void NativeFileSystemFileHandle::QueryPermissionImpl(
+void FileSystemFileHandle::QueryPermissionImpl(
     bool writable,
     base::OnceCallback<void(mojom::blink::PermissionStatus)> callback) {
   if (!mojo_ptr_.is_bound()) {
@@ -117,7 +116,7 @@
   mojo_ptr_->GetPermissionStatus(writable, std::move(callback));
 }
 
-void NativeFileSystemFileHandle::RequestPermissionImpl(
+void FileSystemFileHandle::RequestPermissionImpl(
     bool writable,
     base::OnceCallback<void(mojom::blink::FileSystemAccessErrorPtr,
                             mojom::blink::PermissionStatus)> callback) {
@@ -133,7 +132,7 @@
   mojo_ptr_->RequestPermission(writable, std::move(callback));
 }
 
-void NativeFileSystemFileHandle::IsSameEntryImpl(
+void FileSystemFileHandle::IsSameEntryImpl(
     mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken> other,
     base::OnceCallback<void(mojom::blink::FileSystemAccessErrorPtr, bool)>
         callback) {
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h b/third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h
similarity index 83%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h
rename to third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h
index 449f0536..02f5608 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h
@@ -2,22 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_FILE_HANDLE_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_FILE_HANDLE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_FILE_HANDLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_FILE_HANDLE_H_
 
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_handle.mojom-blink.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_handle.h"
 #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
 
 namespace blink {
 class FileSystemCreateWriterOptions;
 
-class NativeFileSystemFileHandle final : public NativeFileSystemHandle {
+class FileSystemFileHandle final : public FileSystemHandle {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  NativeFileSystemFileHandle(
+  FileSystemFileHandle(
       ExecutionContext* context,
       const String& name,
       mojo::PendingRemote<mojom::blink::FileSystemAccessFileHandle>);
@@ -56,4 +56,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_FILE_HANDLE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_FILE_HANDLE_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_file_handle.idl b/third_party/blink/renderer/modules/file_system_access/file_system_file_handle.idl
index b24362e..931ff2d 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_file_handle.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_file_handle.idl
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#filesystemfilehandle
+// https://wicg.github.io/file-system-access/#filesystemfilehandle
 [
     Exposed=(Window,Worker),
     SecureContext,
     Serializable,
-    RuntimeEnabled=NativeFileSystem,
-    ImplementedAs=NativeFileSystemFileHandle
+    RuntimeEnabled=FileSystemAccess
 ] interface FileSystemFileHandle : FileSystemHandle {
     [CallWith=ScriptState, RaisesException] Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWriterOptions options = {});
+
     [CallWith=ScriptState, RaisesException] Promise<File> getFile();
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_get_directory_options.idl b/third_party/blink/renderer/modules/file_system_access/file_system_get_directory_options.idl
index 529b8dd2..8c45a7f 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_get_directory_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_get_directory_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#dictdef-filesystemgetdirectoryoptions
+// https://wicg.github.io/file-system-access/#dictdef-filesystemgetdirectoryoptions
 dictionary FileSystemGetDirectoryOptions {
   boolean create = false;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_get_file_options.idl b/third_party/blink/renderer/modules/file_system_access/file_system_get_file_options.idl
index 57c5a7d..ad7d5a5 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_get_file_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_get_file_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#dictdef-filesystemgetfileoptions
+// https://wicg.github.io/file-system-access/#dictdef-filesystemgetfileoptions
 dictionary FileSystemGetFileOptions {
   boolean create = false;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_handle.cc b/third_party/blink/renderer/modules/file_system_access/file_system_handle.cc
similarity index 81%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_handle.cc
rename to third_party/blink/renderer/modules/file_system_access/file_system_handle.cc
index 2809a9e..6ad3884 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_handle.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_handle.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_handle.h"
 
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom-blink.h"
 #include "third_party/blink/public/platform/task_type.h"
@@ -10,29 +10,28 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_file_system_handle_permission_descriptor.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/fileapi/file_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 namespace blink {
 using mojom::blink::FileSystemAccessEntryPtr;
 using mojom::blink::FileSystemAccessErrorPtr;
 
-NativeFileSystemHandle::NativeFileSystemHandle(
-    ExecutionContext* execution_context,
-    const String& name)
+FileSystemHandle::FileSystemHandle(ExecutionContext* execution_context,
+                                   const String& name)
     : ExecutionContextClient(execution_context), name_(name) {}
 
 // static
-NativeFileSystemHandle* NativeFileSystemHandle::CreateFromMojoEntry(
+FileSystemHandle* FileSystemHandle::CreateFromMojoEntry(
     mojom::blink::FileSystemAccessEntryPtr e,
     ExecutionContext* execution_context) {
   if (e->entry_handle->is_file()) {
-    return MakeGarbageCollected<NativeFileSystemFileHandle>(
+    return MakeGarbageCollected<FileSystemFileHandle>(
         execution_context, e->name, std::move(e->entry_handle->get_file()));
   }
-  return MakeGarbageCollected<NativeFileSystemDirectoryHandle>(
+  return MakeGarbageCollected<FileSystemDirectoryHandle>(
       execution_context, e->name, std::move(e->entry_handle->get_directory()));
 }
 
@@ -52,7 +51,7 @@
 
 }  // namespace
 
-ScriptPromise NativeFileSystemHandle::queryPermission(
+ScriptPromise FileSystemHandle::queryPermission(
     ScriptState* script_state,
     const FileSystemHandlePermissionDescriptor* descriptor) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
@@ -70,7 +69,7 @@
   return result;
 }
 
-ScriptPromise NativeFileSystemHandle::requestPermission(
+ScriptPromise FileSystemHandle::requestPermission(
     ScriptState* script_state,
     const FileSystemHandlePermissionDescriptor* descriptor) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
@@ -82,7 +81,7 @@
           [](ScriptPromiseResolver* resolver, FileSystemAccessErrorPtr result,
              mojom::blink::PermissionStatus status) {
             if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
-              native_file_system_error::Reject(resolver, *result);
+              file_system_access_error::Reject(resolver, *result);
               return;
             }
             resolver->Resolve(MojoPermissionStatusToString(status));
@@ -92,9 +91,8 @@
   return result;
 }
 
-ScriptPromise NativeFileSystemHandle::isSameEntry(
-    ScriptState* script_state,
-    NativeFileSystemHandle* other) {
+ScriptPromise FileSystemHandle::isSameEntry(ScriptState* script_state,
+                                            FileSystemHandle* other) {
   auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = resolver->Promise();
 
@@ -104,7 +102,7 @@
           [](ScriptPromiseResolver* resolver, FileSystemAccessErrorPtr result,
              bool same) {
             if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
-              native_file_system_error::Reject(resolver, *result);
+              file_system_access_error::Reject(resolver, *result);
               return;
             }
             resolver->Resolve(same);
@@ -113,7 +111,7 @@
   return result;
 }
 
-void NativeFileSystemHandle::Trace(Visitor* visitor) const {
+void FileSystemHandle::Trace(Visitor* visitor) const {
   ScriptWrappable::Trace(visitor);
   ExecutionContextClient::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h b/third_party/blink/renderer/modules/file_system_access/file_system_handle.h
similarity index 82%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h
rename to third_party/blink/renderer/modules/file_system_access/file_system_handle.h
index e3385d8..8ef5fe4 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_handle.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 THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_HANDLE_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_HANDLE_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_HANDLE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_HANDLE_H_
 
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_directory_handle.mojom-blink-forward.h"
@@ -21,14 +21,12 @@
 class ExecutionContext;
 class FileSystemHandlePermissionDescriptor;
 
-class NativeFileSystemHandle : public ScriptWrappable,
-                               public ExecutionContextClient {
+class FileSystemHandle : public ScriptWrappable, public ExecutionContextClient {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  NativeFileSystemHandle(ExecutionContext* execution_context,
-                         const String& name);
-  static NativeFileSystemHandle* CreateFromMojoEntry(
+  FileSystemHandle(ExecutionContext* execution_context, const String& name);
+  static FileSystemHandle* CreateFromMojoEntry(
       mojom::blink::FileSystemAccessEntryPtr,
       ExecutionContext* execution_context);
 
@@ -42,7 +40,7 @@
   ScriptPromise requestPermission(ScriptState*,
                                   const FileSystemHandlePermissionDescriptor*);
 
-  ScriptPromise isSameEntry(ScriptState*, NativeFileSystemHandle* other);
+  ScriptPromise isSameEntry(ScriptState*, FileSystemHandle* other);
 
   // Grab a handle to a transfer token. This may return an invalid PendingRemote
   // if the context is already destroyed.
@@ -69,4 +67,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_HANDLE_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_HANDLE_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_handle.idl b/third_party/blink/renderer/modules/file_system_access/file_system_handle.idl
index 1cab690..bff43870 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_handle.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_handle.idl
@@ -2,19 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#enumdef-filesystemhandlekind
+// https://wicg.github.io/file-system-access/#enumdef-filesystemhandlekind
 enum FileSystemHandleKind {
   "file",
   "directory",
 };
 
-// https://wicg.github.io/native-file-system/#filesystemhandle
+// https://wicg.github.io/file-system-access/#filesystemhandle
 [
     Exposed=(Window,Worker),
     SecureContext,
     Serializable,
-    RuntimeEnabled=NativeFileSystem,
-    ImplementedAs=NativeFileSystemHandle
+    RuntimeEnabled=FileSystemAccess
 ] interface FileSystemHandle {
     // Brand checking APIs because javascript makes it otherwise really hard to
     // figure out what type an object is when you don't know in which global
@@ -28,5 +27,6 @@
     [CallWith=ScriptState] Promise<PermissionState> requestPermission(
         optional FileSystemHandlePermissionDescriptor descriptor = {});
 
-    [CallWith=ScriptState, Measure] Promise<boolean> isSameEntry(FileSystemHandle other);
+    [CallWith=ScriptState, Measure]
+    Promise<boolean> isSameEntry(FileSystemHandle other);
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_handle_permission_descriptor.idl b/third_party/blink/renderer/modules/file_system_access/file_system_handle_permission_descriptor.idl
index c4762fc..4dd4048a 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_handle_permission_descriptor.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_handle_permission_descriptor.idl
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// http://wicg.github.io/native-file-system/#enumdef-filesystempermissionmode
+// http://wicg.github.io/file-system-access/#enumdef-filesystempermissionmode
 enum FileSystemPermissionMode {
   "read",
   "readwrite"
 };
 
-// http://wicg.github.io/native-file-system/#dictdef-filesystemhandlepermissiondescriptor
+// http://wicg.github.io/file-system-access/#dictdef-filesystemhandlepermissiondescriptor
 dictionary FileSystemHandlePermissionDescriptor {
   FileSystemPermissionMode mode = "read";
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_remove_options.idl b/third_party/blink/renderer/modules/file_system_access/file_system_remove_options.idl
index f3f8566..786bc94 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_remove_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_remove_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#dictdef-filesystemremoveoptions
+// https://wicg.github.io/file-system-access/#dictdef-filesystemremoveoptions
 dictionary FileSystemRemoveOptions {
   boolean recursive = false;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_underlying_sink.cc b/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.cc
similarity index 82%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_underlying_sink.cc
rename to third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.cc
index e59cc97..ff365fad 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_underlying_sink.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_underlying_sink.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -12,14 +12,14 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_write_params.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/fileapi/blob.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
 
 namespace blink {
 
-NativeFileSystemUnderlyingSink::NativeFileSystemUnderlyingSink(
+FileSystemUnderlyingSink::FileSystemUnderlyingSink(
     ExecutionContext* context,
     mojo::PendingRemote<mojom::blink::FileSystemAccessFileWriter> writer_remote)
     : writer_remote_(context) {
@@ -28,14 +28,14 @@
   DCHECK(writer_remote_.is_bound());
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::start(
+ScriptPromise FileSystemUnderlyingSink::start(
     ScriptState* script_state,
     WritableStreamDefaultController* controller,
     ExceptionState& exception_state) {
   return ScriptPromise::CastUndefined(script_state);
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::write(
+ScriptPromise FileSystemUnderlyingSink::write(
     ScriptState* script_state,
     ScriptValue chunk,
     WritableStreamDefaultController* controller,
@@ -70,9 +70,8 @@
                    exception_state);
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::close(
-    ScriptState* script_state,
-    ExceptionState& exception_state) {
+ScriptPromise FileSystemUnderlyingSink::close(ScriptState* script_state,
+                                              ExceptionState& exception_state) {
   if (!writer_remote_.is_bound() || pending_operation_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Object reached an invalid state");
@@ -81,16 +80,15 @@
   pending_operation_ =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = pending_operation_->Promise();
-  writer_remote_->Close(WTF::Bind(
-      &NativeFileSystemUnderlyingSink::CloseComplete, WrapPersistent(this)));
+  writer_remote_->Close(WTF::Bind(&FileSystemUnderlyingSink::CloseComplete,
+                                  WrapPersistent(this)));
 
   return result;
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::abort(
-    ScriptState* script_state,
-    ScriptValue reason,
-    ExceptionState& exception_state) {
+ScriptPromise FileSystemUnderlyingSink::abort(ScriptState* script_state,
+                                              ScriptValue reason,
+                                              ExceptionState& exception_state) {
   // The specification guarantees that this will only be called after all
   // pending writes have been aborted. Terminating the remote connection
   // will ensure that the writes are not closed successfully.
@@ -99,7 +97,7 @@
   return ScriptPromise::CastUndefined(script_state);
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::HandleParams(
+ScriptPromise FileSystemUnderlyingSink::HandleParams(
     ScriptState* script_state,
     const WriteParams& params,
     ExceptionState& exception_state) {
@@ -140,7 +138,7 @@
   return ScriptPromise();
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::WriteData(
+ScriptPromise FileSystemUnderlyingSink::WriteData(
     ScriptState* script_state,
     uint64_t position,
     const ArrayBufferOrArrayBufferViewOrBlobOrUSVString& data,
@@ -173,7 +171,7 @@
   return WriteBlob(script_state, position, blob, exception_state);
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::WriteBlob(
+ScriptPromise FileSystemUnderlyingSink::WriteBlob(
     ScriptState* script_state,
     uint64_t position,
     Blob* blob,
@@ -186,14 +184,13 @@
   pending_operation_ =
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = pending_operation_->Promise();
-  writer_remote_->Write(
-      position, blob->AsMojoBlob(),
-      WTF::Bind(&NativeFileSystemUnderlyingSink::WriteComplete,
-                WrapPersistent(this)));
+  writer_remote_->Write(position, blob->AsMojoBlob(),
+                        WTF::Bind(&FileSystemUnderlyingSink::WriteComplete,
+                                  WrapPersistent(this)));
   return result;
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::Truncate(
+ScriptPromise FileSystemUnderlyingSink::Truncate(
     ScriptState* script_state,
     uint64_t size,
     ExceptionState& exception_state) {
@@ -206,15 +203,14 @@
       MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   ScriptPromise result = pending_operation_->Promise();
   writer_remote_->Truncate(
-      size, WTF::Bind(&NativeFileSystemUnderlyingSink::TruncateComplete,
+      size, WTF::Bind(&FileSystemUnderlyingSink::TruncateComplete,
                       WrapPersistent(this), size));
   return result;
 }
 
-ScriptPromise NativeFileSystemUnderlyingSink::Seek(
-    ScriptState* script_state,
-    uint64_t offset,
-    ExceptionState& exception_state) {
+ScriptPromise FileSystemUnderlyingSink::Seek(ScriptState* script_state,
+                                             uint64_t offset,
+                                             ExceptionState& exception_state) {
   if (!writer_remote_.is_bound() || pending_operation_) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       "Object reached an invalid state");
@@ -224,11 +220,11 @@
   return ScriptPromise::CastUndefined(script_state);
 }
 
-void NativeFileSystemUnderlyingSink::WriteComplete(
+void FileSystemUnderlyingSink::WriteComplete(
     mojom::blink::FileSystemAccessErrorPtr result,
     uint64_t bytes_written) {
   DCHECK(pending_operation_);
-  native_file_system_error::ResolveOrReject(pending_operation_, *result);
+  file_system_access_error::ResolveOrReject(pending_operation_, *result);
   pending_operation_ = nullptr;
 
   if (result->status == mojom::blink::FileSystemAccessStatus::kOk) {
@@ -237,11 +233,11 @@
   }
 }
 
-void NativeFileSystemUnderlyingSink::TruncateComplete(
+void FileSystemUnderlyingSink::TruncateComplete(
     uint64_t to_size,
     mojom::blink::FileSystemAccessErrorPtr result) {
   DCHECK(pending_operation_);
-  native_file_system_error::ResolveOrReject(pending_operation_, *result);
+  file_system_access_error::ResolveOrReject(pending_operation_, *result);
   pending_operation_ = nullptr;
 
   if (result->status == mojom::blink::FileSystemAccessStatus::kOk) {
@@ -251,17 +247,17 @@
   }
 }
 
-void NativeFileSystemUnderlyingSink::CloseComplete(
+void FileSystemUnderlyingSink::CloseComplete(
     mojom::blink::FileSystemAccessErrorPtr result) {
   DCHECK(pending_operation_);
-  native_file_system_error::ResolveOrReject(pending_operation_, *result);
+  file_system_access_error::ResolveOrReject(pending_operation_, *result);
   pending_operation_ = nullptr;
   // We close the mojo pipe because we intend this writable file stream to be
   // discarded after close. Subsequent operations will fail.
   writer_remote_.reset();
 }
 
-void NativeFileSystemUnderlyingSink::Trace(Visitor* visitor) const {
+void FileSystemUnderlyingSink::Trace(Visitor* visitor) const {
   ScriptWrappable::Trace(visitor);
   UnderlyingSinkBase::Trace(visitor);
   visitor->Trace(writer_remote_);
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_underlying_sink.h b/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.h
similarity index 87%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_underlying_sink.h
rename to third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.h
index 853a900..23cb4cd 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_underlying_sink.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.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 THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_UNDERLYING_SINK_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_UNDERLYING_SINK_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_UNDERLYING_SINK_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_UNDERLYING_SINK_H_
 
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_file_writer.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h"
@@ -18,9 +18,9 @@
 class ScriptPromiseResolver;
 class WriteParams;
 
-class NativeFileSystemUnderlyingSink final : public UnderlyingSinkBase {
+class FileSystemUnderlyingSink final : public UnderlyingSinkBase {
  public:
-  explicit NativeFileSystemUnderlyingSink(
+  explicit FileSystemUnderlyingSink(
       ExecutionContext*,
       mojo::PendingRemote<mojom::blink::FileSystemAccessFileWriter>);
 
@@ -66,4 +66,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_UNDERLYING_SINK_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_UNDERLYING_SINK_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.cc b/third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.cc
similarity index 86%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.cc
rename to third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.cc
index 4e599eb..9d88ef3f 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.cc
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/array_buffer_or_array_buffer_view_or_blob_or_usv_string.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
@@ -12,13 +12,13 @@
 #include "third_party/blink/renderer/core/streams/count_queuing_strategy.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_default_controller.h"
 #include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_underlying_sink.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_underlying_sink.h"
 
 #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h"
 
 namespace blink {
 
-NativeFileSystemWritableFileStream* NativeFileSystemWritableFileStream::Create(
+FileSystemWritableFileStream* FileSystemWritableFileStream::Create(
     ScriptState* script_state,
     mojo::PendingRemote<mojom::blink::FileSystemAccessFileWriter>
         writer_pending_remote) {
@@ -27,9 +27,9 @@
 
   ExecutionContext* context = ExecutionContext::From(script_state);
 
-  auto* stream = MakeGarbageCollected<NativeFileSystemWritableFileStream>();
+  auto* stream = MakeGarbageCollected<FileSystemWritableFileStream>();
 
-  auto* underlying_sink = MakeGarbageCollected<NativeFileSystemUnderlyingSink>(
+  auto* underlying_sink = MakeGarbageCollected<FileSystemUnderlyingSink>(
       context, std::move(writer_pending_remote));
   stream->underlying_sink_ = underlying_sink;
   auto underlying_sink_value = ScriptValue::From(script_state, underlying_sink);
@@ -43,7 +43,7 @@
 
   ExceptionState exception_state(script_state->GetIsolate(),
                                  ExceptionState::kConstructionContext,
-                                 "NativeFileSystemWritableFileStream");
+                                 "FileSystemWritableFileStream");
   stream->InitInternal(script_state, underlying_sink_value, strategy_value,
                        exception_state);
 
@@ -53,7 +53,7 @@
   return stream;
 }
 
-ScriptPromise NativeFileSystemWritableFileStream::write(
+ScriptPromise FileSystemWritableFileStream::write(
     ScriptState* script_state,
     const ArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrWriteParams& data,
     ExceptionState& exception_state) {
@@ -69,7 +69,7 @@
   return promise;
 }
 
-ScriptPromise NativeFileSystemWritableFileStream::truncate(
+ScriptPromise FileSystemWritableFileStream::truncate(
     ScriptState* script_state,
     uint64_t size,
     ExceptionState& exception_state) {
@@ -89,7 +89,7 @@
   return promise;
 }
 
-ScriptPromise NativeFileSystemWritableFileStream::close(
+ScriptPromise FileSystemWritableFileStream::close(
     ScriptState* script_state,
     ExceptionState& exception_state) {
   WritableStreamDefaultWriter* writer =
@@ -103,7 +103,7 @@
   return promise;
 }
 
-ScriptPromise NativeFileSystemWritableFileStream::seek(
+ScriptPromise FileSystemWritableFileStream::seek(
     ScriptState* script_state,
     uint64_t offset,
     ExceptionState& exception_state) {
@@ -123,7 +123,7 @@
   return promise;
 }
 
-void NativeFileSystemWritableFileStream::Trace(Visitor* visitor) const {
+void FileSystemWritableFileStream::Trace(Visitor* visitor) const {
   WritableStream::Trace(visitor);
   visitor->Trace(underlying_sink_);
 }
diff --git a/third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.h b/third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.h
similarity index 72%
rename from third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.h
rename to third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.h
index 55327a4..2f87469 100644
--- a/third_party/blink/renderer/modules/file_system_access/native_file_system_writable_file_stream.h
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.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 THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
 
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/blink/public/mojom/file_system_access/file_system_access_error.mojom-blink.h"
@@ -17,19 +17,19 @@
 
 class ScriptPromise;
 class ScriptState;
-class NativeFileSystemUnderlyingSink;
+class FileSystemUnderlyingSink;
 
-class NativeFileSystemWritableFileStream final : public WritableStream {
+class FileSystemWritableFileStream final : public WritableStream {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static NativeFileSystemWritableFileStream* Create(
+  static FileSystemWritableFileStream* Create(
       ScriptState*,
       mojo::PendingRemote<mojom::blink::FileSystemAccessFileWriter>);
 
   void Trace(Visitor* visitor) const override;
 
-  // IDL defined functions specific to NativeFileSystemWritableFileStream.
+  // IDL defined functions specific to FileSystemWritableFileStream.
   ScriptPromise write(
       ScriptState*,
       const ArrayBufferOrArrayBufferViewOrBlobOrUSVStringOrWriteParams& data,
@@ -39,8 +39,8 @@
   ScriptPromise seek(ScriptState*, uint64_t offset, ExceptionState&);
 
  private:
-  Member<NativeFileSystemUnderlyingSink> underlying_sink_;
+  Member<FileSystemUnderlyingSink> underlying_sink_;
 };
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_NATIVE_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_FILE_SYSTEM_WRITABLE_FILE_STREAM_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.idl b/third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.idl
index e09e63d9..32a8914 100644
--- a/third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.idl
+++ b/third_party/blink/renderer/modules/file_system_access/file_system_writable_file_stream.idl
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#filesystemwritablefilestream
+// https://wicg.github.io/file-system-access/#filesystemwritablefilestream
 [
     Exposed=(Window,Worker),
     SecureContext,
-    ImplementedAs=NativeFileSystemWritableFileStream,
-    RuntimeEnabled=NativeFileSystem
+    RuntimeEnabled=FileSystemAccess
  ] interface FileSystemWritableFileStream : WritableStream {
    [CallWith=ScriptState, RaisesException] Promise<void> write((BufferSource or Blob or USVString or WriteParams) data);
    [CallWith=ScriptState, RaisesException] Promise<void> truncate(unsigned long long size);
diff --git a/third_party/blink/renderer/modules/file_system_access/global_native_file_system.cc b/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
similarity index 94%
rename from third_party/blink/renderer/modules/file_system_access/global_native_file_system.cc
rename to third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
index e0dad660..ba70723 100644
--- a/third_party/blink/renderer/modules/file_system_access/global_native_file_system.cc
+++ b/third_party/blink/renderer/modules/file_system_access/global_file_system_access.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/global_native_file_system.h"
+#include "third_party/blink/renderer/modules/file_system_access/global_file_system_access.h"
 
 #include <utility>
 
@@ -25,9 +25,9 @@
 #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/workers/worker_global_scope.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_file_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_file_handle.h"
 #include "third_party/blink/renderer/platform/bindings/enumeration_base.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
@@ -142,7 +142,7 @@
     return;
   }
 
-  if (!window.GetSecurityOrigin()->CanAccessNativeFileSystem()) {
+  if (!window.GetSecurityOrigin()->CanAccessFileSystem()) {
     if (window.IsSandboxed(network::mojom::blink::WebSandboxFlags::kOrigin)) {
       exception_state.ThrowSecurityError(
           "Sandboxed documents aren't allowed to show a file picker.");
@@ -229,7 +229,7 @@
               return;
             if (file_operation_result->status !=
                 mojom::blink::FileSystemAccessStatus::kOk) {
-              native_file_system_error::Reject(resolver,
+              file_system_access_error::Reject(resolver,
                                                *file_operation_result);
               return;
             }
@@ -237,25 +237,25 @@
             // While it would be better to not trust the renderer process,
             // we're doing this here to avoid potential mojo message pipe
             // ordering problems, where the frame activation state
-            // reconciliation messages would compete with concurrent Native File
-            // System messages to the browser.
+            // reconciliation messages would compete with concurrent File
+            // System Access messages to the browser.
             // TODO(https://crbug.com/1017270): Remove this after spec change,
             // or when activation moves to browser.
             LocalFrame::NotifyUserActivation(
                 local_frame, mojom::blink::UserActivationNotificationType::
-                                 kNativeFileSystem);
+                                 kFileSystemAccess);
 
             if (return_as_sequence) {
-              HeapVector<Member<NativeFileSystemHandle>> results;
+              HeapVector<Member<FileSystemHandle>> results;
               results.ReserveInitialCapacity(entries.size());
               for (auto& entry : entries) {
-                results.push_back(NativeFileSystemHandle::CreateFromMojoEntry(
+                results.push_back(FileSystemHandle::CreateFromMojoEntry(
                     std::move(entry), context));
               }
               resolver->Resolve(results);
             } else {
               DCHECK_EQ(1u, entries.size());
-              resolver->Resolve(NativeFileSystemHandle::CreateFromMojoEntry(
+              resolver->Resolve(FileSystemHandle::CreateFromMojoEntry(
                   std::move(entries[0]), context));
             }
           },
@@ -267,7 +267,7 @@
 }  // namespace
 
 // static
-ScriptPromise GlobalNativeFileSystem::showOpenFilePicker(
+ScriptPromise GlobalFileSystemAccess::showOpenFilePicker(
     ScriptState* script_state,
     LocalDOMWindow& window,
     const OpenFilePickerOptions* options,
@@ -308,7 +308,7 @@
 }
 
 // static
-ScriptPromise GlobalNativeFileSystem::showSaveFilePicker(
+ScriptPromise GlobalFileSystemAccess::showSaveFilePicker(
     ScriptState* script_state,
     LocalDOMWindow& window,
     const SaveFilePickerOptions* options,
@@ -346,7 +346,7 @@
 }
 
 // static
-ScriptPromise GlobalNativeFileSystem::showDirectoryPicker(
+ScriptPromise GlobalFileSystemAccess::showDirectoryPicker(
     ScriptState* script_state,
     LocalDOMWindow& window,
     const DirectoryPickerOptions* options,
diff --git a/third_party/blink/renderer/modules/file_system_access/global_native_file_system.h b/third_party/blink/renderer/modules/file_system_access/global_file_system_access.h
similarity index 90%
rename from third_party/blink/renderer/modules/file_system_access/global_native_file_system.h
rename to third_party/blink/renderer/modules/file_system_access/global_file_system_access.h
index e0ee3bca..b6190eb 100644
--- a/third_party/blink/renderer/modules/file_system_access/global_native_file_system.h
+++ b/third_party/blink/renderer/modules/file_system_access/global_file_system_access.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 THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_GLOBAL_NATIVE_FILE_SYSTEM_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_GLOBAL_NATIVE_FILE_SYSTEM_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_GLOBAL_FILE_SYSTEM_ACCESS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_GLOBAL_FILE_SYSTEM_ACCESS_H_
 
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -18,8 +18,8 @@
 class ScriptPromise;
 class ScriptState;
 
-class GlobalNativeFileSystem {
-  STATIC_ONLY(GlobalNativeFileSystem);
+class GlobalFileSystemAccess {
+  STATIC_ONLY(GlobalFileSystemAccess);
 
  public:
   static ScriptPromise showOpenFilePicker(ScriptState*,
@@ -38,4 +38,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_GLOBAL_NATIVE_FILE_SYSTEM_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_GLOBAL_FILE_SYSTEM_ACCESS_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/global_native_file_system_test.cc b/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
similarity index 88%
rename from third_party/blink/renderer/modules/file_system_access/global_native_file_system_test.cc
rename to third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
index 31f21e6..77bc8b04 100644
--- a/third_party/blink/renderer/modules/file_system_access/global_native_file_system_test.cc
+++ b/third_party/blink/renderer/modules/file_system_access/global_file_system_access_test.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/global_native_file_system.h"
+#include "third_party/blink/renderer/modules/file_system_access/global_file_system_access.h"
 
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -21,27 +21,27 @@
 
 namespace blink {
 
-class MockNativeFileSystemManager
+class MockFileSystemAccessManager
     : public mojom::blink::FileSystemAccessManager {
  public:
-  MockNativeFileSystemManager(BrowserInterfaceBrokerProxy& broker,
+  MockFileSystemAccessManager(BrowserInterfaceBrokerProxy& broker,
                               base::OnceClosure reached_callback)
       : reached_callback_(std::move(reached_callback)), broker_(broker) {
     broker_.SetBinderForTesting(
         mojom::blink::FileSystemAccessManager::Name_,
         WTF::BindRepeating(
-            &MockNativeFileSystemManager::BindNativeFileSystemManager,
+            &MockFileSystemAccessManager::BindFileSystemAccessManager,
             WTF::Unretained(this)));
   }
-  MockNativeFileSystemManager(BrowserInterfaceBrokerProxy& broker)
+  MockFileSystemAccessManager(BrowserInterfaceBrokerProxy& broker)
       : broker_(broker) {
     broker_.SetBinderForTesting(
         mojom::blink::FileSystemAccessManager::Name_,
         WTF::BindRepeating(
-            &MockNativeFileSystemManager::BindNativeFileSystemManager,
+            &MockFileSystemAccessManager::BindFileSystemAccessManager,
             WTF::Unretained(this)));
   }
-  ~MockNativeFileSystemManager() override {
+  ~MockFileSystemAccessManager() override {
     broker_.SetBinderForTesting(mojom::blink::FileSystemAccessManager::Name_,
                                 {});
   }
@@ -91,7 +91,7 @@
       GetEntryFromDragDropTokenCallback callback) override {}
 
  private:
-  void BindNativeFileSystemManager(mojo::ScopedMessagePipeHandle handle) {
+  void BindFileSystemAccessManager(mojo::ScopedMessagePipeHandle handle) {
     receivers_.Add(this,
                    mojo::PendingReceiver<mojom::blink::FileSystemAccessManager>(
                        std::move(handle)));
@@ -103,7 +103,7 @@
   BrowserInterfaceBrokerProxy& broker_;
 };
 
-class GlobalNativeFileSystemTest : public PageTestBase {
+class GlobalFileSystemAccessTest : public PageTestBase {
  public:
   void SetUp() override {
     PageTestBase::SetUp();
@@ -122,13 +122,13 @@
   }
 };
 
-TEST_F(GlobalNativeFileSystemTest, UserActivationRequiredOtherwiseDenied) {
+TEST_F(GlobalFileSystemAccessTest, UserActivationRequiredOtherwiseDenied) {
   LocalFrame* frame = &GetFrame();
   EXPECT_FALSE(frame->HasStickyUserActivation());
 
-  MockNativeFileSystemManager manager(frame->GetBrowserInterfaceBroker());
+  MockFileSystemAccessManager manager(frame->GetBrowserInterfaceBroker());
   manager.SetChooseEntriesResponse(WTF::Bind(
-      [](MockNativeFileSystemManager::ChooseEntriesCallback callback) {
+      [](MockFileSystemAccessManager::ChooseEntriesCallback callback) {
         FAIL();
       }));
   ClassicScript::CreateUnspecifiedScript(
@@ -138,7 +138,7 @@
   EXPECT_FALSE(frame->HasStickyUserActivation());
 }
 
-TEST_F(GlobalNativeFileSystemTest, UserActivationChooseEntriesSuccessful) {
+TEST_F(GlobalFileSystemAccessTest, UserActivationChooseEntriesSuccessful) {
   LocalFrame* frame = &GetFrame();
   EXPECT_FALSE(frame->HasStickyUserActivation());
 
@@ -147,10 +147,10 @@
   EXPECT_TRUE(frame->HasStickyUserActivation());
 
   base::RunLoop manager_run_loop;
-  MockNativeFileSystemManager manager(frame->GetBrowserInterfaceBroker(),
+  MockFileSystemAccessManager manager(frame->GetBrowserInterfaceBroker(),
                                       manager_run_loop.QuitClosure());
   manager.SetChooseEntriesResponse(WTF::Bind(
-      [](MockNativeFileSystemManager::ChooseEntriesCallback callback) {
+      [](MockFileSystemAccessManager::ChooseEntriesCallback callback) {
         auto error = mojom::blink::FileSystemAccessError::New();
         error->status = mojom::blink::FileSystemAccessStatus::kOk;
         error->message = "";
@@ -183,7 +183,7 @@
   EXPECT_TRUE(frame->HasStickyUserActivation());
 }
 
-TEST_F(GlobalNativeFileSystemTest, UserActivationChooseEntriesErrors) {
+TEST_F(GlobalFileSystemAccessTest, UserActivationChooseEntriesErrors) {
   LocalFrame* frame = &GetFrame();
   EXPECT_FALSE(frame->HasStickyUserActivation());
 
@@ -197,7 +197,7 @@
       // kOperationAborted is when the user cancels the file selection.
       FileSystemAccessStatus::kOperationAborted,
   };
-  MockNativeFileSystemManager manager(frame->GetBrowserInterfaceBroker());
+  MockFileSystemAccessManager manager(frame->GetBrowserInterfaceBroker());
 
   for (const FileSystemAccessStatus& status : statuses) {
     LocalFrame::NotifyUserActivation(
@@ -208,7 +208,7 @@
     manager.SetQuitClosure(manager_run_loop.QuitClosure());
     manager.SetChooseEntriesResponse(WTF::Bind(
         [](mojom::blink::FileSystemAccessStatus status,
-           MockNativeFileSystemManager::ChooseEntriesCallback callback) {
+           MockFileSystemAccessManager::ChooseEntriesCallback callback) {
           auto error = mojom::blink::FileSystemAccessError::New();
           error->status = status;
           error->message = "";
diff --git a/third_party/blink/renderer/modules/file_system_access/idls.gni b/third_party/blink/renderer/modules/file_system_access/idls.gni
index a3b719a..56587022 100644
--- a/third_party/blink/renderer/modules/file_system_access/idls.gni
+++ b/third_party/blink/renderer/modules/file_system_access/idls.gni
@@ -3,7 +3,7 @@
 # found in the LICENSE file.
 
 modules_idl_files = [
-  "native_file_system_directory_iterator.idl",
+  "file_system_directory_iterator.idl",
   "file_system_directory_handle.idl",
   "file_system_file_handle.idl",
   "file_system_handle.idl",
@@ -25,7 +25,7 @@
 ]
 
 modules_dependency_idl_files = [
-  "data_transfer_item_native_file_system.idl",
-  "storage_manager_native_file_system.idl",
-  "window_native_file_system.idl",
+  "data_transfer_item_file_system_access.idl",
+  "storage_manager_file_system_access.idl",
+  "window_file_system_access.idl",
 ]
diff --git a/third_party/blink/renderer/modules/file_system_access/open_file_picker_options.idl b/third_party/blink/renderer/modules/file_system_access/open_file_picker_options.idl
index 96456f75..d74f2c1 100644
--- a/third_party/blink/renderer/modules/file_system_access/open_file_picker_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/open_file_picker_options.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#dictdef-openfilepickeroptions
+// https://wicg.github.io/file-system-access/#dictdef-openfilepickeroptions
 dictionary OpenFilePickerOptions : FilePickerOptions {
   boolean multiple = false;
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/save_file_picker_options.idl b/third_party/blink/renderer/modules/file_system_access/save_file_picker_options.idl
index d18f9d01..c3f1237 100644
--- a/third_party/blink/renderer/modules/file_system_access/save_file_picker_options.idl
+++ b/third_party/blink/renderer/modules/file_system_access/save_file_picker_options.idl
@@ -2,6 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#dictdef-savefilepickeroptions
+// https://wicg.github.io/file-system-access/#dictdef-savefilepickeroptions
 dictionary SaveFilePickerOptions : FilePickerOptions {
 };
diff --git a/third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.cc b/third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.cc
similarity index 92%
rename from third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.cc
rename to third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.cc
index b6fa20c..c21b28d 100644
--- a/third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.cc
+++ b/third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.h"
+#include "third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.h"
 
 #include <utility>
 
@@ -18,8 +18,8 @@
 #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/workers/worker_global_scope.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_directory_handle.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_access_error.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_directory_handle.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
@@ -61,10 +61,10 @@
         if (!context)
           return;
         if (result->status != mojom::blink::FileSystemAccessStatus::kOk) {
-          native_file_system_error::Reject(resolver, *result);
+          file_system_access_error::Reject(resolver, *result);
           return;
         }
-        resolver->Resolve(MakeGarbageCollected<NativeFileSystemDirectoryHandle>(
+        resolver->Resolve(MakeGarbageCollected<FileSystemDirectoryHandle>(
             context, kSandboxRootDirectoryName, std::move(handle)));
       },
       WrapPersistent(resolver), std::move(manager)));
@@ -73,13 +73,13 @@
 }  // namespace
 
 // static
-ScriptPromise StorageManagerNativeFileSystem::getDirectory(
+ScriptPromise StorageManagerFileSystemAccess::getDirectory(
     ScriptState* script_state,
     const StorageManager& storage,
     ExceptionState& exception_state) {
   ExecutionContext* context = ExecutionContext::From(script_state);
 
-  if (!context->GetSecurityOrigin()->CanAccessNativeFileSystem()) {
+  if (!context->GetSecurityOrigin()->CanAccessFileSystem()) {
     if (context->IsSandboxed(network::mojom::blink::WebSandboxFlags::kOrigin)) {
       exception_state.ThrowSecurityError(
           "Storage directory access is denied because the context is "
diff --git a/third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.h b/third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.h
similarity index 79%
rename from third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.h
rename to third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.h
index 300eef49..2ee916a 100644
--- a/third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.h
+++ b/third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.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 THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_STORAGE_MANAGER_NATIVE_FILE_SYSTEM_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_STORAGE_MANAGER_NATIVE_FILE_SYSTEM_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_STORAGE_MANAGER_FILE_SYSTEM_ACCESS_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_STORAGE_MANAGER_FILE_SYSTEM_ACCESS_H_
 
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -15,8 +15,8 @@
 class ScriptState;
 class StorageManager;
 
-class StorageManagerNativeFileSystem {
-  STATIC_ONLY(StorageManagerNativeFileSystem);
+class StorageManagerFileSystemAccess {
+  STATIC_ONLY(StorageManagerFileSystemAccess);
 
  public:
   static ScriptPromise getDirectory(ScriptState*,
@@ -26,4 +26,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_STORAGE_MANAGER_NATIVE_FILE_SYSTEM_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_FILE_SYSTEM_ACCESS_STORAGE_MANAGER_FILE_SYSTEM_ACCESS_H_
diff --git a/third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.idl b/third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.idl
similarity index 69%
rename from third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.idl
rename to third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.idl
index f3a36d5..d73337e3 100644
--- a/third_party/blink/renderer/modules/file_system_access/storage_manager_native_file_system.idl
+++ b/third_party/blink/renderer/modules/file_system_access/storage_manager_file_system_access.idl
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#sandboxed-filesystem
+// https://wicg.github.io/file-system-access/#sandboxed-filesystem
 [
     SecureContext,
-    RuntimeEnabled=NativeFileSystem,
-    ImplementedAs=StorageManagerNativeFileSystem
+    RuntimeEnabled=FileSystemAccess,
+    ImplementedAs=StorageManagerFileSystemAccess
 ] partial interface StorageManager {
     [CallWith=ScriptState, RaisesException, Measure]
     Promise<FileSystemDirectoryHandle> getDirectory();
diff --git a/third_party/blink/renderer/modules/file_system_access/window_native_file_system.idl b/third_party/blink/renderer/modules/file_system_access/window_file_system_access.idl
similarity index 91%
rename from third_party/blink/renderer/modules/file_system_access/window_native_file_system.idl
rename to third_party/blink/renderer/modules/file_system_access/window_file_system_access.idl
index e2d65ef..f416f361 100644
--- a/third_party/blink/renderer/modules/file_system_access/window_native_file_system.idl
+++ b/third_party/blink/renderer/modules/file_system_access/window_file_system_access.idl
@@ -6,8 +6,8 @@
 // https://wicg.github.io/native-file-system/#api-getoriginprivatefilesystem
 [
     SecureContext,
-    RuntimeEnabled=NativeFileSystem,
-    ImplementedAs=GlobalNativeFileSystem
+    RuntimeEnabled=FileSystemAccess,
+    ImplementedAs=GlobalFileSystemAccess
 ] partial interface Window {
     [CallWith=ScriptState, RaisesException, Measure]
     Promise<sequence<FileSystemFileHandle>> showOpenFilePicker(
diff --git a/third_party/blink/renderer/modules/file_system_access/write_params.idl b/third_party/blink/renderer/modules/file_system_access/write_params.idl
index 9e6a39c..f62eff5 100644
--- a/third_party/blink/renderer/modules/file_system_access/write_params.idl
+++ b/third_party/blink/renderer/modules/file_system_access/write_params.idl
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://wicg.github.io/native-file-system/#enumdef-writecommandtype
+// https://wicg.github.io/file-system-access/#enumdef-writecommandtype
 enum WriteCommandType {
   "truncate",
   "seek",
   "write",
 };
 
-// https://wicg.github.io/native-file-system/#dictdef-writeparams
+// https://wicg.github.io/file-system-access/#dictdef-writeparams
 dictionary WriteParams {
   required WriteCommandType type;
   unsigned long long? size;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_value.cc b/third_party/blink/renderer/modules/indexeddb/idb_value.cc
index 6257731..99e6a8c 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_value.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_value.cc
@@ -22,10 +22,10 @@
     scoped_refptr<SharedBuffer> data,
     Vector<WebBlobInfo> blob_info,
     Vector<mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>>
-        native_file_system_tokens)
+        file_system_access_tokens)
     : data_(std::move(data)),
       blob_info_(std::move(blob_info)),
-      native_file_system_tokens_(std::move(native_file_system_tokens)) {}
+      file_system_access_tokens_(std::move(file_system_access_tokens)) {}
 
 IDBValue::~IDBValue() {
   if (isolate_ && external_allocated_size_)
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_value.h b/third_party/blink/renderer/modules/indexeddb/idb_value.h
index 344d71b..73cb14d 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_value.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_value.h
@@ -55,8 +55,8 @@
   const IDBKeyPath& KeyPath() const { return key_path_; }
 
   Vector<mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>>&
-  NativeFileSystemTokens() {
-    return native_file_system_tokens_;
+  FileSystemAccessTokens() {
+    return file_system_access_tokens_;
   }
 
   // Injects a primary key into a value coming from the backend.
@@ -104,7 +104,7 @@
   Vector<WebBlobInfo> blob_info_;
 
   Vector<mojo::PendingRemote<mojom::blink::FileSystemAccessTransferToken>>
-      native_file_system_tokens_;
+      file_system_access_tokens_;
 
   std::unique_ptr<IDBKey> primary_key_;
   IDBKeyPath key_path_;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.h b/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.h
index c4980be..5d60000 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.h
@@ -161,7 +161,7 @@
     DCHECK(owns_file_system_handles_) << __func__ << " called twice";
     owns_file_system_handles_ = false;
 #endif  // DCHECK_IS_ON()
-    return std::move(serialized_value_->NativeFileSystemTokens());
+    return std::move(serialized_value_->FileSystemAccessTokens());
   }
 
   size_t DataLengthBeforeWrapInBytes() { return original_data_length_; }
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.cc
index 16dad28..3b82ac6 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.cc
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.cc
@@ -174,7 +174,7 @@
     external_objects(const std::unique_ptr<blink::IDBValue>& input) {
   Vector<blink::mojom::blink::IDBExternalObjectPtr> external_objects;
   external_objects.ReserveInitialCapacity(
-      input->BlobInfo().size() + input->NativeFileSystemTokens().size());
+      input->BlobInfo().size() + input->FileSystemAccessTokens().size());
   for (const blink::WebBlobInfo& info : input->BlobInfo()) {
     auto blob_info = blink::mojom::blink::IDBBlobInfo::New();
     if (info.IsFile()) {
@@ -198,7 +198,7 @@
         blink::mojom::blink::IDBExternalObject::NewBlobOrFile(
             std::move(blob_info)));
   }
-  for (auto& token : input->NativeFileSystemTokens()) {
+  for (auto& token : input->FileSystemAccessTokens()) {
     external_objects.push_back(
         blink::mojom::blink::IDBExternalObject::NewFileSystemAccessToken(
             std::move(token)));
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_queue.cc b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.cc
index 6687744..c28f7640 100644
--- a/third_party/blink/renderer/modules/launch/dom_window_launch_queue.cc
+++ b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.cc
@@ -5,7 +5,7 @@
 #include "third_party/blink/renderer/modules/launch/dom_window_launch_queue.h"
 
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_handle.h"
 #include "third_party/blink/renderer/modules/launch/launch_params.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
@@ -24,7 +24,7 @@
 
 void DOMWindowLaunchQueue::UpdateLaunchFiles(
     LocalDOMWindow* window,
-    HeapVector<Member<NativeFileSystemHandle>> files) {
+    HeapVector<Member<FileSystemHandle>> files) {
   FromState(window)->launch_queue_->Enqueue(
       MakeGarbageCollected<LaunchParams>(std::move(files)));
 }
diff --git a/third_party/blink/renderer/modules/launch/dom_window_launch_queue.h b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.h
index d1337421..51ae652 100644
--- a/third_party/blink/renderer/modules/launch/dom_window_launch_queue.h
+++ b/third_party/blink/renderer/modules/launch/dom_window_launch_queue.h
@@ -32,7 +32,7 @@
   static Member<LaunchQueue> launchQueue(LocalDOMWindow&);
 
   static void UpdateLaunchFiles(LocalDOMWindow*,
-                                HeapVector<Member<NativeFileSystemHandle>>);
+                                HeapVector<Member<FileSystemHandle>>);
 
   void Trace(Visitor*) const override;
 
diff --git a/third_party/blink/renderer/modules/launch/launch_params.cc b/third_party/blink/renderer/modules/launch/launch_params.cc
index 02d23256..733f82f 100644
--- a/third_party/blink/renderer/modules/launch/launch_params.cc
+++ b/third_party/blink/renderer/modules/launch/launch_params.cc
@@ -4,13 +4,13 @@
 
 #include "third_party/blink/renderer/modules/launch/launch_params.h"
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_handle.h"
 #include "third_party/blink/renderer/platform/heap/visitor.h"
 
 namespace blink {
 
-LaunchParams::LaunchParams(HeapVector<Member<NativeFileSystemHandle>> files)
-    : files_(files) {}
+LaunchParams::LaunchParams(HeapVector<Member<FileSystemHandle>> files)
+    : files_(std::move(files)) {}
 
 LaunchParams::~LaunchParams() = default;
 
diff --git a/third_party/blink/renderer/modules/launch/launch_params.h b/third_party/blink/renderer/modules/launch/launch_params.h
index 4e773c8..07b1805 100644
--- a/third_party/blink/renderer/modules/launch/launch_params.h
+++ b/third_party/blink/renderer/modules/launch/launch_params.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_LAUNCH_PARAMS_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_LAUNCH_LAUNCH_PARAMS_H_
 
-#include "third_party/blink/renderer/modules/file_system_access/native_file_system_handle.h"
+#include "third_party/blink/renderer/modules/file_system_access/file_system_handle.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/heap/heap_allocator.h"
@@ -18,16 +18,16 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit LaunchParams(HeapVector<Member<NativeFileSystemHandle>> files);
+  explicit LaunchParams(HeapVector<Member<FileSystemHandle>> files);
   ~LaunchParams() override;
 
   // LaunchParams IDL interface.
-  const HeapVector<Member<NativeFileSystemHandle>>& files() { return files_; }
+  const HeapVector<Member<FileSystemHandle>>& files() { return files_; }
 
   void Trace(Visitor*) const override;
 
  private:
-  HeapVector<Member<NativeFileSystemHandle>> files_;
+  HeapVector<Member<FileSystemHandle>> files_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/launch/web_launch_service_impl.cc b/third_party/blink/renderer/modules/launch/web_launch_service_impl.cc
index 54a60780..8bd62ab 100644
--- a/third_party/blink/renderer/modules/launch/web_launch_service_impl.cc
+++ b/third_party/blink/renderer/modules/launch/web_launch_service_impl.cc
@@ -37,9 +37,9 @@
   if (!window_)
     return;
 
-  HeapVector<Member<NativeFileSystemHandle>> files;
+  HeapVector<Member<FileSystemHandle>> files;
   for (auto& entry : entries) {
-    files.push_back(NativeFileSystemHandle::CreateFromMojoEntry(
+    files.push_back(FileSystemHandle::CreateFromMojoEntry(
         std::move(entry), window_->GetExecutionContext()));
   }
 
diff --git a/third_party/blink/renderer/modules/webcodecs/color_space_primary_id.idl b/third_party/blink/renderer/modules/webcodecs/color_space_primary_id.idl
index 60392d5..68ef2e9 100644
--- a/third_party/blink/renderer/modules/webcodecs/color_space_primary_id.idl
+++ b/third_party/blink/renderer/modules/webcodecs/color_space_primary_id.idl
@@ -6,7 +6,7 @@
 
 enum ColorSpacePrimaryID {
   "BT709",
-  "BT407M",
+  "BT470M",
   "BT470BG",
   "SMPTE170M",
   "SMPTE240M",
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
index eacd2075..14e0339 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
@@ -16,6 +16,8 @@
 #include "media/media_buildflags.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom-blink.h"
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_encoded_video_chunk.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_decoder_config.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
@@ -26,6 +28,7 @@
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/to_v8.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -39,6 +42,125 @@
 
 namespace blink {
 
+bool ParseCodecString(const String& codec_string,
+                      media::VideoType& out_video_type,
+                      String& out_console_message) {
+  bool is_codec_ambiguous = true;
+  media::VideoCodec codec = media::kUnknownVideoCodec;
+  media::VideoCodecProfile profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
+  media::VideoColorSpace color_space = media::VideoColorSpace::REC709();
+  uint8_t level = 0;
+  bool parse_succeeded =
+      media::ParseVideoCodecString("", codec_string.Utf8(), &is_codec_ambiguous,
+                                   &codec, &profile, &level, &color_space);
+
+  if (!parse_succeeded) {
+    out_console_message = "Failed to parse codec string.";
+    return false;
+  }
+
+  if (is_codec_ambiguous) {
+    out_console_message = "Codec string is ambiguous.";
+    return false;
+  }
+
+  out_video_type = {codec, profile, level, color_space};
+  return true;
+}
+
+bool IsValidConfig(const VideoDecoderConfig& config,
+                   media::VideoType& out_video_type,
+                   String& out_console_message) {
+  if (!ParseCodecString(config.codec(), out_video_type, out_console_message))
+    return false;
+
+  if (config.hasCodedWidth()) {
+    if (config.codedWidth() == 0) {
+      out_console_message =
+          "Invalid codedWidth. Value must be greater than zero.";
+      return false;
+    }
+
+    uint32_t crop_left = config.hasCropLeft() ? config.cropLeft() : 0;
+    uint32_t crop_width =
+        config.hasCropWidth() ? config.cropWidth() : config.codedWidth();
+
+    if (crop_width == 0) {
+      out_console_message =
+          "Invalid cropWidth. Value must be greater than zero.";
+      return false;
+    }
+
+    if (crop_left + crop_width > config.codedWidth()) {
+      out_console_message =
+          "Invalid cropLeft + cropWidth. Sum must not exceed codedWidth.";
+      return false;
+    }
+  } else {  // !config.hasCodedWidth()
+    if (config.hasCropLeft()) {
+      out_console_message =
+          "Invalid config. cropLeft specified without codedWidth.";
+      return false;
+    }
+
+    if (config.hasCropWidth()) {
+      out_console_message =
+          "Invalid config. cropWidth specified without codedWidth.";
+      return false;
+    }
+  }
+
+  if (config.hasCodedHeight()) {
+    if (config.codedHeight() == 0) {
+      out_console_message =
+          "Invalid codedHeight. Value must be greater than zero.";
+      return false;
+    }
+
+    uint32_t crop_top = config.hasCropTop() ? config.cropTop() : 0;
+    uint32_t crop_height =
+        config.hasCropHeight() ? config.cropHeight() : config.codedHeight();
+
+    if (crop_height == 0) {
+      out_console_message =
+          "Invalid cropHeight. Value must be greater than zero.";
+      return false;
+    }
+
+    if (crop_top + crop_height > config.codedHeight()) {
+      out_console_message =
+          "Invalid cropTop + cropHeight. Sum must not exceed codedHeight.";
+      return false;
+    }
+  } else {  // !config.hasCodedHeight()
+    if (config.hasCropTop()) {
+      out_console_message =
+          "Invalid config. cropTop specified without codedHeight.";
+      return false;
+    }
+
+    if (config.hasCropHeight()) {
+      out_console_message =
+          "Invalid config. cropHeight specified without codedHeight.";
+      return false;
+    }
+  }
+
+  if (config.hasDisplayWidth() && config.displayWidth() == 0) {
+    out_console_message =
+        "Invalid displayWidth. Value must be greater than zero.";
+    return false;
+  }
+
+  if (config.hasDisplayHeight() && config.displayHeight() == 0) {
+    out_console_message =
+        "Invalid displayHeight. Value must be greater than zero.";
+    return false;
+  }
+
+  return true;
+}
+
 // static
 std::unique_ptr<VideoDecoderTraits::MediaDecoderType>
 VideoDecoderTraits::CreateDecoder(
@@ -96,6 +218,24 @@
 }
 
 // static
+ScriptPromise VideoDecoder::isConfigSupported(ScriptState* script_state,
+                                              const VideoDecoderConfig* config,
+                                              ExceptionState& exception_state) {
+  media::VideoType video_type;
+  String console_message;
+
+  if (!IsValidConfig(*config, video_type, console_message)) {
+    exception_state.ThrowTypeError(console_message);
+    return ScriptPromise();
+  }
+
+  // TODO(https://crbug.com/1164013): Add async checks for hardware support upon
+  // adding "acceleration" options to the config.
+  bool is_supported = media::IsSupportedVideoType(video_type);
+  return ScriptPromise::Cast(script_state, ToV8(is_supported, script_state));
+}
+
+// static
 CodecConfigEval VideoDecoder::MakeMediaVideoDecoderConfig(
     const ConfigType& config,
     MediaConfigType& out_media_config,
@@ -104,29 +244,10 @@
     std::unique_ptr<media::mp4::AVCDecoderConfigurationRecord>& out_h264_avcc,
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
     String& out_console_message) {
-  bool is_codec_ambiguous = true;
-  media::VideoCodec codec = media::kUnknownVideoCodec;
-  media::VideoCodecProfile profile = media::VIDEO_CODEC_PROFILE_UNKNOWN;
-  media::VideoColorSpace color_space = media::VideoColorSpace::REC709();
-  uint8_t level = 0;
-  bool parse_succeeded = media::ParseVideoCodecString(
-      "", config.codec().Utf8(), &is_codec_ambiguous, &codec, &profile, &level,
-      &color_space);
+  media::VideoType video_type;
 
-  if (!parse_succeeded) {
-    out_console_message = "Failed to parse codec string.";
+  if (!IsValidConfig(config, video_type, out_console_message))
     return CodecConfigEval::kInvalid;
-  }
-
-  if (is_codec_ambiguous) {
-    out_console_message = "Codec string is ambiguous.";
-    return CodecConfigEval::kInvalid;
-  }
-
-  if (!media::IsSupportedVideoType({codec, profile, level, color_space})) {
-    out_console_message = "Configuration is not supported.";
-    return CodecConfigEval::kUnsupported;
-  }
 
   // TODO(sandersd): Can we allow shared ArrayBuffers?
   std::vector<uint8_t> extra_data;
@@ -147,7 +268,7 @@
   }
 
 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
-  if (codec == media::kCodecH264) {
+  if (video_type.codec == media::kCodecH264) {
     if (extra_data.empty()) {
       out_console_message =
           "H.264 configuration must include an avcC description.";
@@ -169,7 +290,7 @@
     out_h264_converter.reset();
   }
 #else
-  if (codec == media::kCodecH264) {
+  if (video_type.codec == media::kCodecH264) {
     out_console_message = "H.264 decoding is not supported.";
     return CodecConfigEval::kUnsupported;
   }
@@ -180,11 +301,11 @@
   // match.
   gfx::Size size = gfx::Size(1280, 720);
 
-  out_media_config.Initialize(codec, profile,
-                              media::VideoDecoderConfig::AlphaMode::kIsOpaque,
-                              color_space, media::kNoTransformation, size,
-                              gfx::Rect(gfx::Point(), size), size, extra_data,
-                              media::EncryptionScheme::kUnencrypted);
+  out_media_config.Initialize(
+      video_type.codec, video_type.profile,
+      media::VideoDecoderConfig::AlphaMode::kIsOpaque, video_type.color_space,
+      media::kNoTransformation, size, gfx::Rect(gfx::Point(), size), size,
+      extra_data, media::EncryptionScheme::kUnencrypted);
 
   return CodecConfigEval::kSupported;
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.h b/third_party/blink/renderer/modules/webcodecs/video_decoder.h
index c88e261..b3020dfb 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.h
@@ -48,6 +48,7 @@
 class VideoDecoderInit;
 class VideoFrame;
 class V8VideoFrameOutputCallback;
+class ScriptPromise;
 
 class MODULES_EXPORT VideoDecoderTraits {
  public:
@@ -86,6 +87,10 @@
                               const VideoDecoderInit*,
                               ExceptionState&);
 
+  static ScriptPromise isConfigSupported(ScriptState*,
+                                         const VideoDecoderConfig*,
+                                         ExceptionState&);
+
   // For use by MediaSource and by ::MakeMediaConfig.
   static CodecConfigEval MakeMediaVideoDecoderConfig(
       const ConfigType& config,
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.idl b/third_party/blink/renderer/modules/webcodecs/video_decoder.idl
index f805b5d44..3213222 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.idl
@@ -35,6 +35,9 @@
   // TODO(sandersd): Consider emitting an event when this number decreases.
   readonly attribute long decodeQueueSize;
 
+  // Which state the decoder is in, indicating which methods can be called.
+  readonly attribute CodecState state;
+
   // Set the stream configuration for future decode() requests.
   //
   // The next decode request must be for a keyframe.
@@ -73,6 +76,7 @@
   // Not recoverable: make a new VideoDecoder if needed.
   [RaisesException] void close();
 
-  // Which state the decoder is in, indicating which methods can be called.
-  readonly attribute CodecState state;
+  // Call prior to configure() to determine whether config will be supported.
+  [CallWith=ScriptState, RaisesException]
+  static Promise<boolean> isConfigSupported(VideoDecoderConfig config);
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.cc b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
index 314f845..60fcc365 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
@@ -213,6 +213,11 @@
         // a VideoFrame instead of using writable_addr() here.
         reinterpret_cast<uint8_t*>(pm.writable_addr()), pm.computeByteSize(),
         timestamp);
+    if (!frame) {
+      exception_state.ThrowDOMException(DOMExceptionCode::kOperationError,
+                                        "Failed to create video frame");
+      return nullptr;
+    }
     frame->set_color_space(gfx::ColorSpace(*sk_color_space));
     frame->AddDestructionObserver(ConvertToBaseOnceCallback(CrossThreadBindOnce(
         base::DoNothing::Once<sk_sp<SkImage>>(), std::move(sk_image))));
diff --git a/third_party/blink/renderer/modules/webgl/DEPS b/third_party/blink/renderer/modules/webgl/DEPS
index bb4a18d4..2e4eb39 100644
--- a/third_party/blink/renderer/modules/webgl/DEPS
+++ b/third_party/blink/renderer/modules/webgl/DEPS
@@ -8,6 +8,7 @@
     "+gpu/config/gpu_feature_info.h",
     "+media/base/video_frame.h",
     "+media/base/wait_and_replace_sync_token_client.h",
+    "+media/video/gpu_memory_buffer_video_frame_pool.h",
     "+skia/ext",
     "+third_party/blink/renderer/modules/webcodecs",
     "+ui/gl/gpu_preference.h",
diff --git a/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.cc b/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.cc
index ed8db318..cf82e9b2 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.cc
@@ -6,12 +6,16 @@
 
 #include "build/build_config.h"
 #include "media/base/wait_and_replace_sync_token_client.h"
+#include "media/video/gpu_memory_buffer_video_frame_pool.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_color_space.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_webgl_webcodecs_texture_info.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_webgl_webcodecs_video_frame_handle.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_unowned_texture.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/scheduler/public/worker_pool.h"
+#include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 #include "ui/gfx/color_transform.h"
 
 namespace blink {
@@ -19,15 +23,168 @@
 namespace {
 
 #if defined(OS_WIN)
-const char* kRequiredExtension = "GL_NV_EGL_stream_consumer_external";
+const char kRequiredExtension[] = "GL_NV_EGL_stream_consumer_external";
 #elif defined(OS_MAC)
-const char* kRequiredExtension = "GL_ANGLE_texture_rectangle";
+const char kRequiredExtension[] = "GL_ANGLE_texture_rectangle";
 #elif defined(OS_ANDROID) || defined(OS_CHROMEOS)
-const char* kRequiredExtension = "GL_OES_EGL_image_external";
+const char kRequiredExtension[] = "GL_OES_EGL_image_external";
 #else
-const char* kRequiredExtension = "";
+const char kRequiredExtension[] = "";
 #endif
 
+void FillVideoColorSpace(VideoColorSpace* video_color_space,
+                         gfx::ColorSpace& gfx_color_space) {
+  gfx::ColorSpace::PrimaryID primaries = gfx_color_space.GetPrimaryID();
+  switch (primaries) {
+    case gfx::ColorSpace::PrimaryID::BT709:
+      video_color_space->setPrimaryID("BT709");
+      break;
+    case gfx::ColorSpace::PrimaryID::BT470M:
+      video_color_space->setPrimaryID("BT470M");
+      break;
+    case gfx::ColorSpace::PrimaryID::BT470BG:
+      video_color_space->setPrimaryID("BT470BG");
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTE170M:
+      video_color_space->setPrimaryID("SMPTE170M");
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTE240M:
+      video_color_space->setPrimaryID("SMPTE240M");
+      break;
+    case gfx::ColorSpace::PrimaryID::FILM:
+      video_color_space->setPrimaryID("FILM");
+      break;
+    case gfx::ColorSpace::PrimaryID::BT2020:
+      video_color_space->setPrimaryID("BT2020");
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTEST428_1:
+      video_color_space->setPrimaryID("SMPTEST428_1");
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTEST431_2:
+      video_color_space->setPrimaryID("SMPTEST431_2");
+      break;
+    case gfx::ColorSpace::PrimaryID::SMPTEST432_1:
+      video_color_space->setPrimaryID("SMPTEST432_1");
+      break;
+    // TODO(jie.a.chen@intel.com): Need to check EBU_3213_E.
+    default:;
+  }
+
+  gfx::ColorSpace::TransferID transfer = gfx_color_space.GetTransferID();
+  switch (transfer) {
+    case gfx::ColorSpace::TransferID::BT709:
+#if defined(OS_MAC)
+    // TODO(jie.a.chen@intel.com): BT709_APPLE is not available in WebCodecs.
+    case gfx::ColorSpace::TransferID::BT709_APPLE:
+#endif
+      video_color_space->setTransferID("BT709");
+      break;
+    case gfx::ColorSpace::TransferID::GAMMA22:
+      video_color_space->setTransferID("GAMMA22");
+      break;
+    case gfx::ColorSpace::TransferID::GAMMA28:
+      video_color_space->setTransferID("GAMMA28");
+      break;
+    case gfx::ColorSpace::TransferID::SMPTE170M:
+      video_color_space->setTransferID("SMPTE170M");
+      break;
+    case gfx::ColorSpace::TransferID::SMPTE240M:
+      video_color_space->setTransferID("SMPTE240M");
+      break;
+    case gfx::ColorSpace::TransferID::LINEAR:
+      video_color_space->setTransferID("LINEAR");
+      break;
+    case gfx::ColorSpace::TransferID::LOG:
+      video_color_space->setTransferID("LOG");
+      break;
+    case gfx::ColorSpace::TransferID::LOG_SQRT:
+      video_color_space->setTransferID("LOG_SQRT");
+      break;
+    case gfx::ColorSpace::TransferID::IEC61966_2_4:
+      video_color_space->setTransferID("IEC61966_2_4");
+      break;
+    case gfx::ColorSpace::TransferID::BT1361_ECG:
+      video_color_space->setTransferID("BT1361_ECG");
+      break;
+    case gfx::ColorSpace::TransferID::IEC61966_2_1:
+      video_color_space->setTransferID("IEC61966_2_1");
+      break;
+    case gfx::ColorSpace::TransferID::BT2020_10:
+      video_color_space->setTransferID("BT2020_10");
+      break;
+
+    case gfx::ColorSpace::TransferID::BT2020_12:
+      video_color_space->setTransferID("BT2020_12");
+      break;
+    case gfx::ColorSpace::TransferID::SMPTEST2084:
+      video_color_space->setTransferID("SMPTEST2084");
+      break;
+    case gfx::ColorSpace::TransferID::SMPTEST428_1:
+      video_color_space->setTransferID("SMPTEST428_1");
+      break;
+    default:;
+  }
+
+  gfx::ColorSpace::MatrixID matrix = gfx_color_space.GetMatrixID();
+  switch (matrix) {
+    case gfx::ColorSpace::MatrixID::RGB:
+      video_color_space->setMatrixID("RGB");
+      break;
+    case gfx::ColorSpace::MatrixID::BT709:
+      video_color_space->setMatrixID("BT709");
+      break;
+    case gfx::ColorSpace::MatrixID::FCC:
+      video_color_space->setMatrixID("FCC");
+      break;
+    case gfx::ColorSpace::MatrixID::BT470BG:
+      video_color_space->setMatrixID("BT470BG");
+      break;
+    case gfx::ColorSpace::MatrixID::SMPTE170M:
+      video_color_space->setMatrixID("SMPTE170M");
+      break;
+    case gfx::ColorSpace::MatrixID::SMPTE240M:
+      video_color_space->setMatrixID("SMPTE240M");
+      break;
+    case gfx::ColorSpace::MatrixID::YCOCG:
+      video_color_space->setMatrixID("YCOCG");
+      break;
+    case gfx::ColorSpace::MatrixID::BT2020_NCL:
+      video_color_space->setMatrixID("BT2020_NCL");
+      break;
+    case gfx::ColorSpace::MatrixID::BT2020_CL:
+      video_color_space->setMatrixID("BT2020_CL");
+      break;
+    case gfx::ColorSpace::MatrixID::YDZDX:
+      video_color_space->setMatrixID("YDZDX");
+      break;
+    default:;
+  }
+
+  gfx::ColorSpace::RangeID range = gfx_color_space.GetRangeID();
+  switch (range) {
+    case gfx::ColorSpace::RangeID::LIMITED:
+      video_color_space->setRangeID("LIMITED");
+      break;
+    case gfx::ColorSpace::RangeID::FULL:
+      video_color_space->setRangeID("FULL");
+      break;
+    case gfx::ColorSpace::RangeID::DERIVED:
+      video_color_space->setRangeID("DERIVED");
+      break;
+    default:;
+  }
+}
+
+void GetMediaTaskRunnerAndGpuFactoriesOnMainThread(
+    scoped_refptr<base::SingleThreadTaskRunner>* media_task_runner_out,
+    media::GpuVideoAcceleratorFactories** gpu_factories_out,
+    base::WaitableEvent* waitable_event) {
+  DCHECK(IsMainThread());
+  *media_task_runner_out = Platform::Current()->MediaThreadTaskRunner();
+  *gpu_factories_out = Platform::Current()->GetGpuFactories();
+  waitable_event->Signal();
+}
+
 }  // namespace
 
 WebGLWebCodecsVideoFrame::WebGLWebCodecsVideoFrame(
@@ -40,29 +197,39 @@
   context->ContextGL()->Enable(GC3D_TEXTURE_RECTANGLE_ARB);
 #endif
 
-  // TODO(jie.a.chen@intel.com): More supports for HDR video.
 #if defined(OS_WIN)
-  sampler_type_ = "samplerExternalOES";
-  sampler_func_ = "texture2D";
   formats_supported[media::PIXEL_FORMAT_NV12] = true;
-  auto& components = format_to_components_map_[media::PIXEL_FORMAT_NV12];
-  components[media::VideoFrame::kYPlane] = "r";
-  components[media::VideoFrame::kUPlane] = "rg";
+  auto& components_nv12 = format_to_components_map_[media::PIXEL_FORMAT_NV12];
+  components_nv12[media::VideoFrame::kYPlane] = "r";
+  components_nv12[media::VideoFrame::kUPlane] = "rg";
 #elif defined(OS_MAC)
-  sampler_type_ = "sampler2DRect";
-  sampler_func_ = "texture2DRect";
   formats_supported[media::PIXEL_FORMAT_XRGB] = true;
-  auto& components = format_to_components_map_[media::PIXEL_FORMAT_XRGB];
-  components[media::VideoFrame::kYPlane] = "rgba";
-#elif defined(OS_ANDROID) || defined(OS_CHROMEOS)
-  sampler_type_ = "samplerExternalOES";
-  sampler_func_ = "texture2D";
+  auto& components_xrgb = format_to_components_map_[media::PIXEL_FORMAT_XRGB];
+  components_xrgb[media::VideoFrame::kYPlane] = "rgba";
+#elif defined(OS_ANDROID)
   formats_supported[media::PIXEL_FORMAT_ABGR] = true;
-  auto& components = format_to_components_map_[media::PIXEL_FORMAT_ABGR];
-  components[media::VideoFrame::kYPlane] = "rgb";
+  auto& components_abgr = format_to_components_map_[media::PIXEL_FORMAT_ABGR];
+  components_abgr[media::VideoFrame::kYPlane] = "rgb";
+
+  // GpuMemoryBufferVideoFramePool
+  formats_supported[media::PIXEL_FORMAT_NV12] = true;
+  auto& components_nv12 = format_to_components_map_[media::PIXEL_FORMAT_NV12];
+  components_nv12[media::VideoFrame::kYPlane] = "r";
+  components_nv12[media::VideoFrame::kUPlane] = "rg";
+#elif defined(OS_CHROMEOS)
+  formats_supported[media::PIXEL_FORMAT_ABGR] = true;
+  auto& components_abgr = format_to_components_map_[media::PIXEL_FORMAT_ABGR];
+  components_abgr[media::VideoFrame::kYPlane] = "rgb";
 #endif
 }
 
+WebGLWebCodecsVideoFrame::~WebGLWebCodecsVideoFrame() {
+  if (gpu_memory_buffer_pool_) {
+    media_task_runner_->DeleteSoon(FROM_HERE,
+                                   std::move(gpu_memory_buffer_pool_));
+  }
+}
+
 WebGLExtensionName WebGLWebCodecsVideoFrame::GetName() const {
   return kWebGLWebCodecsVideoFrameName;
 }
@@ -92,29 +259,68 @@
   if (!video_frame || scoped.IsLost())
     return nullptr;
 
+  const char* sampler_type = "sampler2D";
+  const char* sampler_func = "texture2D";
+  gfx::ColorSpace src_color_space = gfx::ColorSpace::CreateREC709();
+  media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN;
+#if defined(OS_WIN)
+  sampler_type = "samplerExternalOES";
+  pixel_format = media::PIXEL_FORMAT_NV12;
+#elif defined(OS_MAC)
+  sampler_type = "sampler2DRect";
+  sampler_func = "texture2DRect";
+  pixel_format = media::PIXEL_FORMAT_XRGB;
+#elif defined(OS_ANDROID) || defined(OS_CHROMEOS)
+  sampler_type = "samplerExternalOES";
+  pixel_format = media::PIXEL_FORMAT_ABGR;
+  src_color_space = gfx::ColorSpace::CreateSRGB();
+#endif
+
   scoped_refptr<media::VideoFrame> frame = video_frame->frame();
   if (!frame->HasTextures()) {
-    exception_state.ThrowTypeError("Unable to import a software video frame.");
+    InitializeGpuMemoryBufferPool();
+    base::WaitableEvent waitable_event;
+    media_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            &media::GpuMemoryBufferVideoFramePool::MaybeCreateHardwareFrame,
+            base::Unretained(gpu_memory_buffer_pool_.get()),
+            base::RetainedRef(frame),
+            base::BindOnce(
+                &WebGLWebCodecsVideoFrame::OnHardwareVideoFrameCreated,
+                base::Unretained(this), base::Unretained(&waitable_event))));
+    waitable_event.Wait();
+
+    if (frame == hardware_video_frame_) {
+      exception_state.ThrowTypeError(
+          "Unable to import a software video frame.");
+      return nullptr;
+    }
+    frame = std::move(hardware_video_frame_);
+#if defined(OS_WIN)
+    sampler_type = "sampler2D";
+#elif defined(OS_ANDROID)
+    sampler_type = "sampler2D";
+    pixel_format = frame->format();
+    src_color_space = frame->ColorSpace();
+#endif
+  }
+
+  if (!formats_supported[pixel_format]) {
+    exception_state.ThrowTypeError(
+        String::Format("VideoPixelFormat:%s is not supported yet.",
+                       media::VideoPixelFormatToString(pixel_format).c_str()));
     return nullptr;
   }
 
-  if (!formats_supported[frame->format()]) {
-    std::ostringstream ss;
-    ss << "VideoPixelFormat:"
-       << media::VideoPixelFormatToString(frame->format())
-       << " is not supported yet.";
-    exception_state.ThrowTypeError(ss.str().c_str());
-    return nullptr;
-  }
-
-  const auto& components = format_to_components_map_[frame->format()];
+  const auto& components = format_to_components_map_[pixel_format];
   HeapVector<Member<WebGLWebCodecsTextureInfo>> info_array;
   for (size_t tex = 0; tex < frame->NumTextures(); ++tex) {
     WebGLWebCodecsTextureInfo* texture_info =
         MakeGarbageCollected<WebGLWebCodecsTextureInfo>();
     info_array.push_back(texture_info);
-    texture_info->setSamplerType(sampler_type_.c_str());
-    texture_info->setSamplerFunc(sampler_func_.c_str());
+    texture_info->setSamplerType(sampler_type);
+    texture_info->setSamplerFunc(sampler_func);
     texture_info->setComponents(components[tex].c_str());
 
     auto* gl = scoped.Context()->ContextGL();
@@ -144,45 +350,27 @@
   if (std::string(kRequiredExtension) != "") {
     video_frame_handle->setRequiredExtension(kRequiredExtension);
   }
+  // Remove "PIXEL_FORMAT_" prefix
+  video_frame_handle->setPixelFormat(
+      &VideoPixelFormatToString(pixel_format)[13]);
+
   // TODO(jie.a.chen@intel.com): Is the colorspace/flip-y/pre-alpha of video
   // frame specific to OS only? For the same OS, does it vary for different
   // video streams?
   video_frame_handle->setFlipY(true);
   video_frame_handle->setPremultipliedAlpha(false);
-  gfx::ColorSpace src_color_space = frame->ColorSpace();
 #if defined(OS_WIN)
-  video_frame_handle->setPixelFormat("NV12");
+  DCHECK(frame->format() == media::PIXEL_FORMAT_NV12);
+  src_color_space = frame->ColorSpace();
 #elif defined(OS_MAC)
   video_frame_handle->setRequiredExtension("GL_ARB_texture_rectangle");
-  video_frame_handle->setPixelFormat("XRGB");
   video_frame_handle->setPremultipliedAlpha(true);
+  src_color_space = frame->ColorSpace();
   src_color_space = src_color_space.GetAsFullRangeRGB();
-#elif defined(OS_ANDROID) || defined(OS_CHROMEOS)
-  if (!frame->ColorSpace().IsValid()) {
-    video_frame_handle->setPixelFormat("ABGR");
-    src_color_space = gfx::ColorSpace::CreateSRGB();
-  }
 #endif
   VideoColorSpace* video_frame_color_space =
       MakeGarbageCollected<VideoColorSpace>();
-  // TODO(jie.a.chen@intel.com): Add ToString() for color space members.
-#if defined(OS_WIN)
-  video_frame_color_space->setPrimaryID("BT709");
-  video_frame_color_space->setTransferID("BT709");
-  video_frame_color_space->setMatrixID("BT709");
-  video_frame_color_space->setRangeID("LIMITED");
-#elif defined(OS_MAC)
-  video_frame_color_space->setPrimaryID("SMPTE240M");
-  // TODO(jie.a.chen@intel.com): The actual BT709_APPLE is not available.
-  video_frame_color_space->setTransferID("BT709");
-  video_frame_color_space->setMatrixID("RGB");
-  video_frame_color_space->setRangeID("FULL");
-#elif defined(OS_ANDROID) || defined(OS_CHROMEOS)
-  video_frame_color_space->setPrimaryID("BT709");
-  video_frame_color_space->setTransferID("IEC61966_2_1");
-  video_frame_color_space->setMatrixID("RGB");
-  video_frame_color_space->setRangeID("FULL");
-#endif
+  FillVideoColorSpace(video_frame_color_space, src_color_space);
   video_frame_handle->setColorSpace(video_frame_color_space);
 
   gfx::ColorSpace dst_color_space = gfx::ColorSpace::CreateSRGB();
@@ -225,4 +413,39 @@
   return true;
 }
 
+void WebGLWebCodecsVideoFrame::OnHardwareVideoFrameCreated(
+    base::WaitableEvent* waitable_event,
+    scoped_refptr<media::VideoFrame> video_frame) {
+  hardware_video_frame_ = std::move(video_frame);
+  waitable_event->Signal();
+}
+
+void WebGLWebCodecsVideoFrame::InitializeGpuMemoryBufferPool() {
+  if (!worker_task_runner_) {
+    worker_task_runner_ = worker_pool::CreateSequencedTaskRunner({});
+  }
+  if (!gpu_memory_buffer_pool_) {
+    media::GpuVideoAcceleratorFactories* gpu_factories = nullptr;
+    if (IsMainThread()) {
+      media_task_runner_ = Platform::Current()->MediaThreadTaskRunner();
+      gpu_factories = Platform::Current()->GetGpuFactories();
+    } else {
+      base::WaitableEvent waitable_event;
+      // TODO(crbug.com/1164152): Lift the main thread restriction.
+      if (PostCrossThreadTask(
+              *Thread::MainThread()->GetTaskRunner(), FROM_HERE,
+              CrossThreadBindOnce(
+                  &GetMediaTaskRunnerAndGpuFactoriesOnMainThread,
+                  CrossThreadUnretained(&media_task_runner_),
+                  CrossThreadUnretained(&gpu_factories),
+                  CrossThreadUnretained(&waitable_event)))) {
+        waitable_event.Wait();
+      }
+    }
+    gpu_memory_buffer_pool_ =
+        std::make_unique<media::GpuMemoryBufferVideoFramePool>(
+            media_task_runner_, worker_task_runner_, gpu_factories);
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.h b/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.h
index c6fa3374..644d01c 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.h
+++ b/third_party/blink/renderer/modules/webgl/webgl_webcodecs_video_frame.h
@@ -10,6 +10,10 @@
 #include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
 #include "third_party/blink/renderer/modules/webgl/webgl_extension.h"
 
+namespace media {
+class GpuMemoryBufferVideoFramePool;
+}  // namespace media
+
 namespace blink {
 
 class WebGLWebCodecsVideoFrameHandle;
@@ -18,6 +22,8 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
+  ~WebGLWebCodecsVideoFrame() override;
+
   static bool Supported(WebGLRenderingContextBase*);
   static const char* ExtensionName();
 
@@ -36,9 +42,13 @@
                          ExceptionState&);
 
  private:
+  void OnHardwareVideoFrameCreated(
+      base::WaitableEvent* waitable_event,
+      scoped_refptr<media::VideoFrame> video_frame);
+
+  void InitializeGpuMemoryBufferPool();
+
   std::bitset<media::PIXEL_FORMAT_MAX + 1> formats_supported;
-  std::string sampler_type_;
-  std::string sampler_func_;
   std::array<std::array<std::string, media::VideoFrame::kMaxPlanes>,
              media::PIXEL_FORMAT_MAX + 1>
       format_to_components_map_;
@@ -47,6 +57,11 @@
   // This holds the reference for all video frames being imported, but not
   // yet released.
   VideoFrameHandleMap tex0_to_video_frame_map_;
+
+  std::unique_ptr<media::GpuMemoryBufferVideoFramePool> gpu_memory_buffer_pool_;
+  scoped_refptr<base::SequencedTaskRunner> worker_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
+  scoped_refptr<media::VideoFrame> hardware_video_frame_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 1d87a9f..c37c11a 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1658,6 +1658,7 @@
     "//third_party/blink/public/common",
     "//third_party/blink/public/strings",
     "//third_party/blink/renderer/platform/wtf",
+    "//third_party/blink/renderer/platform/wtf:buildflags",
     "//third_party/ced",
     "//third_party/emoji-segmenter",
     "//third_party/harfbuzz-ng:hb_scoped_util",
diff --git a/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h b/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h
index 865c5c5..80a5914 100644
--- a/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h
+++ b/third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h
@@ -11,9 +11,17 @@
 #include "base/macros.h"
 #include "third_party/blink/renderer/platform/heap/unified_heap_marking_visitor.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
+#include "third_party/blink/renderer/platform/wtf/buildflags.h"
 #include "third_party/blink/renderer/platform/wtf/vector_traits.h"
 #include "v8/include/v8.h"
 
+namespace cppgc {
+
+template <typename T>
+struct TraceTrait;
+
+}  // namespace cppgc
+
 namespace blink {
 
 template <typename T>
@@ -123,13 +131,37 @@
   }
 
   v8::TracedReference<T> handle_;
-};
 
+  friend struct cppgc::TraceTrait<TraceWrapperV8Reference<T>>;
+};
+}  // namespace blink
+
+#if BUILDFLAG(USE_V8_OILPAN)
+
+namespace cppgc {
 template <typename T>
 struct TraceTrait<TraceWrapperV8Reference<T>> {
   STATIC_ONLY(TraceTrait);
 
- public:
+  static cppgc::TraceDescriptor GetTraceDescriptor(
+      const TraceWrapperV8Reference<T>* ref) {
+    return {ref, Trace};
+  }
+
+  static void Trace(Visitor* visitor, const void* self) {
+    visitor->Trace(
+        static_cast<const TraceWrapperV8Reference<T>*>(self)->handle_);
+  }
+};
+}  // namespace cppgc
+
+#else  // !USE_V8_OILPAN
+
+namespace blink {
+template <typename T>
+struct TraceTrait<TraceWrapperV8Reference<T>> {
+  STATIC_ONLY(TraceTrait);
+
   static TraceDescriptor GetTraceDescriptor(
       const TraceWrapperV8Reference<T>* ref) {
     return {ref, TraceTrait<TraceWrapperV8Reference<T>>::Trace};
@@ -139,9 +171,10 @@
     visitor->Trace(*static_cast<const TraceWrapperV8Reference<T>*>(ref));
   }
 };
-
 }  // namespace blink
 
+#endif  // !USE_V8_OILPAN
+
 namespace WTF {
 
 template <typename T>
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 847557a..9f46f5ea 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -503,6 +503,30 @@
   return UnionRect(bounds, offset_bounds);
 }
 
+static void AdjustHoleForSideClipping(FloatRect& hole_rect,
+                                      const FloatSize& shadow_offset,
+                                      float shadow_blur,
+                                      GraphicsContext::Edges clipped_edges) {
+  if (clipped_edges & GraphicsContext::kLeftEdge) {
+    float extend_by = std::max(shadow_offset.Width(), 0.0f) + shadow_blur;
+    hole_rect.Move(-extend_by, 0);
+    hole_rect.SetWidth(hole_rect.Width() + extend_by);
+  }
+  if (clipped_edges & GraphicsContext::kTopEdge) {
+    float extend_by = std::max(shadow_offset.Height(), 0.0f) + shadow_blur;
+    hole_rect.Move(0, -extend_by);
+    hole_rect.SetHeight(hole_rect.Height() + extend_by);
+  }
+  if (clipped_edges & GraphicsContext::kRightEdge) {
+    float shrink_by = std::min(shadow_offset.Width(), 0.0f) - shadow_blur;
+    hole_rect.SetWidth(hole_rect.Width() - shrink_by);
+  }
+  if (clipped_edges & GraphicsContext::kBottomEdge) {
+    float shrink_by = std::min(shadow_offset.Height(), 0.0f) - shadow_blur;
+    hole_rect.SetHeight(hole_rect.Height() - shrink_by);
+  }
+}
+
 void GraphicsContext::DrawInnerShadow(const FloatRoundedRect& rect,
                                       const Color& orig_shadow_color,
                                       const FloatSize& shadow_offset,
@@ -514,34 +538,13 @@
 
   FloatRect hole_rect(rect.Rect());
   hole_rect.Inflate(-shadow_spread);
-
   if (hole_rect.IsEmpty()) {
     FillRoundedRect(rect, Color(shadow_color));
     return;
   }
+  AdjustHoleForSideClipping(hole_rect, shadow_offset, shadow_blur,
+                            clipped_edges);
 
-  if (clipped_edges & kLeftEdge) {
-    hole_rect.Move(-std::max(shadow_offset.Width(), 0.0f) - shadow_blur, 0);
-    hole_rect.SetWidth(hole_rect.Width() +
-                       std::max(shadow_offset.Width(), 0.0f) + shadow_blur);
-  }
-  if (clipped_edges & kTopEdge) {
-    hole_rect.Move(0, -std::max(shadow_offset.Height(), 0.0f) - shadow_blur);
-    hole_rect.SetHeight(hole_rect.Height() +
-                        std::max(shadow_offset.Height(), 0.0f) + shadow_blur);
-  }
-  if (clipped_edges & kRightEdge)
-    hole_rect.SetWidth(hole_rect.Width() -
-                       std::min(shadow_offset.Width(), 0.0f) + shadow_blur);
-  if (clipped_edges & kBottomEdge)
-    hole_rect.SetHeight(hole_rect.Height() -
-                        std::min(shadow_offset.Height(), 0.0f) + shadow_blur);
-
-  Color fill_color(SkColorGetR(shadow_color), SkColorGetG(shadow_color),
-                   SkColorGetB(shadow_color), 255);
-
-  FloatRect outer_rect = AreaCastingShadowInHole(rect.Rect(), shadow_blur,
-                                                 shadow_spread, shadow_offset);
   FloatRoundedRect rounded_hole(hole_rect, rect.GetRadii());
 
   GraphicsContextStateSaver state_saver(*this);
@@ -556,11 +559,15 @@
   }
 
   DrawLooperBuilder draw_looper_builder;
-  draw_looper_builder.AddShadow(FloatSize(shadow_offset), shadow_blur,
-                                shadow_color,
+  draw_looper_builder.AddShadow(shadow_offset, shadow_blur, shadow_color,
                                 DrawLooperBuilder::kShadowRespectsTransforms,
                                 DrawLooperBuilder::kShadowIgnoresAlpha);
   SetDrawLooper(draw_looper_builder.DetachDrawLooper());
+
+  Color fill_color(SkColorGetR(shadow_color), SkColorGetG(shadow_color),
+                   SkColorGetB(shadow_color));
+  FloatRect outer_rect = AreaCastingShadowInHole(rect.Rect(), shadow_blur,
+                                                 shadow_spread, shadow_offset);
   FillRectWithRoundedHole(outer_rect, rounded_hole, fill_color);
 }
 
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc b/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc
index 06cb2110..d230b83 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc
@@ -34,9 +34,9 @@
 }
 
 ThreadState::ThreadState(v8::CppHeap& cpp_heap)
-    : cpp_heap_(cpp_heap),
-      allocation_handle_(cpp_heap.GetAllocationHandle())
-          thread_id_(CurrentThread()) {}
+    : allocation_handle_(cpp_heap.GetAllocationHandle()),
+      cpp_heap_(cpp_heap),
+      thread_id_(CurrentThread()) {}
 
 ThreadState::~ThreadState() {
   DCHECK(!IsMainThread());
diff --git a/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h b/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h
index c759d89..3b3cb82 100644
--- a/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h
+++ b/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_THREAD_STATE_H_
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_THREAD_STATE_H_
 
+#include "base/compiler_specific.h"
 #include "base/lazy_instance.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/thread_specific.h"
@@ -68,6 +69,7 @@
   ALWAYS_INLINE cppgc::AllocationHandle& allocation_handle() const {
     return allocation_handle_;
   }
+  ALWAYS_INLINE v8::CppHeap& cpp_heap() const { return cpp_heap_; }
 
  private:
   // Main-thread ThreadState avoids TLS completely by using a regular global.
diff --git a/third_party/blink/renderer/platform/mojo/drag_mojom_traits.cc b/third_party/blink/renderer/platform/mojo/drag_mojom_traits.cc
index 8074424..3aa3c34 100644
--- a/third_party/blink/renderer/platform/mojo/drag_mojom_traits.cc
+++ b/third_party/blink/renderer/platform/mojo/drag_mojom_traits.cc
@@ -103,12 +103,12 @@
   item.filename_data = blink::FilePathToWebString(filename_data);
   item.display_name_data = blink::FilePathToWebString(display_name_data);
   mojo::PendingRemote<::blink::mojom::blink::FileSystemAccessDragDropToken>
-      native_file_system_token(
+      file_system_access_token(
           data.TakeFileSystemAccessToken<mojo::PendingRemote<
               ::blink::mojom::blink::FileSystemAccessDragDropToken>>());
-  item.native_file_system_entry =
-      base::MakeRefCounted<::blink::NativeFileSystemDropData>(
-          std::move(native_file_system_token));
+  item.file_system_access_entry =
+      base::MakeRefCounted<::blink::FileSystemAccessDropData>(
+          std::move(file_system_access_token));
 
   *out = std::move(item);
   return true;
@@ -187,7 +187,7 @@
     file_system_access_token(const blink::WebDragData::Item& item) {
   // Should never have to send a transfer token information from the renderer
   // to the browser.
-  DCHECK(!item.native_file_system_entry);
+  DCHECK(!item.file_system_access_entry);
   return mojo::NullRemote();
 }
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 9c35cb5..5b045a5 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -406,14 +406,6 @@
       status: "experimental",
     },
     {
-      name: "CookieStoreDocument",
-      status: "stable",
-    },
-    {
-      name: "CookieStoreWorker",
-      status: "stable",
-    },
-    {
       name: "CookiesWithoutSameSiteMustBeSecure",
       status: "test",
     },
@@ -622,6 +614,17 @@
       name: "CSSSnapSize",
       status: "experimental",
     },
+    {
+      // Support for CSS ::spelling-error, ::grammar-error, and the
+      // spelling-error and grammar-error values in text-decoration-line.
+      //
+      // https://drafts.csswg.org/css-pseudo-4/#selectordef-spelling-error
+      // https://drafts.csswg.org/css-pseudo-4/#selectordef-grammar-error
+      // https://drafts.csswg.org/css-text-decor-4/#valdef-text-decoration-line-spelling-error
+      // https://drafts.csswg.org/css-text-decor-4/#valdef-text-decoration-line-grammar-error
+      name: "CSSSpellingGrammarErrors",
+      status: "test",
+    },
     // Make system color keywords compute to themselves.
     // https://github.com/w3c/csswg-drafts/issues/3847
     {
@@ -869,7 +872,7 @@
       // Also enabled when blink::features::kFileHandlingAPI is overridden
       // on the command line (or via chrome://flags).
       name: "FileHandling",
-      depends_on: ["NativeFileSystem"],
+      depends_on: ["FileSystemAccess"],
       status: {"Android": "test", "default": "experimental"},
       origin_trial_feature_name: "FileHandling",
       origin_trial_os: ["win", "mac", "linux", "chromeos"],
@@ -879,6 +882,15 @@
       status: "stable",
     },
     {
+      // Also enabled when blink::features::kNativeFileSystemAPI is overridden
+      // on the command line (or via chrome://flags).
+      name: "FileSystemAccess",
+      status: {"Android": "test", "default": "stable"},
+      // TODO(mek): Remove origin trial integration.
+      origin_trial_feature_name: "NativeFileSystem2",
+      origin_trial_os: ["win", "mac", "linux", "chromeos"],
+    },
+    {
       // In-development features for the File System Access API.
       name: "FileSystemAccessAPIExperimental",
       status: "experimental",
@@ -1303,15 +1315,6 @@
       status: "stable",
     },
     {
-      // Also enabled when blink::features::kNativeFileSystemAPI is overridden
-      // on the command line (or via chrome://flags).
-      name: "NativeFileSystem",
-      status: {"Android": "test", "default": "stable"},
-      // TODO(mek): Remove origin trial integration.
-      origin_trial_feature_name: "NativeFileSystem2",
-      origin_trial_os: ["win", "mac", "linux", "chromeos"],
-    },
-    {
       name: "NativeIO",
       status: "experimental",
     },
@@ -2180,7 +2183,8 @@
     },
     {
       name: "WebHID",
-      status: {"Android": "", "default": "stable"},
+      origin_trial_feature_name: "WebHID",
+      status: {"Android": "", "default": "experimental"},
     },
     {
       name: "WebID",
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread.h b/third_party/blink/renderer/platform/scheduler/worker/worker_thread.h
index 2caf785..80502aa 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread.h
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread.h
@@ -15,6 +15,7 @@
 #include "third_party/blink/public/platform/web_private_ptr.h"
 #include "third_party/blink/renderer/platform/heap/gc_task_runner.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 class BlinkGCMemoryDumpProvider;
diff --git a/third_party/blink/renderer/platform/supplementable.h b/third_party/blink/renderer/platform/supplementable.h
index 7c309d3..1ff7c996 100644
--- a/third_party/blink/renderer/platform/supplementable.h
+++ b/third_party/blink/renderer/platform/supplementable.h
@@ -77,7 +77,7 @@
 //      public:
 //       static const char kSupplementName[];
 //
-//       NavigatorFoo& From(Navigator&);
+//       static NavigatorFoo& From(Navigator&);
 //     }
 //
 //     // static
diff --git a/third_party/blink/renderer/platform/weborigin/security_origin.h b/third_party/blink/renderer/platform/weborigin/security_origin.h
index c97a41b..9792167 100644
--- a/third_party/blink/renderer/platform/weborigin/security_origin.h
+++ b/third_party/blink/renderer/platform/weborigin/security_origin.h
@@ -230,7 +230,6 @@
   bool CanAccessCookies() const { return !IsOpaque(); }
   bool CanAccessPasswordManager() const { return !IsOpaque(); }
   bool CanAccessFileSystem() const { return !IsOpaque(); }
-  bool CanAccessNativeFileSystem() const { return !IsOpaque(); }
   bool CanAccessCacheStorage() const { return !IsOpaque(); }
   bool CanAccessLocks() const { return !IsOpaque(); }
   bool CanAccessSessionStorage() const { return !IsOpaque(); }
diff --git a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
index b0eb5b71..e8b3b486 100644
--- a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
+++ b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
@@ -34,6 +34,7 @@
 #include "base/allocator/partition_allocator/memory_reclaimer.h"
 #include "base/allocator/partition_allocator/oom.h"
 #include "base/allocator/partition_allocator/page_allocator.h"
+#include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/allocator/partition_allocator/partition_alloc_features.h"
 #include "base/debug/alias.h"
 #include "base/no_destructor.h"
@@ -48,9 +49,11 @@
 const char* const Partitions::kAllocatedObjectPoolName =
     "partition_alloc/allocated_objects";
 
+#if defined(PA_HAS_64_BITS_POINTERS) && !ENABLE_REF_COUNT_FOR_BACKUP_REF_PTR
 // Runs PCScan on WTF partitions.
 const base::Feature kPCScanBlinkPartitions{"PCScanBlinkPartitions",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
 
 bool Partitions::initialized_ = false;
 
@@ -103,7 +106,7 @@
   buffer_root_ = buffer_allocator->root();
   layout_root_ = layout_allocator->root();
 
-#if !ENABLE_REF_COUNT_FOR_BACKUP_REF_PTR
+#if defined(PA_HAS_64_BITS_POINTERS) && !ENABLE_REF_COUNT_FOR_BACKUP_REF_PTR
   if (base::features::IsPartitionAllocPCScanEnabled() ||
       base::FeatureList::IsEnabled(kPCScanBlinkPartitions)) {
 #if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 00d59cd4..31b83ef 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -389,7 +389,7 @@
             'layout_invalidation_reason::.+',
             'media_constraints_impl::.+',
             'media_element_parser_helpers::.+',
-            'native_file_system_error::.+',
+            'file_system_access_error::.+',
             'network_utils::.+',
             'origin_trials::.+',
             'paint_filter_builder::.+',
diff --git a/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh b/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
index 4f6fba2..1be9072 100755
--- a/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
+++ b/third_party/blink/tools/blinkpy/third_party/wpt/checkout.sh
@@ -27,7 +27,7 @@
   # xargs on some platforms, so we remove those directories first.
   rm -fr html css
   # Remove all except white-listed.
-  comm -23 <(find . -type f | sort) <(cat ../WPTIncludeList | sort) | xargs -d '\n' -n 1 rm
+  comm -23 <(find . -type f -o -type l | sort) <(cat ../WPTIncludeList | sort) | xargs -d '\n' -n 1 rm
   find . -empty -type d -delete
 }
 
diff --git a/third_party/blink/web_tests/FlagExpectations/composite-after-paint b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
index 5e395d7..8dfab63 100644
--- a/third_party/blink/web_tests/FlagExpectations/composite-after-paint
+++ b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
@@ -64,6 +64,9 @@
 crbug.com/1124979 compositing/video/video-controls-layer-creation.html [ Pass Failure ]
 
 crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-animation.html [ Crash ]
+crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation1.html [ Crash ]
+crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2.html [ Crash ]
+crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation3.html [ Crash ]
 crbug.com/1150468 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/animations/one-element-transition.html [ Crash ]
 
 crbug.com/1157199 external/wpt/css/css-paint-api/column-count-crash.https.html [ Crash ]
diff --git a/third_party/blink/web_tests/LeakExpectations b/third_party/blink/web_tests/LeakExpectations
index 0ef64b0..28cd6c2 100644
--- a/third_party/blink/web_tests/LeakExpectations
+++ b/third_party/blink/web_tests/LeakExpectations
@@ -178,12 +178,6 @@
 # Sheriff 2020-10-08
 crbug.com/1136690 [ Linux ] http/tests/inspector-protocol/service-worker/service-worker-fetch-async-stacks.js [ Pass Timeout ]
 
-# Sheriff 2021-01-06
-crbug.com/1163172 [ Linux ] external/wpt/css/selectors/focus-visible-003.html [ Pass Failure ]
-crbug.com/1163172 [ Linux ] external/wpt/css/selectors/focus-visible-004.html [ Pass Failure ]
-crbug.com/1163172 [ Linux ] external/wpt/css/selectors/focus-visible-015.html [ Pass Failure ]
-crbug.com/1163172 [ Linux ] external/wpt/css/selectors/focus-visible-016.html [ Pass Failure ]
-
 ###########################################################################
 # WARNING: Memory leaks must be fixed asap. Sheriff is expected to revert #
 # culprit CLs instead of suppressing the leaks. If you have any question, #
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 0865e97..0931a64 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -74,6 +74,7 @@
 crbug.com/1132260 http/tests/devtools/components/linkifier.js [ Pass Failure ]
 crbug.com/1132260 http/tests/devtools/console/console-bad-stacktrace.js [ Pass Failure ]
 crbug.com/1132260 http/tests/devtools/console/console-uncaught-promise.js [ Pass Failure ]
+crbug.com/1132260 http/tests/devtools/tracing/timeline-network/timeline-network-resource-details.js [ Pass Failure ]
 
 # With --enable-display-compositor-pixel-dump enabled by default, these three
 # tests fail. See crbug.com/887140 for more info.
@@ -2473,6 +2474,7 @@
 crbug.com/958381 [ Mac ] external/wpt/css/CSS2/tables/table-anonymous-objects-206.xht [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/table-element-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-025.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-026.html [ Failure ]
 crbug.com/626703 external/wpt/html/interaction/focus/document-level-focus-apis/document-has-system-focus.html [ Timeout ]
@@ -5935,3 +5937,9 @@
 crbug.com/1161301 [ Mac10.15 ] external/wpt/webxr/xrSession_requestReferenceSpace_features.https.html [ Pass Timeout ]
 crbug.com/1161352 [ Mac10.14 ] virtual/controls-refresh-hc/fast/forms/color-scheme/search/search-appearance-basic.html [ Pass Timeout ]
 crbug.com/1161352 [ Mac10.14 ] external/wpt/webxr/xrSession_requestReferenceSpace_features.https.html [ Pass Failure Timeout ]
+
+# Failing on Webkit Linux Leak only:
+crbug.com/1046784 http/tests/devtools/tracing/timeline-receive-response-event.js [ Pass Failure ]
+
+# Sheriff 2021-01-21
+crbug.com/1164166 [ Linux ] virtual/synchronous_html_parser/external/wpt/preload/avoid-prefetching-on-text-plain.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 0dd813e2..f43a58c 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -102639,6 +102639,19 @@
         {}
        ]
       ],
+      "table-element-001.html": [
+       "fd2dcd81dd90c6d82a3652750cea2370979aff7a",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "zero-or-infinity-001.html": [
        "760635c40e8fde8f3514761c77e83385592861b0",
        [
@@ -176808,270 +176821,6 @@
         "b6c297cc77ddef6c4b0eae5c94a07d0827de89c7",
         []
        ],
-       "name0001-expected": [
-        "96527926e107477e66ab0386dea671d8973de695",
-        []
-       ],
-       "name0001-test": [
-        "3ce5f5f9824b95711aa93a82c45e39250aa225ed",
-        []
-       ],
-       "name0002-expected": [
-        "d4d3cda811bd744d741d883675d789e124c32449",
-        []
-       ],
-       "name0002-test": [
-        "d6eac8a757af54e177a04bf7d3b15e14080cdc5a",
-        []
-       ],
-       "name0003-expected": [
-        "0c00f45c4a479545f1272b6c7d3312f7b48d658a",
-        []
-       ],
-       "name0003-test": [
-        "7ea96155bd7752a8abffb7a85093fdd3fa19fc93",
-        []
-       ],
-       "name0004-expected": [
-        "b079529aa249748cdf1ada8ce4b99d12952745a7",
-        []
-       ],
-       "name0004-test": [
-        "99f0e61801a92ddc2034fec94fc61b02f1b2baf1",
-        []
-       ],
-       "name0005-expected": [
-        "a0f031bb34ed2284c99cab25c4864f7e55af6522",
-        []
-       ],
-       "name0005-test": [
-        "9e33e0c8e476c24b698b7c8061ea42a585b9ed73",
-        []
-       ],
-       "name0006-expected": [
-        "ee0e7d7c335d08c78793167fd581458b15e6a3ec",
-        []
-       ],
-       "name0006-test": [
-        "fbd03632c61b3df902d75dd8f11dcde5a7cf8634",
-        []
-       ],
-       "name0007-expected": [
-        "6d6e56ebe59b54fc030d7c88c72a142d1e5f7486",
-        []
-       ],
-       "name0007-test": [
-        "d41e64b87ec3d1e45cf0a9020c2a093063f8844f",
-        []
-       ],
-       "name0008-expected": [
-        "a4b8c2428258033c534e7ff05ac967dbe24ed9e9",
-        []
-       ],
-       "name0008-test": [
-        "7afcf700376b02f3a38c12752b0067cc66f3e190",
-        []
-       ],
-       "name0009-expected": [
-        "49506acc24893411e24dd9c63ed25f2b5945052d",
-        []
-       ],
-       "name0009-test": [
-        "f40d2c4d6b9e06be56505acb73594718724e5254",
-        []
-       ],
-       "name0010-expected": [
-        "1e72557892b01144f0cd3051a9a079fcc2253e46",
-        []
-       ],
-       "name0010-test": [
-        "fb4fd921be31471edd70aa0399c011aea48ee36d",
-        []
-       ],
-       "name0011-expected": [
-        "260d7028a0195e1c93da4cb10cac211016856e60",
-        []
-       ],
-       "name0011-test": [
-        "b36b72388f2b3dc6480dd798f57d8ed5624b5ae4",
-        []
-       ],
-       "name0012-expected": [
-        "0a2686a15257f03f02af93c39d410cc070e408fd",
-        []
-       ],
-       "name0012-test": [
-        "6927aac6fa061ad0e0be4f13f4d7c686602aef25",
-        []
-       ],
-       "name0013-expected": [
-        "87dec78b62265dd3dfd115e68ce526aef5213353",
-        []
-       ],
-       "name0013-test": [
-        "59ada98321c69945673b94c2504f81408224325b",
-        []
-       ],
-       "name0014-expected": [
-        "82bfe0e60acd0c9194bbbb5c33a0a4e95686d5fa",
-        []
-       ],
-       "name0014-test": [
-        "a113e99be5e27dd6186d247d5f1fe0f197f1349f",
-        []
-       ],
-       "name0015-expected": [
-        "390b77b30c40d33180bbe2026b052dadd44af527",
-        []
-       ],
-       "name0015-test": [
-        "60fc0746ba258f0b2ea7d20e32625d70f1ea481b",
-        []
-       ],
-       "name0016-expected": [
-        "7d4d9e330792bf536a304785940f865d9cd20aa3",
-        []
-       ],
-       "name0016-test": [
-        "371dbcde6cab2545f54b15d3f797936d39fae3d3",
-        []
-       ],
-       "name0017-expected": [
-        "96527926e107477e66ab0386dea671d8973de695",
-        []
-       ],
-       "name0017-test": [
-        "056143166bd1c31df501c74d13425656a744884c",
-        []
-       ],
-       "name0018-expected": [
-        "96527926e107477e66ab0386dea671d8973de695",
-        []
-       ],
-       "name0018-test": [
-        "e86a48360cf5735c3b0389d0f2d2507ec6c44b43",
-        []
-       ],
-       "name0019-expected": [
-        "8d0bc2d07d699558f0dc68690af76bd9b7f4e430",
-        []
-       ],
-       "name0019-test": [
-        "d48e3f6954a690c35628e3665fa329a67fb76452",
-        []
-       ],
-       "name0020-expected": [
-        "aa9cd6d435cfdb9bd2180bd163e1f1503e6deca0",
-        []
-       ],
-       "name0020-test": [
-        "b84f64d17981f59c5b1e557ba33362e38594e8b8",
-        []
-       ],
-       "name0021-expected": [
-        "206ff76e3e324b0c652024962dce34dd7e4dcf93",
-        []
-       ],
-       "name0021-test": [
-        "56b319e9d491dc99a1434b157803249fe7b4acbb",
-        []
-       ],
-       "name0022-expected": [
-        "b14d4f69b58c3d9cc7ed1f363d42d625e7bdcf8c",
-        []
-       ],
-       "name0022-test": [
-        "cc59ff17edabb8466b416dda9b4ed3c2f42a0b80",
-        []
-       ],
-       "name0023-expected": [
-        "5ab27668fdbdd021d29abbda3acd08e94aa92b0b",
-        []
-       ],
-       "name0023-test": [
-        "b7f9cc257d20fbc98b5c12125ee9dde8e4ae9436",
-        []
-       ],
-       "name0024-expected": [
-        "5ac4f2535c81810f380414f3e72ac8d35bfa76b7",
-        []
-       ],
-       "name0024-test": [
-        "da7b696da9129d5762468c0ba2b98d30974664ee",
-        []
-       ],
-       "name0025-expected": [
-        "68c38595db832182a19bf62086972fe2fd1a9295",
-        []
-       ],
-       "name0025-test": [
-        "708f006b9c0777592199ec0e8b333f44894e90fb",
-        []
-       ],
-       "name0026-expected": [
-        "b14d4f69b58c3d9cc7ed1f363d42d625e7bdcf8c",
-        []
-       ],
-       "name0026-test": [
-        "bbeb77a029c18442c90d81cd96a3b1eb1aa72170",
-        []
-       ],
-       "name0027-expected": [
-        "b14d4f69b58c3d9cc7ed1f363d42d625e7bdcf8c",
-        []
-       ],
-       "name0027-test": [
-        "d222227e6de7ded31c0be0e212f4611035c7ba79",
-        []
-       ],
-       "name0028-expected": [
-        "f14f9930f6b6b11391196c12ceeb6d98723ed2fb",
-        []
-       ],
-       "name0028-test": [
-        "1c197e3324c87aa5364224ecaf029dbf5ae0a7a4",
-        []
-       ],
-       "name0029-expected": [
-        "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-        []
-       ],
-       "name0029-test": [
-        "44212468d449cf18fac8987c1f0ba4127e1483ee",
-        []
-       ],
-       "name0030-expected": [
-        "a391380fb666180af1950103f50f584207c75138",
-        []
-       ],
-       "name0030-test": [
-        "cf3ff160f451ac4f53a847942df1b7a3f29861eb",
-        []
-       ],
-       "name0031-expected": [
-        "0cd5a50dd1b4eeda99b236c2716f656c7d1e83a7",
-        []
-       ],
-       "name0031-test": [
-        "9394184904027da428e718414c5ce19d0c17a6c1",
-        []
-       ],
-       "name0032-expected": [
-        "f4f7f3ff4cd4cf9e5c2528f0c082f4486b84c3b6",
-        []
-       ],
-       "name0032-test": [
-        "93fc9752333920c9b70236d7e53dd82199404811",
-        []
-       ],
-       "name0033-expected": [
-        "6e7762e35e375552eecb099f8b05ffa508825dc4",
-        []
-       ],
-       "name0033-test": [
-        "7bbdd89566a0a01d97df5d16b34a9822abeed6e9",
-        []
-       ],
        "optional-domain0030-expected": [
         "b14d4f69b58c3d9cc7ed1f363d42d625e7bdcf8c",
         []
@@ -177111,54 +176860,6 @@
        "ordering0001-test": [
         "ba6e85c44cdea2aa269ef170fe2d3a4c3a14e1ce",
         []
-       ],
-       "value0001-expected": [
-        "b14d4f69b58c3d9cc7ed1f363d42d625e7bdcf8c",
-        []
-       ],
-       "value0001-test": [
-        "38b7dd296e613e2d659a7516f56ea6e00e80a507",
-        []
-       ],
-       "value0002-expected": [
-        "9e96a81e3b4f787a158cd20a10e9defee7446948",
-        []
-       ],
-       "value0002-test": [
-        "bed691f1809cf37eb44c8a1083e9544bf333c03a",
-        []
-       ],
-       "value0003-expected": [
-        "5cc2d467ef724279da03a2af49535aa58382ee40",
-        []
-       ],
-       "value0003-test": [
-        "ce1d455201dbef9b08ff18315d2cceef569d0346",
-        []
-       ],
-       "value0004-expected": [
-        "400030f6f1d38f1833f1d8c960941f842b12a9e8",
-        []
-       ],
-       "value0004-test": [
-        "c569216520ca270f46741ec3ad0acd20bdd77887",
-        []
-       ],
-       "value0005-expected": [
-        "cad285f70867f41fdb4e4439d389b1f8171ee6ce",
-        []
-       ],
-       "value0005-test": [
-        "514c0f125f1f4d9d116d5ccfef3a44998b340869",
-        []
-       ],
-       "value0006-expected": [
-        "b14d4f69b58c3d9cc7ed1f363d42d625e7bdcf8c",
-        []
-       ],
-       "value0006-test": [
-        "a939a8229149c1d2311379d1632b47dd184affd8",
-        []
        ]
       }
      }
@@ -213464,7 +213165,7 @@
      ]
     },
     "requirements.txt": [
-     "725ddd9e85ee202cf7fc9d3072a770a6351cdb71",
+     "28b20408eb011fbd2fad6665ed36b4bb8a4f5685",
      []
     ],
     "selectors": {
@@ -259816,7 +259517,7 @@
      []
     ],
     "RTCRtpTransceiver-stop-expected.txt": [
-     "596e3c4a758c5b8e097b2636d39d59c64927781a",
+     "85ff9f1cffcadd652996a92e672c8e618349f2a6",
      []
     ],
     "RTCRtpTransceiver.https-expected.txt": [
@@ -288711,28 +288412,12 @@
        {}
       ]
      ],
-     "name-tests.html": [
-      "e962539b0fbdcde83859496cc2b815194c7596a0",
-      [
-       null,
-       {
-        "timeout": "long"
-       }
-      ]
-     ],
      "ordering-tests.html": [
       "4dce985ac71a0d48538dc7376dff7102a0fcd78f",
       [
        null,
        {}
       ]
-     ],
-     "value-tests.html": [
-      "0d8daae165cbd2c33a4051048047ea459ad97b3c",
-      [
-       null,
-       {}
-      ]
      ]
     },
     "meta-blocked.html": [
@@ -288744,7 +288429,7 @@
     ],
     "name": {
      "name.html": [
-      "18fd87659442f95157d233a2e99b2704c8ea8f03",
+      "39fdc75d5992ad04c35f1f19c4613e2f35762fa2",
       [
        null,
        {
@@ -289159,7 +288844,7 @@
     },
     "value": {
      "value.html": [
-      "c473a6bf7bd256586d74f2a6515cd096b93974e1",
+      "c1910839aa570d1de0a6eb2ee5b0d889d75224ff",
       [
        null,
        {
@@ -315434,7 +315119,7 @@
       ]
      ],
      "focus-visible-003.html": [
-      "aa73b4da44f0e831dbbd5516bd71daef245e7e65",
+      "3076823f703c5fe4a5822e9d2271034f94ebc829",
       [
        null,
        {
@@ -315443,7 +315128,7 @@
       ]
      ],
      "focus-visible-004.html": [
-      "b30b9e2938ab4ed4e3972340953649c21e0085d9",
+      "f1274b61ee9cec65a517a877b3fbcf967632ce04",
       [
        null,
        {
@@ -315527,7 +315212,7 @@
       ]
      ],
      "focus-visible-015.html": [
-      "685baeb7bad07ad45a8efda163dba5db94b9ffe8",
+      "98e44005c7604a764769472acac203e00c16023c",
       [
        null,
        {
@@ -315536,7 +315221,7 @@
       ]
      ],
      "focus-visible-016.html": [
-      "a65e5a5b3dd6aa2be7aa733d01725242e8b01cc4",
+      "080787fb1ee9f4ad96d8dacba051601421435932",
       [
        null,
        {
@@ -437597,7 +437282,7 @@
      ]
     ],
     "video-decoder.any.js": [
-     "2dd85ad955b7321167b9809007d1badea5780555",
+     "33ea2dbe375d5446e68e90405f9867ce8f7fb272",
      [
       "webcodecs/video-decoder.any.html",
       {
@@ -439457,7 +439142,7 @@
      ]
     ],
     "RTCRtpTransceiver-stop.html": [
-     "4f3a9ce85fa244bc673447fa389f44bdc381b230",
+     "60f6d0c13357b56a6e24ce4d5f29067e2683f492",
      [
       null,
       {}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation1.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation1.html
new file mode 100644
index 0000000..380c5d3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation1.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#background-color">
+<link rel="match" href="one-element-animation-ref.html">
+<style>
+.container {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+  /* Use a long animation that start at 5% progress where the slope of the
+     selected timing function is zero. By setting up the animation in this way,
+     we accommodate lengthy delays in running the test without a potential drift
+     in the animated property value. This is important for avoiding flakes,
+     especially on debug builds. The screenshots are taken as soon as the
+     animation is ready, thus the long animation duration has no bearing on
+     the actual duration of the test. */
+  animation: bgcolor 1000000s cubic-bezier(0,1,1,0) -50000s;
+}
+@keyframes bgcolor {
+  0% { background-color: rgb(0, 200, 0); }
+  10% {
+    background-color: rgb(200, 0, 0);
+    animation-timing-function: cubic-bezier(0,1,1,0);
+  }
+  100% {
+    background-color: rgb(0, 0, 200);
+    animation-timing-function: cubic-bezier(0,1,1,0);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<body>
+<div class="container"></div>
+
+<script>
+// This test and the "one-element-three-keyframes-animation2.html" ensure that
+// we select the correct start and end keyframes for interpolation. In this
+// test, the start delay of the animation makes it jump to 5% right away, and in
+// the "one-element-three-keyframes-animation2.html" the start delay makes it
+// jump to 50%. So for this test, we would choose the keyframes at 0% and 10%
+// for interpolation, where for the other test it would be 10% and 100%.
+document.getAnimations()[0].ready.then(() => {
+  takeScreenshot();
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2-ref.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2-ref.html
new file mode 100644
index 0000000..823d8ac
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<body>
+  <canvas id="canvas" width="100" height="100"></canvas>
+</body>
+<script>
+  var canvas = document.getElementById('canvas');
+  var ctx = canvas.getContext('2d');
+  ctx.fillStyle = 'rgb(100, 0, 100)';
+  ctx.fillRect(0, 0, 100, 100);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2.html
new file mode 100644
index 0000000..e71b858
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation2.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#background-color">
+<link rel="match" href="one-element-three-keyframes-animation2-ref.html">
+<style>
+.container {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+  /* Use a long animation that start at 50% progress where the slope of the
+     selected timing function is zero. By setting up the animation in this way,
+     we accommodate lengthy delays in running the test without a potential drift
+     in the animated property value. This is important for avoiding flakes,
+     especially on debug builds. The screenshots are taken as soon as the
+     animation is ready, thus the long animation duration has no bearing on
+     the actual duration of the test. */
+  animation: bgcolor 1000000s cubic-bezier(0,1,1,0) -500000s;
+}
+@keyframes bgcolor {
+  0% { background-color: rgb(0, 200, 0); }
+  10% {
+    background-color: rgb(200, 0, 0);
+    animation-timing-function: cubic-bezier(0,1,1,0);
+  }
+  100% {
+    background-color: rgb(0, 0, 200);
+    animation-timing-function: cubic-bezier(0,1,1,0);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<body>
+<div class="container"></div>
+
+<script>
+// The start delay of the animation makes it jump 50% of the animation, which
+// means we would select the keyframes at 10% and 100% for animation. The
+// progress would be (0.5-0.1) / (1-0.1) = 0.44. So a timing function input of
+// 0.44 results in an output of 0.5.
+document.getAnimations()[0].ready.then(() => {
+  takeScreenshot();
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation3.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation3.html
new file mode 100644
index 0000000..080ed01
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/one-element-three-keyframes-animation3.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#background-color">
+<link rel="match" href="one-element-animation-ref.html">
+<style>
+.container {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+  /* Use a long animation that start at 5% progress where the slope of the
+     selected timing function is zero. By setting up the animation in this way,
+     we accommodate lengthy delays in running the test without a potential drift
+     in the animated property value. This is important for avoiding flakes,
+     especially on debug builds. The screenshots are taken as soon as the
+     animation is ready, thus the long animation duration has no bearing on
+     the actual duration of the test. */
+  animation: bgcolor 1000000s cubic-bezier(0,1,1,0) -50000s;
+}
+@keyframes bgcolor {
+  10% {
+    background-color: rgb(200, 0, 0);
+    animation-timing-function: cubic-bezier(0,1,1,0);
+  }
+  0% { background-color: rgb(0, 200, 0); }
+  100% {
+    background-color: rgb(0, 0, 200);
+    animation-timing-function: cubic-bezier(0,1,1,0);
+  }
+}
+</style>
+<script src="/common/reftest-wait.js"></script>
+<body>
+<div class="container"></div>
+
+<script>
+document.getAnimations()[0].ready.then(() => {
+  takeScreenshot();
+});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-004-ref.html
new file mode 100644
index 0000000..3cc79ed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-004-ref.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies overflow-clip-margin extends outside bounds with contain: paint</title>
+<link rel="help" href="https://www.w3.org/TR/css-overflow-3/#propdef-overflow-clip-margin">
+<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
+<style>
+  .container {
+      display: flex;
+  }
+  .parent {
+      position: relative;
+      top: -10px;
+      left: -10px;
+      width: 120px;
+      height: 120px;
+      flex: none;
+      background-color: green;
+  }
+  .spacer {
+      flex: none;
+      height: 100px;
+      width: 100px;
+  }
+
+</style>
+<p>You should see two green squares touching each other. The one on the
+  right should be slightly larger.</p>
+<div class="spacer"></div>
+<div class="container">
+  <div class="spacer" style="background-color: green"></div>
+  <div class="parent"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-004.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-004.html
new file mode 100644
index 0000000..aab356f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-004.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies overflow-clip-margin extends outside bounds with contain: paint</title>
+<link rel="help" href="https://www.w3.org/TR/css-overflow-3/#propdef-overflow-clip-margin">
+<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
+<link rel="match" href="overflow-clip-margin-004-ref.html">
+<style>
+  .container {
+      display: flex;
+  }
+  .parent {
+      width: 100px;
+      height: 100px;
+      flex: none;
+      contain: paint;
+      overflow-clip-margin: 10px;
+  }
+  .child {
+      width: 200px;
+      height: 200px;
+      position: relative;
+      top: -50px;
+      left: -50px;
+      background-color: green;
+  }
+  .spacer {
+      flex: none;
+      height: 100px;
+      width: 100px;
+  }
+
+</style>
+<p>You should see two green squares touching each other. The one on the
+  right should be slightly larger.</p>
+<div class="spacer"></div>
+<div class="container">
+  <div class="spacer" style="width: 90px; background-color: green"></div>
+  <div class="spacer" style="width: 10px"></div>
+  <div class="parent">
+    <div class="child"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-005-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-005-ref.html
new file mode 100644
index 0000000..84110e57
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-005-ref.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies overflow-clip-margin impacts layout</title>
+<link rel="help" href="https://www.w3.org/TR/css-overflow-3/#propdef-overflow-clip-margin">
+<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
+<style>
+  .scroller {
+      overflow: auto;
+      width: 100px;
+      height: 100px;
+  }
+  .child {
+      position: relative;
+      width: 110px;
+      height: 110px;
+      background-color: green;
+  }
+</style>
+<p>You should see a green box with scrollbars.</p>
+<div class="scroller">
+  <div class="child"></div>
+</div>
+
+<p>You should see a green box with scrollbars.</p>
+<div class="scroller">
+  <div class="child" style="width: 150px; height: 150px"></div>
+</div>
+
+<p>You should see a green box with no scrollbars.</p>
+<div class="scroller" style="background-color: green">
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-005.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-005.html
new file mode 100644
index 0000000..2d03a96
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-005.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies overflow-clip-margin impacts layout</title>
+<link rel="help" href="https://www.w3.org/TR/css-overflow-3/#propdef-overflow-clip-margin">
+<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
+<link rel="match" href="overflow-clip-margin-005-ref.html">
+<style>
+  .scroller {
+      overflow: auto;
+      width: 100px;
+      height: 100px;
+  }
+  .parent {
+      width: 100px;
+      height: 100px;
+      contain: paint;
+      overflow-clip-margin: 10px;
+  }
+  .child {
+      width: 200px;
+      height: 200px;
+      position: relative;
+      top: -50px;
+      left: -50px;
+      background-color: green;
+  }
+</style>
+<p>You should see a green box with scrollbars.</p>
+<div class="scroller">
+  <div class="parent">
+    <div class="child"></div>
+  </div>
+</div>
+
+<p>You should see a green box with scrollbars.</p>
+<div class="scroller">
+  <div class="parent" style="overflow-clip-margin: 100px">
+    <div class="child"></div>
+  </div>
+</div>
+
+<p>You should see a green box with no scrollbars.</p>
+<div class="scroller">
+  <div class="parent" style="background-color: green">
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-006-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-006-ref.html
new file mode 100644
index 0000000..cf6b55a2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-006-ref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies overflow-clip-margin doesn't impact paint effects</title>
+<link rel="help" href="https://www.w3.org/TR/css-overflow-3/#propdef-overflow-clip-margin">
+<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
+<style>
+  .parent {
+      width: 100px;
+      height: 100px;
+      background-color: green;
+      box-shadow: 20px 20px 5px red;
+  }
+</style>
+<p>You should see a green box with a red box shadow.
+<div class="parent"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-006.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-006.html
new file mode 100644
index 0000000..493bcee
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/overflow-clip-margin-006.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Verifies overflow-clip-margin doesn't impact paint effects</title>
+<link rel="help" href="https://www.w3.org/TR/css-overflow-3/#propdef-overflow-clip-margin">
+<link rel="author" title="Scott Violet" href="mailto:sky@chromium.org">
+<link rel="match" href="overflow-clip-margin-006-ref.html">
+<style>
+  .parent {
+      width: 100px;
+      height: 100px;
+      background-color: green;
+      contain: paint;
+      overflow-clip-margin: 1px;
+      box-shadow: 20px 20px 5px red;
+  }
+</style>
+<p>You should see a green box with a red box shadow.
+<div class="parent"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-pseudos-computed.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-pseudos-computed.html
new file mode 100644
index 0000000..ec3532db
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/highlight-pseudos-computed.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Pseudo-Elements Test: highlight selectors getComputedStyle</title>
+<link rel="help" href="https://drafts.csswg.org/css-pseudo/#highlight-selectors">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  #target::selection,
+  #target::target-text,
+  #target::spelling-error,
+  #target::grammar-error {
+    background-color: green;
+    color: lime;
+  }
+</style>
+<div id="target"></div>
+<script>
+  for (const pseudo of ["::selection", "::target-text", "::spelling-error", "::grammar-error"]) {
+    test(() => {
+      let style = getComputedStyle(target, pseudo);
+      assert_equals(style.backgroundColor, "rgb(0, 128, 0)", "Background color is green.");
+      assert_equals(style.color, "rgb(0, 255, 0)", "Color is lime.");
+    }, `getComputedStyle() for ${pseudo}`);
+  }
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/highlight-pseudos.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/highlight-pseudos.html
new file mode 100644
index 0000000..aa3c4e20
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/highlight-pseudos.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Pseudo-Elements Test: highlight selectors parsing</title>
+<link rel="help" href="https://drafts.csswg.org/css-pseudo/#highlight-selectors">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+  for (const pseudo of ["::selection", "::target-text", "::spelling-error", "::grammar-error"]) {
+    test_valid_selector(`${pseudo}`);
+    test_valid_selector(`.a${pseudo}`);
+    test_valid_selector(`div ${pseudo}`);
+    test_valid_selector(`::part(my-part)${pseudo}`);
+
+    test_invalid_selector(`::before${pseudo}`);
+    test_invalid_selector(`${pseudo}.a`);
+    test_invalid_selector(`${pseudo} div`);
+    test_invalid_selector(`${pseudo}::after`);
+    test_invalid_selector(`${pseudo}:hover`);
+    test_invalid_selector(`:not(${pseudo})`);
+  }
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/target-text.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/target-text.html
deleted file mode 100644
index 37cbdd4..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/parsing/target-text.html
+++ /dev/null
@@ -1,20 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSS Pseudo-Elements Test: ::target-text parsing</title>
-<link rel="help" href="https://drafts.csswg.org/css-pseudo/#selectordef-target-text">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/css/support/parsing-testcommon.js"></script>
-<script>
-  test_valid_selector("::target-text");
-  test_valid_selector(".a::target-text");
-  test_valid_selector("div ::target-text");
-  test_valid_selector("::part(my-part)::target-text");
-
-  test_invalid_selector("::before::target-text");
-  test_invalid_selector("::target-text.a");
-  test_invalid_selector("::target-text div");
-  test_invalid_selector("::target-text::after");
-  test_invalid_selector("::target-text:hover");
-  test_invalid_selector(":not(::target-text)");
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-computed.html b/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-computed.html
deleted file mode 100644
index 42990305..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-pseudo/target-text-computed.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!doctype html>
-<meta charset="utf-8">
-<title>CSS Pseudo-Elements Test: ::target-text getComputedStyle</title>
-<link rel="help" href="https://drafts.csswg.org/css-pseudo/#selectordef-target-text">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<style>
-  #target::target-text {
-    background-color: green;
-  }
-  #target::target-text {
-    color: lime;
-  }
-</style>
-<div id="target"></div>
-<script>
-  test(() => {
-    let style = getComputedStyle(target, "::target-text");
-    assert_equals(style.backgroundColor, "rgb(0, 128, 0)", "Background color is green.");
-    assert_equals(style.color, "rgb(0, 255, 0)", "Color is lime.");
-  }, "getComputedStyle() for ::target-text");
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/table-element-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/table-element-001.html
new file mode 100644
index 0000000..fd2dcd8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/aspect-ratio/table-element-001.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>CSS aspect-ratio: aspect-ratio shouldn't apply to internal table boxes</title>
+<link rel="author" title="mozilla" href="https://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-sizing-4/#aspect-ratio">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht" />
+<style>
+table {
+  border-collapse: collapse;
+}
+th, td {
+  padding: 0px;
+}
+</style>
+
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+
+<!-- aspect-ratio shouldn't apply to internal table boxes -->
+<table>
+  <tr>
+    <th style='background: green; width: 100px; aspect-ratio: 1/1;'></th>
+    <td style='background: red; height: 50px; aspect-ratio: 4/1;'></td>
+  </tr>
+</table>
+<!-- aspect-ratio should apply to the table element -->
+<table style='background: green; width: 100px; aspect-ratio: 2/1;'></table>
diff --git a/third_party/blink/web_tests/external/wpt/css/requirements.txt b/third_party/blink/web_tests/external/wpt/css/requirements.txt
index 725ddd9..28b2040 100644
--- a/third_party/blink/web_tests/external/wpt/css/requirements.txt
+++ b/third_party/blink/web_tests/external/wpt/css/requirements.txt
@@ -1,6 +1,6 @@
 Template-Python==0.1.post1
 html5lib==1.1
-lxml==4.5.2
+lxml==4.6.2
 mercurial==4.5
 six==1.15.0
 webencodings==0.5.1
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-003.html b/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-003.html
index aa73b4d..3076823 100644
--- a/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-003.html
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-003.html
@@ -78,16 +78,18 @@
     <label><input class="check" id="el-13" type="color"></input> Focus me.</label>
   </div>
   <script>
-    function mouseClickInTarget(selector) {
-       let target = document.querySelector(selector);
-       return test_driver.click(target);
-    }
+    setup({ explicit_done: true });
 
-    for (const target of document.querySelectorAll(".check")) {
+    const elements = document.querySelectorAll(".check");
+    for (let i = 0; i < elements.length; i++) {
+      const target = elements[i];
       promise_test(() => {
         return new Promise(resolve => {
           target.addEventListener("focus", resolve);
-          test_driver.click(target);
+          test_driver.click(target).then(() => {
+            if (i == (elements.length - 1))
+              done();
+          });
         }).then(() => {
           assert_equals(getComputedStyle(target).backgroundColor, "rgb(0, 255, 0)", `backgroundColor for ${target.tagName}#${target.id} should be lime`);
           assert_not_equals(getComputedStyle(target).outlineColor, "rgb(255, 0, 0)", `outlineColor for ${target.tagName}#${target.id} should NOT be red`);
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-004.html b/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-004.html
index b30b9e2..f1274b61 100644
--- a/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-004.html
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-004.html
@@ -77,16 +77,18 @@
     <label><input class="check" id="el-11" type="color"></input> Focus me.</label>
   </div>
   <script>
-    function mouseClickInTarget(selector) {
-       let target = document.querySelector(selector);
-       return test_driver.click(target);
-    }
+    setup({ explicit_done: true });
 
-    for (const target of document.querySelectorAll(".check")) {
+    const elements = document.querySelectorAll(".check");
+    for (let i = 0; i < elements.length; i++) {
+      const target = elements[i];
       promise_test(() => {
         return new Promise(resolve => {
           target.addEventListener("focus", resolve);
-          test_driver.click(target);
+          test_driver.click(target).then(() => {
+            if (i == (elements.length - 1))
+              done();
+          });
         }).then(() => {
           assert_equals(getComputedStyle(target).backgroundColor, "rgb(0, 255, 0)", `backgroundColor for ${target.tagName}#${target.id} should be lime`);
           assert_not_equals(getComputedStyle(target).outlineColor, "rgb(255, 0, 0)", `outlineColor for ${target.tagName}#${target.id} should NOT be red`);
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-015.html b/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-015.html
index 685baeb..98e4400 100644
--- a/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-015.html
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-015.html
@@ -29,6 +29,8 @@
 <div id="target" tabindex="0">Target</div>
 
 <script>
+  setup({ explicit_done: true });
+
   async_test(function(t) {
     initial.addEventListener("focus", t.step_func(function() {
       assert_equals(getComputedStyle(initial).backgroundColor, "rgb(0, 255, 0)", `backgroundColor for ${initial.tagName}#${initial.id} should be lime`);
@@ -40,6 +42,6 @@
       t.done();
     }));
 
-    test_driver.click(initial);
+    test_driver.click(initial).then(() => done());
   }, ":focus-visible does not match after script focus move");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-016.html b/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-016.html
index a65e5a5..080787f 100644
--- a/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-016.html
+++ b/third_party/blink/web_tests/external/wpt/css/selectors/focus-visible-016.html
@@ -37,6 +37,8 @@
 <input id="target" />
 
 <script>
+  setup({ explicit_done: true });
+
   async_test(function(t) {
     initial.addEventListener("focus", t.step_func(function() {
       assert_equals(getComputedStyle(initial).backgroundColor, "rgb(0, 255, 0)", `backgroundColor for ${initial.tagName}#${initial.id} should be lime`);
@@ -48,7 +50,7 @@
       t.done();
     }));
 
-    test_driver.click(initial);
+    test_driver.click(initial).then(() => done());
   }, ":focus-visible always match on text inputs");
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_coordinates_when_locked.html.ini b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_coordinates_when_locked.html.ini
deleted file mode 100644
index 04c0f72..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_coordinates_when_locked.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pointerevent_coordinates_when_locked.html]
-  expected: TIMEOUT
-
-  [mouse Test pointerevent coordinates when pointer is locked]
-    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_getCoalescedEvents_when_pointerlocked.html.ini b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_getCoalescedEvents_when_pointerlocked.html.ini
deleted file mode 100644
index 9e12583..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_getCoalescedEvents_when_pointerlocked.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[pointerevent_getCoalescedEvents_when_pointerlocked.html]
-  [mouse pointermove getCoalescedEvents when lock test]
-    expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html.ini b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html.ini
deleted file mode 100644
index d22487b..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[pointerevent_movementxy_with_pointerlock.html]
-  expected: TIMEOUT
-
-  [mouse pointerevent movementX/Y with pointerlock test]
-    expected: TIMEOUT
-    message: Test timed out
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html.ini b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html.ini
deleted file mode 100644
index dc1a38a9..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[pointerevent_pointerlock_after_pointercapture.html]
-  [mouse no pointercapture while pointerlock]
-    expected: FAIL
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html.ini b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html.ini
deleted file mode 100644
index 7c4043d..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_in_pointerlock.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[pointerevent_pointermove_in_pointerlock.html]
-  expected: TIMEOUT
-
-  [pointermove event received]
-    expected: NOTRUN
-
-  [pointermove event received inner frame]
-    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_on_chorded_mouse_button_when_locked.html.ini b/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_on_chorded_mouse_button_when_locked.html.ini
deleted file mode 100644
index 9d0b178..0000000
--- a/third_party/blink/web_tests/external/wpt/pointerevents/pointerlock/pointerevent_pointermove_on_chorded_mouse_button_when_locked.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[pointerevent_pointermove_on_chorded_mouse_button_when_locked.html]
-  expected: TIMEOUT
-
-  [pointer locked pointermove events received for button state changes]
-    expected: NOTRUN
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.any.js b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.any.js
index 2dd85ad..33ea2db 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.any.js
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.any.js
@@ -4,7 +4,7 @@
 // TODO(sandersd): Move metadata into a helper library.
 // TODO(sandersd): Add H.264 decode test once there is an API to query for
 // supported codecs.
-let h264 = {
+const h264 = {
   async buffer() { return (await fetch('h264.mp4')).arrayBuffer(); },
   codec: "avc1.64000c",
   description: {offset: 7229, size: 46},
@@ -20,7 +20,7 @@
            {offset: 6429, size: 281}]
 };
 
-let vp9 = {
+const vp9 = {
   async buffer() { return (await fetch('vp9.mp4')).arrayBuffer(); },
   // TODO(sandersd): Verify that the file is actually level 1.
   codec: "vp09.00.10.08",
@@ -36,6 +36,88 @@
            {offset: 5193, size: 159}]
 };
 
+const badCodecsList = [
+    '',                         // Empty codec
+    'bogus',                    // Non exsitent codec
+    'vorbis',                   // Audio codec
+    'vp9',                      // Ambiguous codec
+    'video/webm; codecs="vp9"'  // Codec with mime type
+  ]
+
+const invalidConfigs = [
+  {
+    comment: 'Emtpy codec',
+    config: {codec: ''},
+  },
+  {
+    comment: 'Unrecognized codec',
+    config: {codec: 'bogus'},
+  },
+  {
+    comment: 'Audio codec',
+    config: {codec: 'vorbis'},
+  },
+  {
+    comment: 'Ambiguous codec',
+    config: {codec: 'vp9'},
+  },
+  {
+    comment: 'Codec with MIME type',
+    config: {codec: 'video/webm; codecs="vp8"'},
+  },
+  {
+    comment: 'Zero coded size',
+    config: {
+      codec: h264.codec,
+      codedWidth: 0,
+      codedHeight: 0,
+    },
+  },
+  {
+    comment: 'Out of bounds crop size caused by left/top offset',
+    config: {
+      codec: h264.codec,
+      codedWidth: 1920,
+      codedHeight: 1088,
+      cropLeft: 10,
+      cropTop: 10,
+      // When unspecified, these default to coded dimensions
+      // cropWidth: 1920,
+      // cropHeight: 1088
+    },
+  },
+  {
+    comment: 'Out of bounds crop size',
+    config: {
+      codec: h264.codec,
+      codedWidth: 1920,
+      codedHeight: 1088,
+      cropLeft: 10,
+      cropTop: 10,
+      cropWidth: 1920,
+      cropHeight: 1088,
+    },
+  },
+  {
+    comment: 'Way out of bounds crop size',
+    config: {
+      codec: h264.codec,
+      codedWidth: 1920,
+      codedHeight: 1088,
+      cropWidth: 4000,
+      cropHeight: 5000,
+    },
+  },
+  {
+    comment: 'Invalid display size',
+    config: {
+      codec: h264.codec,
+      displayWidth: 0,
+      displayHeight: 0,
+    },
+  },
+] //  invalidConfigs
+
 function view(buffer, {offset, size}) {
   return new Uint8Array(buffer, offset, size);
 }
@@ -48,6 +130,40 @@
   });
 }
 
+invalidConfigs.forEach(entry => {
+  promise_test(t => {
+    return promise_rejects_js(t, TypeError, VideoDecoder.isConfigSupported(entry.config));
+  }, 'Test that VideoDecoder.isConfigSupported() rejects invalid config:' + entry.comment);
+});
+
+invalidConfigs.forEach(entry => {
+  async_test(t => {
+    let codec = new VideoDecoder(getDefaultCodecInit(t));
+    assert_throws_js(TypeError, () => { codec.configure(entry.config); });
+    t.done();
+  }, 'Test that VideoDecoder.configure() rejects invalid config:' + entry.comment);
+});
+
+promise_test(t => {
+  return VideoDecoder.isConfigSupported({codec: vp9.codec});
+}, 'Test VideoDecoder.isConfigSupported() with minimal valid config');
+
+promise_test(t => {
+  // This config specifies a slight crop. H264 1080p content always crops
+  // because H264 coded dimensions are a multiple of 16 (e.g. 1088).
+  return VideoDecoder.isConfigSupported({
+    codec: h264.codec,
+    codedWidth: 1920,
+    codedHeight: 1088,
+    cropLeft: 0,
+    cropTop: 0,
+    cropWidth: 1920,
+    cropHeight: 1080,
+    displayWidth: 1920,
+    displayHeight: 1080
+  });
+}, 'Test VideoDecoder.isConfigSupported() with valid expanded config');
+
 promise_test(t => {
   // VideoDecoderInit lacks required fields.
   assert_throws_js(TypeError, () => { new VideoDecoder({}); });
@@ -65,18 +181,12 @@
 promise_test(t => {
   let decoder = new VideoDecoder(getDefaultCodecInit(t));
 
-  let badCodecsList = [
-    '',                         // Empty codec
-    'bogus',                    // Non exsitent codec
-    'vorbis',                   // Audio codec
-    'vp9',                      // Ambiguous codec
-    'video/webm; codecs="vp9"'  // Codec with mime type
-  ]
-
+  // TODO(chcunningham): Remove badCodecsList testing. It's now covered more
+  // extensively by other tests.
   testConfigurations(decoder, { codec: vp9.codec }, badCodecsList);
 
   return endAfterEventLoopTurn();
-}, 'Test VideoDecoder.configure()');
+}, 'Test VideoDecoder.configure() with various codec strings');
 
 promise_test(async t => {
   let buffer = await vp9.buffer();
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
index 596e3c4..85ff9f1c 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
@@ -1,5 +1,6 @@
 This is a testharness.js-based test.
 PASS A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation
+PASS A transceiver added and stopped should not crash when getting receiver's transport
 PASS During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation
 PASS A stopped sendonly transceiver should generate an inactive m-section in the offer
 PASS A stopped inactive transceiver should generate an inactive m-section in the offer
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-stop.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-stop.html
index 4f3a9ce..60f6d0c1 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-stop.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCRtpTransceiver-stop.html
@@ -23,6 +23,18 @@
 
 promise_test(async (t)=> {
     const pc1 = new RTCPeerConnection();
+    t.add_cleanup(() => pc1.close());
+
+    pc1.addTransceiver("audio", { direction: "sendonly" });
+    pc1.addTransceiver("video");
+    assert_equals(null, pc1.getTransceivers()[1].receiver.transport);
+
+    pc1.getTransceivers()[1].stop();
+    assert_equals(pc1.getTransceivers()[1].receiver.transport, null);
+}, "A transceiver added and stopped should not crash when getting receiver's transport");
+
+promise_test(async (t)=> {
+    const pc1 = new RTCPeerConnection();
     const pc2 = new RTCPeerConnection();
     t.add_cleanup(() => pc1.close());
     t.add_cleanup(() => pc2.close());
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles-2/pseudo-elements-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/styles-2/pseudo-elements-expected.txt
index 7d8fad9..c8b42e8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/styles-2/pseudo-elements-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles-2/pseudo-elements-expected.txt
@@ -39,6 +39,11 @@
 [$#inspected:after$] { (<style>)
     content: "AFTER";
 
+======== Pseudo ::grammar-error element ========
+[expanded] 
+#inspected::grammar-error { (<style>)
+    color: teal;
+
 ======== Pseudo ::marker element ========
 [expanded] 
 [$#inspected::marker$] { (<style>)
@@ -53,6 +58,11 @@
     text-align: start !important;
     text-align-last: start !important;
 
+======== Pseudo ::spelling-error element ========
+[expanded] 
+#inspected::spelling-error { (<style>)
+    color: orange;
+
 ======== Pseudo ::target-text element ========
 [expanded] 
 #inspected::target-text { (<style>)
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles-2/pseudo-elements.js b/third_party/blink/web_tests/http/tests/devtools/elements/styles-2/pseudo-elements.js
index d1f44e8c..ddf302f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/styles-2/pseudo-elements.js
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles-2/pseudo-elements.js
@@ -12,6 +12,14 @@
         color: green;
       }
 
+      #inspected::spelling-error {
+        color: orange;
+      }
+
+      #inspected::grammar-error {
+        color: teal;
+      }
+
       #inspected {
         display: list-item;
       }
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/cookiestore-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/cookiestore-origin-trial-interfaces.html
deleted file mode 100644
index 79c4c203..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/cookiestore-origin-trial-interfaces.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8">
-<!-- Generate token with the command:
-generate_token.py http://127.0.0.1:8000 CookieStore --expire-timestamp=2000000000
--- -->
-<meta http-equiv="origin-trial" content="A/tS990j7n9VjcZbX16/nUmZ8VdMMUzvcPOC6lSkRjLCqhbITiPki31a3TpeDuSZzcSKeZ7bik7JB0c+M/i6UQQAAABTeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiQ29va2llU3RvcmUiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=" />
-<title>Cookie Store API - interfaces exposed by origin trial</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/origin-trials-helper.js"></script>
-<script src="/serviceworker/resources/test-helpers.js"></script>
-<script>
-test(t => {
-  OriginTrialsHelper.check_properties_exist(this, {
-     'CookieStore': ['get', 'getAll', 'set', 'delete', 'onchange'],
-     'CookieStoreManager': ['getSubscriptions', 'subscribe', 'unsubscribe'],
-     'CookieChangeEvent': ['changed', 'deleted'],
-     'ServiceWorkerRegistration': ['cookies'],
-  });
-}, 'Cookie Store API interfaces and properties in Origin-Trial enabled document.');
-
-test(t => {
-  assert_true('cookieStore' in self, 'cookieStore property exists on global scope');
-  assert_not_equals(window.cookieStore, undefined, 'cookieStore property can be accessed on window');
-}, 'Cookie Store API entry point in Origin-Trial enabled document.');
-
-service_worker_test('resources/cookiestore-origin-trial-interfaces-serviceworker-enabled.php');
-
-</script>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/native-file-system-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/file-system-access-origin-trial-interfaces.html
similarity index 89%
rename from third_party/blink/web_tests/http/tests/origin_trials/webexposed/native-file-system-origin-trial-interfaces.html
rename to third_party/blink/web_tests/http/tests/origin_trials/webexposed/file-system-access-origin-trial-interfaces.html
index 05cc05c2..b806171b 100644
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/native-file-system-origin-trial-interfaces.html
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/file-system-access-origin-trial-interfaces.html
@@ -4,7 +4,7 @@
 generate_token.py http://127.0.0.1:8000 NativeFileSystem2 --expire-timestamp=2000000000
 -- -->
 <meta http-equiv="origin-trial" content="AmmBxJTKsdbb26LLpdAVmIKBvVxU6fKf6UZDoMXiD+b9bULkOHzUTtZ4dNwHMhI7tZzO+tNgmWCzNK2MLoZsvAEAAABZeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiTmF0aXZlRmlsZVN5c3RlbTIiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=">
-<title>Native File System API - interfaces exposed by origin trial</title>
+<title>File System Access API - interfaces exposed by origin trial</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/origin-trials-helper.js"></script>
@@ -17,5 +17,5 @@
     'FileSystemWritableFileStream': ['write', 'truncate', 'close', 'seek'],
     'global': ['showOpenFilePicker', 'showSaveFilePicker', 'showDirectoryPicker'],
   });
-}, 'Native File System API interfaces and properties in Origin-Trial enabled document.');
+}, 'File System Access API interfaces and properties in Origin-Trial enabled document.');
 </script>
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/cookiestore-origin-trial-interfaces-serviceworker-enabled.php b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/cookiestore-origin-trial-interfaces-serviceworker-enabled.php
deleted file mode 100644
index ff2782f..0000000
--- a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/resources/cookiestore-origin-trial-interfaces-serviceworker-enabled.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-// Generate token with the command:
-// generate_token.py http://127.0.0.1:8000 CookieStore --expire-timestamp=2000000000
-header("Origin-Trial: AuCNc4F6ez8bdiKV6reoNKgzu2afmtUl5FgKkP6jdrbbCqVh8BfddejNqciWMz+V+oZXxJdW1LU5nQuC0Ij2GQkAAABTeyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiQ29va2llU3RvcmUiLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=");
-header('Content-Type: application/javascript');
-?>
-importScripts('/resources/testharness.js',
-              '/resources/origin-trials-helper.js');
-
-test(t => {
-  OriginTrialsHelper.check_properties_exist(this, {
-     'CookieStore': ['get', 'getAll', 'set', 'delete'],
-     'CookieStoreManager': ['getSubscriptions', 'subscribe', 'unsubscribe'],
-     'ExtendableCookieChangeEvent': ['changed', 'deleted'],
-     'ServiceWorkerRegistration': ['cookies'],
-  });
-}, 'Cookie Store API interfaces and properties in Origin-Trial enabled serviceworker.');
-
-test(t => {
-  assert_true('cookieStore' in self, 'cookieStore property exists on global');
-  assert_not_equals(self.cookieStore, undefined, 'cookieStore property can be accessed on self');
-  assert_true('oncookiechange' in self,
-              'oncookiechange property exists on global');
-}, 'Cookie Store API entry points in Origin-Trial enabled serviceworker.');
-
-done();
diff --git a/third_party/blink/web_tests/http/tests/security/isolatedWorld/world-reuse.html b/third_party/blink/web_tests/http/tests/security/isolatedWorld/world-reuse.html
index 03db5478..edf747a 100644
--- a/third_party/blink/web_tests/http/tests/security/isolatedWorld/world-reuse.html
+++ b/third_party/blink/web_tests/http/tests/security/isolatedWorld/world-reuse.html
@@ -26,9 +26,13 @@
   var iframeComplete = function(result) {
 
     // Isolated world executing in frame should be able to to access parent content.
+    let childFrameScriptElement = frames[0].document.createElement('script');
+    childFrameScriptElement.innerText = `
     testRunner.evaluateScriptInIsolatedWorld(1,
       "parent.document.body.appendChild(parent.document.createTextNode('Expecting true: ' + !!parent.frames[0].document.getElementById('output')));" +
       "parent.document.body.appendChild(parent.document.createElement('br'));");
+    `;
+    frames[0].document.body.appendChild(childFrameScriptElement);
 
     document.body.appendChild(document.createTextNode('Expecting undefined,undefined: ' + result));
     document.body.appendChild(document.createElement('br'));
diff --git a/third_party/blink/web_tests/inspector-protocol/page/add-script-to-evaluate-in-world.js b/third_party/blink/web_tests/inspector-protocol/page/add-script-to-evaluate-in-world.js
index 436034f..9a58105 100644
--- a/third_party/blink/web_tests/inspector-protocol/page/add-script-to-evaluate-in-world.js
+++ b/third_party/blink/web_tests/inspector-protocol/page/add-script-to-evaluate-in-world.js
@@ -6,10 +6,11 @@
 
   const scriptIds = [];
   dp.Runtime.onConsoleAPICalled(msg => testRunner.log(msg.params.args[0].value));
-  dp.Runtime.onExecutionContextCreated(msg => {
+  const logContextCreationCallback = msg => {
     if (msg.params.context.name.includes('world'))
       testRunner.log(msg.params.context.name);
-  });
+  };
+  dp.Runtime.onExecutionContextCreated(logContextCreationCallback);
 
   testRunner.log('Adding scripts');
   for (let i = 0; i < 5; ++i) {
@@ -27,6 +28,7 @@
       testRunner.log('Failed script removal');
   }
 
+  dp.Runtime.offExecutionContextCreated(logContextCreationCallback);
   await session.navigate('../resources/blank.html');
 
   testRunner.completeTest();
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
new file mode 100644
index 0000000..596e3c4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+PASS A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation
+PASS During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation
+PASS A stopped sendonly transceiver should generate an inactive m-section in the offer
+PASS A stopped inactive transceiver should generate an inactive m-section in the offer
+PASS If a transceiver is stopped locally, setting a locally generated answer should still work
+PASS If a transceiver is stopped remotely, setting a locally generated answer should still work
+PASS If a transceiver is stopped, transceivers, senders and receivers should disappear after offer/answer
+FAIL If a transceiver is stopped, transceivers should end up in state stopped assert_equals: expected "recvonly" but got "stopped"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt b/third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
new file mode 100644
index 0000000..7339289
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac-arm11.0/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
@@ -0,0 +1,11 @@
+This is a testharness.js-based test.
+FAIL A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL A stopped sendonly transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL A stopped inactive transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL If a transceiver is stopped locally, setting a locally generated answer should still work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL If a transceiver is stopped remotely, setting a locally generated answer should still work promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL If a transceiver is stopped, transceivers, senders and receivers should disappear after offer/answer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL If a transceiver is stopped, transceivers should end up in state stopped promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
index d7c52ed..0b96c8de 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
@@ -35,8 +35,6 @@
 PASS window.cached_navigator.webdriver is false
 PASS window.cached_navigator_connection.onchange is null
 PASS window.cached_navigator_connection.saveData is false
-PASS window.cached_navigator_hid.onconnect is null
-PASS window.cached_navigator_hid.ondisconnect is null
 PASS window.cached_navigator_mediaDevices.ondevicechange is null
 PASS window.cached_navigator_mediaSession.metadata is null
 PASS window.cached_navigator_mediaSession.playbackState is 'none'
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
index 463e454..b17bd1f5 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
@@ -35,8 +35,6 @@
 PASS window.cached_navigator.webdriver is false
 PASS window.cached_navigator_connection.onchange is null
 PASS window.cached_navigator_connection.saveData is false
-PASS window.cached_navigator_hid.onconnect is null
-PASS window.cached_navigator_hid.ondisconnect is null
 PASS window.cached_navigator_mediaDevices.ondevicechange is null
 PASS window.cached_navigator_mediaSession.metadata is null
 PASS window.cached_navigator_mediaSession.playbackState is 'none'
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
index 350b91e..3530916 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
@@ -35,8 +35,6 @@
 PASS window.cached_navigator.webdriver is false
 PASS window.cached_navigator_connection.onchange is null
 PASS window.cached_navigator_connection.saveData is false
-PASS window.cached_navigator_hid.onconnect is null
-PASS window.cached_navigator_hid.ondisconnect is null
 PASS window.cached_navigator_mediaDevices.ondevicechange is null
 PASS window.cached_navigator_mediaSession.metadata is null
 PASS window.cached_navigator_mediaSession.playbackState is 'none'
diff --git a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index e8c74642..d62632e5 100644
--- a/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -35,8 +35,6 @@
 PASS oldChildWindow.navigator.deviceMemory is newChildWindow.navigator.deviceMemory
 PASS oldChildWindow.navigator.doNotTrack is newChildWindow.navigator.doNotTrack
 PASS oldChildWindow.navigator.hardwareConcurrency is newChildWindow.navigator.hardwareConcurrency
-PASS oldChildWindow.navigator.hid.onconnect is newChildWindow.navigator.hid.onconnect
-PASS oldChildWindow.navigator.hid.ondisconnect is newChildWindow.navigator.hid.ondisconnect
 PASS oldChildWindow.navigator.language is newChildWindow.navigator.language
 PASS oldChildWindow.navigator.maxTouchPoints is newChildWindow.navigator.maxTouchPoints
 PASS oldChildWindow.navigator.mediaDevices.ondevicechange is newChildWindow.navigator.mediaDevices.ondevicechange
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
index c0da977..4c4046d 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/feature-policy-features-expected.txt
@@ -27,7 +27,6 @@
 fullscreen
 geolocation
 gyroscope
-hid
 magnetometer
 microphone
 midi
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 31af8656..75d90f4 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -2183,40 +2183,6 @@
     getter y
     getter z
     method constructor
-interface HID : EventTarget
-    attribute @@toStringTag
-    getter onconnect
-    getter ondisconnect
-    method constructor
-    method getDevices
-    method requestDevice
-    setter onconnect
-    setter ondisconnect
-interface HIDConnectionEvent : Event
-    attribute @@toStringTag
-    getter device
-    method constructor
-interface HIDDevice : EventTarget
-    attribute @@toStringTag
-    getter collections
-    getter oninputreport
-    getter opened
-    getter productId
-    getter productName
-    getter vendorId
-    method close
-    method constructor
-    method open
-    method receiveFeatureReport
-    method sendFeatureReport
-    method sendReport
-    setter oninputreport
-interface HIDInputReportEvent : Event
-    attribute @@toStringTag
-    getter data
-    getter device
-    getter reportId
-    method constructor
 interface HTMLAllCollection
     attribute @@toStringTag
     getter length
@@ -4581,7 +4547,6 @@
     getter doNotTrack
     getter geolocation
     getter hardwareConcurrency
-    getter hid
     getter keyboard
     getter language
     getter languages
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
index 7339289..483bec2 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCRtpTransceiver-stop-expected.txt
@@ -1,5 +1,6 @@
 This is a testharness.js-based test.
 FAIL A transceiver added and stopped before the initial offer generation should not trigger an offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
+FAIL A transceiver added and stopped should not crash when getting receiver's transport promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL During renegotiation, adding and stopping a transceiver should not trigger a renegotiated offer m-section generation promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL A stopped sendonly transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
 FAIL A stopped inactive transceiver should generate an inactive m-section in the offer promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'."
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 95b5641..88d9550 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -1725,6 +1725,7 @@
 [Worker]     getter isActive
 [Worker]     method constructor
 [Worker] interface VideoDecoder
+[Worker]     static method isConfigSupported
 [Worker]     attribute @@toStringTag
 [Worker]     getter decodeQueueSize
 [Worker]     getter state
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 9f13f56b..f0c175c 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -8778,6 +8778,7 @@
     getter valueMissing
     method constructor
 interface VideoDecoder
+    static method isConfigSupported
     attribute @@toStringTag
     getter decodeQueueSize
     getter state
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index c90af48..d1a7856 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-10-4-101-gcbc14b10e
-Revision: cbc14b10e623a5d08c89a1323ae9c420c8492118
+Version: VER-2-10-4-104-g0901a6f74
+Revision: 0901a6f74c707f4e631d810b9f3ac7f0c0d2042e
 CPEPrefix: cpe:/a:freetype:freetype:2.10.4
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/third_party/usrsctp/BUILD.gn b/third_party/usrsctp/BUILD.gn
index 0e5e0f6..b869852 100644
--- a/third_party/usrsctp/BUILD.gn
+++ b/third_party/usrsctp/BUILD.gn
@@ -105,7 +105,6 @@
     "SCTP_PROCESS_LEVEL_LOCKS",
     "SCTP_SIMPLE_ALLOCATOR",
     "SCTP_USE_OPENSSL_SHA1",
-    "SCTP_USE_OPENSSL_RAND",
     "__Userspace__",
 
     # "SCTP_DEBUG", # Uncomment for SCTP debugging.
@@ -129,13 +128,26 @@
   ]
 
   if (is_linux || is_chromeos || is_android) {
-    defines += [ "_GNU_SOURCE" ]
+    defines += [
+      "__Userspace_os_Linux",
+      "_GNU_SOURCE",
+    ]
   } else if (is_apple) {
     defines += [
       "HAVE_SA_LEN",
       "__APPLE_USE_RFC_2292",
+      "__Userspace_os_Darwin",
     ]
   }
 
+  if (is_win) {
+    defines += [ "__Userspace_os_Windows" ]
+  } else {
+    defines += [ "NON_WINDOWS_DEFINE" ]
+  }
+
+  if (is_fuchsia) {
+    defines += [ "__Userspace_os_Fuchsia" ]
+  }
   deps = [ "//third_party/boringssl" ]
 }
diff --git a/third_party/usrsctp/README.chromium b/third_party/usrsctp/README.chromium
index e30db20..bfc3852 100644
--- a/third_party/usrsctp/README.chromium
+++ b/third_party/usrsctp/README.chromium
@@ -2,8 +2,8 @@
 URL: http://github.com/sctplab/usrsctp
 Version: 0
 CPEPrefix: cpe:/a:usrsctp_project:usrsctp:2019-12-20
-Date: Jan 5, 2021
-Revision: 4191ca1784d8774dbf62d48ab9426c7311a91bc5
+Date: Aug 28, 2020
+Revision: 995c0b84414466d77d52011e5b572cbe213b770a
 License: New BSD
 License File: LICENSE
 Security Critical: yes
@@ -19,3 +19,9 @@
    draft-stewart-prsctp-00.txt
    draft-stewart-tsvwg-sctpipv6-00.txt
    draft-iyengar-sctp-cacc-00.txt
+
+Local Modifications:
+Cherry picked:
+d844a690d30ace22de75e8b80d9bb08ae7283795
+355fb576b1a9dfef70fc0e158d66692662baaddc
+7dab23aa0d8db86fedd222a308067439b03fad0f
diff --git a/tools/gritsettings/resource_ids.spec b/tools/gritsettings/resource_ids.spec
index ea86ce6..b1fe70dd 100644
--- a/tools/gritsettings/resource_ids.spec
+++ b/tools/gritsettings/resource_ids.spec
@@ -162,6 +162,10 @@
   "chrome/browser/resources/preinstalled_web_apps/resources.grd": {
     "includes": [1710],
   },
+  "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/pdf/resources.grd": {
+    "META": {"sizes": {"includes": [200]}},
+    "includes": [1715],
+  },
   "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/print_preview/print_preview_resources.grd": {
     "META": {"sizes": {"includes": [500],}},
     "includes": [1720],
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 8fc5f5a..4d9234e 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -374,10 +374,6 @@
       'win32-archive-rel-goma-canary-localoutputcache': 'release_bot_x86_minimal_symbols_enable_archive_compression',
       'win32-archive-rel-goma-latest-localoutputcache': 'release_bot_x86_minimal_symbols_enable_archive_compression',
 
-      'Win7 Builder (dbg) Goma Canary': 'gpu_tests_debug_bot_x86_no_symbols',
-      'Win7 Builder (dbg) Goma Latest Client': 'gpu_tests_debug_bot_x86_no_symbols',
-      'Win7 Builder Goma Canary': 'gpu_tests_release_bot_x86_minimal_symbols',
-      'Win7 Builder Goma Latest Client': 'gpu_tests_release_bot_x86_minimal_symbols',
 
       'Mac Builder (dbg) Goma Canary': 'gpu_tests_debug_bot',
       'Mac Builder (dbg) Goma Canary (clobber)': 'gpu_tests_debug_bot',
diff --git a/tools/mb/mb_config_expectations/chromium.goma.fyi.json b/tools/mb/mb_config_expectations/chromium.goma.fyi.json
index cabdae5..cdfa93d 100644
--- a/tools/mb/mb_config_expectations/chromium.goma.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.goma.fyi.json
@@ -181,50 +181,6 @@
       "use_goma": true
     }
   },
-  "Win7 Builder (dbg) Goma Canary": {
-    "gn_args": {
-      "ffmpeg_branding": "Chrome",
-      "is_component_build": true,
-      "is_debug": true,
-      "proprietary_codecs": true,
-      "symbol_level": 0,
-      "target_cpu": "x86",
-      "use_goma": true
-    }
-  },
-  "Win7 Builder (dbg) Goma Latest Client": {
-    "gn_args": {
-      "ffmpeg_branding": "Chrome",
-      "is_component_build": true,
-      "is_debug": true,
-      "proprietary_codecs": true,
-      "symbol_level": 0,
-      "target_cpu": "x86",
-      "use_goma": true
-    }
-  },
-  "Win7 Builder Goma Canary": {
-    "gn_args": {
-      "ffmpeg_branding": "Chrome",
-      "is_component_build": false,
-      "is_debug": false,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "target_cpu": "x86",
-      "use_goma": true
-    }
-  },
-  "Win7 Builder Goma Latest Client": {
-    "gn_args": {
-      "ffmpeg_branding": "Chrome",
-      "is_component_build": false,
-      "is_debug": false,
-      "proprietary_codecs": true,
-      "symbol_level": 1,
-      "target_cpu": "x86",
-      "use_goma": true
-    }
-  },
   "android-archive-dbg-goma-canary": {
     "gn_args": {
       "is_component_build": true,
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 27e293f..63d0e09 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7169,6 +7169,14 @@
   <int value="4" label="Unknown"/>
 </enum>
 
+<enum name="BlueZResultOfAdvertisementMonitor">
+  <int value="0" label="Success"/>
+  <int value="1" label="Unknown Error"/>
+  <int value="2" label="Invalid Parameters"/>
+  <int value="3" label="No Resource"/>
+  <int value="4" label="Busy"/>
+</enum>
+
 <enum name="BlueZResultOfAdvertisementRegistration">
   <int value="1" label="Success"/>
   <int value="2" label="LE unsupported"/>
@@ -30350,7 +30358,7 @@
   <int value="3343" label="V8Window_GetOriginPrivateDirectory_Method"/>
   <int value="3344" label="RTCConstraintEnableRtpDataChannelsTrue"/>
   <int value="3345" label="RTCConstraintEnableRtpDataChannelsFalse"/>
-  <int value="3346" label="NativeFileSystemDragAndDrop"/>
+  <int value="3346" label="FileSystemAccessDragAndDrop"/>
   <int value="3347" label="RTCAdaptivePtime"/>
   <int value="3348"
       label="HTMLMetaElementReferrerPolicyMultipleTokensAffectingRequest"/>
@@ -61423,6 +61431,13 @@
   </int>
 </enum>
 
+<enum name="ProfileKeepAliveOrigin">
+  <int value="0" label="kWaitingForFirstBrowserWindow"/>
+  <int value="1" label="kBrowserWindow"/>
+  <int value="2" label="kBackgroundMode"/>
+  <int value="3" label="kOffTheRecordProfile"/>
+</enum>
+
 <enum name="ProfileMenuActionableItem">
   <int value="0" label="Manage your Google Account button"/>
   <int value="1" label="Passwords button"/>
diff --git a/tools/metrics/histograms/histograms_xml/input/histograms.xml b/tools/metrics/histograms/histograms_xml/input/histograms.xml
index 2a9a8ce..466ab52 100644
--- a/tools/metrics/histograms/histograms_xml/input/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/input/histograms.xml
@@ -480,6 +480,36 @@
   </summary>
 </histogram>
 
+<histogram name="InputMethod.MultilingualExperiment.Autocorrect.Actions"
+    enum="IMEAutocorrectActions" expires_after="2021-06-30">
+  <owner>tranbaoduy@google.com</owner>
+  <owner>essential-inputs-team@google.com</owner>
+  <summary>
+    Same as InputMethod.Assistive.Autocorrect.Actions but only recorded for CrOS
+    1P experimental multilingual input methods.
+  </summary>
+</histogram>
+
+<histogram name="InputMethod.MultilingualExperiment.Autocorrect.Count"
+    enum="IMETextInputClient" expires_after="2021-06-30">
+  <owner>tranbaoduy@google.com</owner>
+  <owner>essential-inputs-team@google.com</owner>
+  <summary>
+    Same as InputMethod.Assistive.Autocorrect.Count but only recorded for CrOS
+    1P experimental multilingual input methods.
+  </summary>
+</histogram>
+
+<histogram name="InputMethod.MultilingualExperiment.Autocorrect.Delay"
+    units="ms" expires_after="2021-06-30">
+  <owner>tranbaoduy@google.com</owner>
+  <owner>essential-inputs-team@google.com</owner>
+  <summary>
+    Same as InputMethod.Assistive.Autocorrect.Delay but only recorded for CrOS
+    1P experimental multilingual input methods.
+  </summary>
+</histogram>
+
 <histogram name="InputMethod.NextWordPrediction" enum="BooleanEnabled"
     expires_after="2021-06-13">
   <owner>myy@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 29590f4..46df97a6 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -1251,6 +1251,53 @@
   </summary>
 </histogram>
 
+<histogram name="BlueZ.AdvertisementMonitor.NumOfMonitors" units="count"
+    expires_after="2021-12-28">
+  <owner>apusaka@chromium.org</owner>
+  <owner>chromeos-bt-platform-sw-core@google.com</owner>
+  <summary>
+    This is specific to Chrome OS. Records the number of active advertisement
+    monitors every couple of minutes. This helps us to gauge the additional
+    power requirement for the advertisement monitor feature.
+  </summary>
+</histogram>
+
+<histogram name="BlueZ.AdvertisementMonitor.{Filter}.FilterPatternsPerMinute"
+    units="count" expires_after="2021-12-28">
+  <owner>apusaka@chromium.org</owner>
+  <owner>chromeos-bt-platform-sw-core@google.com</owner>
+  <summary>
+    This is specific to Chrome OS. Records the number of advertisement packet
+    received in the user space while the advertisement monitor is done using
+    {Filter} filtering. This helps us to gauge the additional power requirement
+    for the advertisement monitor feature.
+  </summary>
+  <token key="Filter">
+    <variant name="MSFT"/>
+    <variant name="SW"/>
+  </token>
+</histogram>
+
+<histogram name="BlueZ.AdvertisementMonitor.{Filter}.{Operation}.Result"
+    enum="BlueZResultOfAdvertisementMonitor" expires_after="2021-12-28">
+  <owner>apusaka@chromium.org</owner>
+  <owner>chromeos-bt-platform-sw-core@google.com</owner>
+  <summary>
+    This is specific to Chrome OS. Records the outcome of {Operation} operation
+    of advertisement monitor when {Filter} filtering is used. This helps us to
+    verify the correctness of this feature as well as to point out possible
+    regression.
+  </summary>
+  <token key="Filter">
+    <variant name="MSFT"/>
+    <variant name="SW"/>
+  </token>
+  <token key="Operation">
+    <variant name="Add"/>
+    <variant name="Remove"/>
+  </token>
+</histogram>
+
 <histogram name="BlueZ.ChipLost2" units="seconds" expires_after="2021-05-16">
   <owner>sonnysasaka@chromium.org</owner>
   <summary>
@@ -4628,7 +4675,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.AuthEvent.DidUserManuallyUnlockPhone"
-    enum="EasyUnlockDidUserManuallyUnlockPhone" expires_after="2021-02-02">
+    enum="EasyUnlockDidUserManuallyUnlockPhone" expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -4640,7 +4687,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.AuthEvent.SignIn" enum="EasyUnlockAuthEvent"
-    expires_after="2021-06-06">
+    expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -4652,7 +4699,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.AuthEvent.SignIn.Duration" units="ms"
-    expires_after="2021-02-02">
+    expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -4662,7 +4709,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.AuthEvent.Unlock" enum="EasyUnlockAuthEvent"
-    expires_after="2021-06-06">
+    expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -4674,7 +4721,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.AuthEvent.Unlock.Duration" units="ms"
-    expires_after="2021-04-04">
+    expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -4686,7 +4733,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.AuthProximity.RemoteDeviceModelHash"
-    enum="EasyUnlockDeviceModelHash" expires_after="2021-02-02">
+    enum="EasyUnlockDeviceModelHash" expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -4700,7 +4747,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.AuthProximity.RollingRssi" units="dBm"
-    expires_after="2021-02-02">
+    expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -4721,7 +4768,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.RemoteLockScreenState"
-    enum="EasyUnlockRemoteLockScreenState" expires_after="2021-02-02">
+    enum="EasyUnlockRemoteLockScreenState" expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -4735,7 +4782,7 @@
 </histogram>
 
 <histogram name="EasyUnlock.StartupTimeFromSuspend" units="ms"
-    expires_after="2021-02-02">
+    expires_after="2022-02-02">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -6746,7 +6793,10 @@
 </histogram>
 
 <histogram name="ImportantFile.DeleteOnCloseError" enum="PlatformFileError"
-    expires_after="2021-01-19">
+    expires_after="M89">
+  <obsolete>
+    Removed in M89.
+  </obsolete>
   <owner>grt@chromium.org</owner>
   <owner>xaerox@yandex-team.ru</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/profile/histograms.xml b/tools/metrics/histograms/histograms_xml/profile/histograms.xml
index fa0b66d4..a26f13ec 100644
--- a/tools/metrics/histograms/histograms_xml/profile/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/profile/histograms.xml
@@ -248,6 +248,17 @@
   </summary>
 </histogram>
 
+<histogram name="Profile.KeepAliveLeakAtShutdown" enum="ProfileKeepAliveOrigin"
+    expires_after="2021-06-01">
+  <owner>nicolaso@chromium.org</owner>
+  <owner>cbe-eng@google.com</owner>
+  <summary>
+    Recorded during BrowserProcess teardown. Indicates that a Profile still has
+    ScopedProfileKeepAlive objects referencing it, of the given origin. This is
+    a sign of a bug, or incorrect usage of the ScopedProfileKeepAlive API.
+  </summary>
+</histogram>
+
 <histogram name="Profile.Menu.ClickedActionableItem"
     enum="ProfileMenuActionableItem" expires_after="2021-06-30">
   <owner>droger@chromium.org</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index b371e75..609aea1 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/070ef598892aee3d5cfb628f577867217c863142/trace_processor_shell"
         },
         "linux": {
-            "hash": "153bdd7d922d3d225b8c2855de063cf00ff59efa",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/070ef598892aee3d5cfb628f577867217c863142/trace_processor_shell"
+            "hash": "5417da4ae1c19ada86faeaba0e3d694c4ff3661b",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/3ce4e8ee1301d0102b3595187a93a3ba067b2a69/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 24ab739..dc5eedf 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -1036,7 +1036,10 @@
         "ime/chromeos/input_method_descriptor_unittest.cc",
         "ime/chromeos/input_method_util_unittest.cc",
       ]
-      deps += [ "//ui/base/ime/chromeos" ]
+      deps += [
+        "//build:branding_buildflags",
+        "//ui/base/ime/chromeos",
+      ]
     }
     if ((is_linux || is_chromeos_lacros) && use_aura) {
       sources += [ "dragdrop/os_exchange_data_provider_non_backed_unittest.cc" ]
diff --git a/ui/base/ime/chromeos/extension_ime_util.cc b/ui/base/ime/chromeos/extension_ime_util.cc
index 9588354..54ff2f2 100644
--- a/ui/base/ime/chromeos/extension_ime_util.cc
+++ b/ui/base/ime/chromeos/extension_ime_util.cc
@@ -4,6 +4,7 @@
 
 #include "ui/base/ime/chromeos/extension_ime_util.h"
 
+#include "base/strings/strcat.h"
 #include "base/strings/string_util.h"
 #include "build/branding_buildflags.h"
 
@@ -166,5 +167,16 @@
   return false;
 }
 
+bool IsExperimentalMultilingual(const std::string& input_method_id) {
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  const std::string prefix = base::StrCat(
+      {kComponentExtensionIMEPrefix, kXkbExtensionId, "experimental_"});
+  return base::StartsWith(input_method_id, prefix,
+                          base::CompareCase::SENSITIVE);
+#else
+  return false;
+#endif
+}
+
 }  // namespace extension_ime_util
 }  // namespace chromeos
diff --git a/ui/base/ime/chromeos/extension_ime_util.h b/ui/base/ime/chromeos/extension_ime_util.h
index a81c488..c975c14e 100644
--- a/ui/base/ime/chromeos/extension_ime_util.h
+++ b/ui/base/ime/chromeos/extension_ime_util.h
@@ -99,6 +99,11 @@
 std::string COMPONENT_EXPORT(UI_BASE_IME_CHROMEOS)
     GetComponentIDByInputMethodID(const std::string& input_method_id);
 
+// Returns true if |input_method_id| refers to a CrOS 1P experimental
+// multilingual input method.
+bool COMPONENT_EXPORT(UI_BASE_IME_CHROMEOS)
+    IsExperimentalMultilingual(const std::string& input_method_id);
+
 }  // namespace extension_ime_util
 }  // namespace chromeos
 
diff --git a/ui/base/ime/chromeos/extension_ime_util_unittest.cc b/ui/base/ime/chromeos/extension_ime_util_unittest.cc
index 38ee414..d71a814b 100644
--- a/ui/base/ime/chromeos/extension_ime_util_unittest.cc
+++ b/ui/base/ime/chromeos/extension_ime_util_unittest.cc
@@ -6,6 +6,8 @@
 
 #include <string>
 
+#include "base/strings/strcat.h"
+#include "build/branding_buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
@@ -82,4 +84,30 @@
   EXPECT_FALSE(extension_ime_util::IsArcIME("mozc"));
 }
 
+TEST(ExtensionIMEUtilTest, IsExperimentalMultilingualTest) {
+  // TODO(crbug.com/1162211): Input method IDs are tuples of extension type,
+  // extension ID, and extension-local input method ID. However, currently
+  // they're just concats of the three constituent pieces of info, hence StrCat
+  // here. Replace StrCat once they're no longer unstructured string concats.
+
+  EXPECT_FALSE(extension_ime_util::IsExperimentalMultilingual(
+      base::StrCat({"some_extension_type", "some_extension_id",
+                    "experimental_hello_world"})));
+
+  EXPECT_FALSE(extension_ime_util::IsExperimentalMultilingual(base::StrCat(
+      {"_comp_ime_", "some_extension_id", "experimental_hello_world"})));
+
+  EXPECT_FALSE(extension_ime_util::IsExperimentalMultilingual(base::StrCat(
+      {"_comp_ime_", "jkghodnilhceideoidjikpgommlajknk", "hello_world"})));
+
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
+  EXPECT_TRUE(
+#else
+  EXPECT_FALSE(
+#endif
+      extension_ime_util::IsExperimentalMultilingual(
+          base::StrCat({"_comp_ime_", "jkghodnilhceideoidjikpgommlajknk",
+                        "experimental_hello_world"})));
+}
+
 }  // namespace chromeos
diff --git a/ui/file_manager/BUILD.gn b/ui/file_manager/BUILD.gn
index 717f98cc..7f6dbd4c 100644
--- a/ui/file_manager/BUILD.gn
+++ b/ui/file_manager/BUILD.gn
@@ -87,13 +87,17 @@
   in_files = [
     "audio_player/js/main.m.js",
     "audio_player/js/main_background.m.js",
+    "file_manager/background/js/main_background.m.js",
     "video_player/js/main.m.js",
     "video_player/js/main_background.m.js",
   ]
 
   deps = [
     "//ui/file_manager/audio_player/js:main.m",
+    "//ui/file_manager/audio_player/js:main_background.m",
+    "//ui/file_manager/file_manager/background/js:main_background.m",
     "//ui/file_manager/video_player/js:main.m",
+    "//ui/file_manager/video_player/js:main_background.m",
   ]
 }
 
@@ -117,39 +121,75 @@
 
     # Base:
     "base/js/app_util.m.js",
-    "base/js/volume_manager_types.m.js",
-    "base/js/mediasession_types.m.js",
+    "base/js/error_counter.m.js",
     "base/js/filtered_volume_manager.m.js",
+    "base/js/mediasession_types.m.js",
+    "base/js/volume_manager_types.m.js",
 
     # Common:
     "file_manager/common/js/async_util.m.js",
-    "file_manager/common/js/util.m.js",
+    "file_manager/common/js/file_operation_common.m.js",
     "file_manager/common/js/file_type.m.js",
     "file_manager/common/js/files_app_entry_types.m.js",
+    "file_manager/common/js/importer_common.m.js",
     "file_manager/common/js/lru_cache.m.js",
     "file_manager/common/js/metrics.m.js",
     "file_manager/common/js/metrics_base.m.js",
+    "file_manager/common/js/progress_center_common.m.js",
+    "file_manager/common/js/trash.m.js",
+    "file_manager/common/js/util.m.js",
 
     # Externs:
-    "externs/entry_location.m.js",
-    "externs/files_app_entry_interfaces.m.js",
     "externs/background/background_base.m.js",
+    "externs/background/crostini.m.js",
+    "externs/background/drive_sync_handler.m.js",
+    "externs/background/duplicate_finder.m.js",
+    "externs/background/file_browser_background_full.m.js",
+    "externs/background/file_operation_manager.m.js",
+    "externs/background/import_history.m.js",
+    "externs/background/media_import_handler.m.js",
+    "externs/background/media_scanner.m.js",
+    "externs/background/progress_center.m.js",
+    "externs/background/task_queue.m.js",
+    "externs/entry_location.m.js",
+    "externs/exif_entry.m.js",
+    "externs/files_app_entry_interfaces.m.js",
+    "externs/metadata_worker_window.m.js",
+    "externs/progress_center_panel.m.js",
     "externs/volume_info.m.js",
     "externs/volume_info_list.m.js",
     "externs/volume_manager.m.js",
-    "externs/metadata_worker_window.m.js",
-    "externs/exif_entry.m.js",
 
     # Files app Background:
-    "file_manager/background/js/app_windows.m.js",
     "file_manager/background/js/app_window_wrapper.m.js",
+    "file_manager/background/js/app_windows.m.js",
+    "file_manager/background/js/background.m.js",
     "file_manager/background/js/background_base.m.js",
+    "file_manager/background/js/crostini.m.js",
+    "file_manager/background/js/device_handler.m.js",
+    "file_manager/background/js/drive_sync_handler.m.js",
+    "file_manager/background/js/duplicate_finder.m.js",
     "file_manager/background/js/entry_location_impl.m.js",
-    "file_manager/background/js/volume_manager_factory.m.js",
-    "file_manager/background/js/volume_manager_impl.m.js",
+    "file_manager/background/js/file_operation_handler.m.js",
+    "file_manager/background/js/file_operation_manager.m.js",
+    "file_manager/background/js/file_operation_util.m.js",
+    "file_manager/background/js/import_history.m.js",
+    "file_manager/background/js/launcher.m.js",
+    "file_manager/background/js/launcher_search.m.js",
+    "file_manager/background/js/media_import_handler.m.js",
+    "file_manager/background/js/media_scanner.m.js",
+    "file_manager/background/js/metadata_proxy.m.js",
+    "file_manager/background/js/metrics_start.m.js",
+    "file_manager/background/js/mount_metrics.m.js",
+    "file_manager/background/js/progress_center.m.js",
+    "file_manager/background/js/task_queue.m.js",
+    "file_manager/background/js/test_util.m.js",
+    "file_manager/background/js/test_util_base.m.js",
+    "file_manager/background/js/trash.m.js",
     "file_manager/background/js/volume_info_impl.m.js",
     "file_manager/background/js/volume_info_list_impl.m.js",
-    "file_manager/background/js/test_util_base.m.js",
+    "file_manager/background/js/volume_manager_factory.m.js",
+    "file_manager/background/js/volume_manager_impl.m.js",
     "file_manager/background/js/volume_manager_util.m.js",
 
     # Files app Foreground:
@@ -222,6 +262,7 @@
     "audio_player/js/main_background.m.rollup.js",
     "audio_player/js/metadata_worker.m.rollup.js",
 
+    "file_manager/background/js/main_background.m.rollup.js",
     "file_manager/foreground/elements/files_icon_button.m.js",
     "file_manager/foreground/elements/files_toggle_ripple.m.js",
     "file_manager/foreground/elements/files_ripple.m.js",
@@ -234,6 +275,7 @@
     "audio_player/js/main.m.rollup.js|audio_player/js/main.m.js",
     "audio_player/js/main_background.m.rollup.js|audio_player/js/main_background.m.js",
     "audio_player/js/metadata_worker.m.rollup.js|audio_player/js/metadata_worker.m.js",
+    "file_manager/background/js/main_background.m.rollup.js|file_manager/background/js/main_background.m.js",
     "video_player/js/main.m.rollup.js|video_player/js/main.m.js",
     "video_player/js/main_background.m.rollup.js|video_player/js/main_background.m.js",
   ]
@@ -249,6 +291,7 @@
     "//ui/file_manager/audio_player/js:build",
     "//ui/file_manager/audio_player/js:build_background",
     "//ui/file_manager/audio_player/js:build_worker",
+    "//ui/file_manager/file_manager/background/js:build",
     "//ui/file_manager/file_manager/foreground/elements:elements",
     "//ui/file_manager/video_player/js:build",
     "//ui/file_manager/video_player/js:build_background",
diff --git a/ui/file_manager/file_manager/background.html b/ui/file_manager/file_manager/background.html
index 1086056..95b12478 100644
--- a/ui/file_manager/file_manager/background.html
+++ b/ui/file_manager/file_manager/background.html
@@ -1,10 +1,4 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/cr/event_target.js"></script>
-<script src="chrome://resources/js/assert.js"></script>
-<script src="chrome://resources/js/cr/ui/array_data_model.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="background/js/background_common_scripts.js"></script>
-<script src="background/js/background_scripts.js"></script>
+<script type="module" src="background/js/main_background.m.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index 6fa08250..fb3480a 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//chrome/browser/resources/tools/optimize_webui.gni")
 import("//third_party/closure_compiler/compile_js.gni")
 import("//third_party/closure_compiler/js_unit_tests.gni")
 import("//ui/file_manager/base/gn/js_test_gen_html.gni")
@@ -267,6 +268,21 @@
   extra_deps = [ ":modulize" ]
 }
 
+js_library("main_background.m") {
+  visibility = []
+  visibility = [
+    ":*",
+    "//ui/file_manager:preprocess_static",
+  ]
+
+  deps = [
+    ":background.m",
+    ":metrics_start.m",
+    ":test_util.m",
+    "//ui/file_manager/base/js:error_counter.m",
+  ]
+}
+
 js_library("background_base") {
   visibility += related_apps
   deps = [
@@ -1315,3 +1331,24 @@
 
   namespace_rewrites = cr_namespace_rewrites
 }
+
+preprocess_folder =
+    rebase_path(
+        "$target_gen_dir/../../../preprocessed/file_manager/background/js",
+        root_build_dir)
+
+optimize_webui("build") {
+  host = "file_manager"
+
+  input = preprocess_folder
+  js_module_in_files = [ "main_background.m.js" ]
+
+  js_out_files = [ "main_background.m.rollup.js" ]
+
+  deps = [
+    ":main_background.m",
+    "//ui/file_manager:preprocess_generated",
+    "//ui/file_manager:preprocess_static",
+    "//ui/webui/resources:preprocess",
+  ]
+}
diff --git a/ui/file_manager/file_manager/background/js/main_background.m.js b/ui/file_manager/file_manager/background/js/main_background.m.js
new file mode 100644
index 0000000..de846b6d
--- /dev/null
+++ b/ui/file_manager/file_manager/background/js/main_background.m.js
@@ -0,0 +1,13 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Main entry point for background page, it replaces the
+ * background_scripts.js.
+ */
+
+import './metrics_start.m.js';
+import '../../../base/js/error_counter.m.js';
+import './background.m.js';
+import './test_util.m.js';
diff --git a/ui/file_manager/file_manager/test/BUILD.gn b/ui/file_manager/file_manager/test/BUILD.gn
index 6f0b620..f48e22d 100644
--- a/ui/file_manager/file_manager/test/BUILD.gn
+++ b/ui/file_manager/file_manager/test/BUILD.gn
@@ -4,7 +4,7 @@
 
 import("//third_party/closure_compiler/compile_js.gni")
 
-python2_action("create_test_main") {
+action("create_test_main") {
   script = "//ui/file_manager/file_manager/test/scripts/create_test_main.py"
   output = "$target_gen_dir/../test.html"
   sources = [
diff --git a/ui/file_manager/file_manager/test/scripts/create_test_main.py b/ui/file_manager/file_manager/test/scripts/create_test_main.py
index cc953b1a..24b7057 100755
--- a/ui/file_manager/file_manager/test/scripts/create_test_main.py
+++ b/ui/file_manager/file_manager/test/scripts/create_test_main.py
@@ -45,16 +45,16 @@
 GENERATED_HTML = '<!-- %s -->\n\n' % GENERATED
 
 
-def read(path):
-  with open(os.path.join(SRC, path)) as f:
+def read(path, mode='r'):
+  with open(os.path.join(SRC, path), mode) as f:
     return f.read()
 
 
-def write(path, content):
+def write(path, content, mode='w'):
   fullpath = os.path.join(GEN, path)
   if not os.path.exists(os.path.dirname(fullpath)):
     os.makedirs(os.path.dirname(fullpath))
-  with open(fullpath, 'w') as f:
+  with open(fullpath, mode) as f:
     f.write(content)
 
 
@@ -130,7 +130,11 @@
       srcf = os.path.join(root[len(SRC):], f)
       dstf = R_GEN + dst_dir + srcf[len(src_dir):]
       relpath = os.path.relpath(R_GEN, os.path.dirname(dstf)) + '/'
-      write(dstf, i18n(read(srcf).replace('chrome://resources/', relpath)))
+      try:
+        write(dstf, i18n(read(srcf).replace('chrome://resources/', relpath)))
+      except UnicodeDecodeError:
+        # Binary files get utf-8 codec errors in py3, copy them as binary.
+        write(dstf, read(srcf, 'rb'), 'wb')
 
 # Copy any files required in chrome://resources/... into test/gen/resources.
 copyresources('ui/webui/resources/', '')
@@ -256,5 +260,5 @@
   main_html = replaceline(main_html, filename,
                           ['<script src="test/gen/%s"></script>' % filename])
 
-test_html = GENERATED_HTML + '\n'.join(main_html).encode('utf-8')
-write('test.html', test_html)
+test_html = (GENERATED_HTML + '\n'.join(main_html)).encode('utf-8')
+write('test.html', test_html, 'wb')
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 6570153..03db25c7 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -530,6 +530,10 @@
     deps += [ "//ui/display/util" ]
   }
 
+  if (is_chromeos) {
+    deps += [ "//ui/base/ime/chromeos" ]
+  }
+
   if (is_linux || is_chromeos) {
     public += [ "color_chooser/color_chooser_view.h" ]
     sources += [ "color_chooser/color_chooser_view.cc" ]
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 1c79aa2..590da79 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -96,6 +96,11 @@
 #include "ui/ozone/public/platform_gl_egl_utility.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "ui/base/ime/chromeos/extension_ime_util.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
+#endif
+
 namespace views {
 
 namespace {
@@ -1937,6 +1942,20 @@
   if (!range.is_empty()) {
     base::UmaHistogramEnumeration("InputMethod.Assistive.Autocorrect.Count",
                                   TextInputClient::SubClass::kTextField);
+
+#if defined(OS_CHROMEOS)
+    auto* input_method_manager =
+        chromeos::input_method::InputMethodManager::Get();
+    if (input_method_manager &&
+        chromeos::extension_ime_util::IsExperimentalMultilingual(
+            input_method_manager->GetActiveIMEState()
+                ->GetCurrentInputMethod()
+                .id())) {
+      base::UmaHistogramEnumeration(
+          "InputMethod.MultilingualExperiment.Autocorrect.Count",
+          TextInputClient::SubClass::kTextField);
+    }
+#endif
   }
   return model_->SetAutocorrectRange(range);
 }